minsung

Merge branch 'master' of https://bitbucket.org/vel1024/capstone

Showing 54 changed files with 450 additions and 513 deletions
1 *.xlsx 1 *.xlsx
2 -.env
...\ No newline at end of file ...\ No newline at end of file
2 +.env
3 +node_modules
4 +.vscode
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
4 4
5 Subject : Making website in order to practice latest web technologies 5 Subject : Making website in order to practice latest web technologies
6 6
7 +- (Tech : react.js, react hooks, styled-components, GraphQL, Prisma, Apollo, AWS, Docker, sendGrid, Twilio, Maxmind)
8 +
7 sub-subject : making chat site using JS 9 sub-subject : making chat site using JS
8 10
9 version 11 version
...@@ -16,13 +18,12 @@ Atom : 1.45.0 ...@@ -16,13 +18,12 @@ Atom : 1.45.0
16 - 4/6 ~ 4/12 18 - 4/6 ~ 4/12
17 - [x] Set development environment 19 - [x] Set development environment
18 20
19 - (tech : react.js, react hooks, styled-components, GraphQL, Prisma, Apollo, AWS, Docker)
20 -
21 - 4/13 ~ 4/19 21 - 4/13 ~ 4/19
22 -- [x] login, sign up (using JWT, sendGrid), Create Docker-compose 22 +- [x] login, sign up, Create Docker-compose
23 23
24 - 4/20 ~ 4/26 24 - 4/20 ~ 4/26
25 -- [ ] Find ID, PW 25 +- [X] Find ID, PW
26 + : find password : sendgrid, find userID : twilio
26 27
27 - 4/27 ~ 5/3 28 - 4/27 ~ 5/3
28 - [ ] Make chat room (using GraphQL's subscription) 29 - [ ] Make chat room (using GraphQL's subscription)
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
4 4
5 Subject : Making website in order to practice latest web technologies 5 Subject : Making website in order to practice latest web technologies
6 6
7 +- (Tech : react.js, react hooks, styled-components, GraphQL, Prisma, Apollo, AWS, Docker, sendGrid, Twilio, Maxmind)
8 +
7 sub-subject : making chat site using JS 9 sub-subject : making chat site using JS
8 10
9 # Schedule 11 # Schedule
...@@ -11,20 +13,19 @@ sub-subject : making chat site using JS ...@@ -11,20 +13,19 @@ sub-subject : making chat site using JS
11 - 4/6 ~ 4/12 13 - 4/6 ~ 4/12
12 - [x] Set development environment 14 - [x] Set development environment
13 15
14 - (tech : react.js, react hooks, styled-components, GraphQL, Prisma, Apollo, AWS, Docker)
15 -
16 - 4/13 ~ 4/19 16 - 4/13 ~ 4/19
17 -- [x] login, sign up (using JWT, sendGrid), Create Docker-compose 17 +- [x] login, sign up, Create Docker-compose
18 18
19 - 4/20 ~ 4/26 19 - 4/20 ~ 4/26
20 -- [ ] Find ID, PW 20 +- [X] Find ID, PW <br>
21 + -> find password : sendgrid || find userID : twilio
21 22
22 - 4/27 ~ 5/3 23 - 4/27 ~ 5/3
23 - [ ] Make chat room (using GraphQL's subscription) 24 - [ ] Make chat room (using GraphQL's subscription)
24 25
25 - 5/4 ~ 5/10 26 - 5/4 ~ 5/10
26 - [ ] Mid-term exam, Debugging 27 - [ ] Mid-term exam, Debugging
27 - 28 +
28 - 5/11 ~ 5/17 29 - 5/11 ~ 5/17
29 - [ ] Plan chat category and make code 30 - [ ] Plan chat category and make code
30 31
......
This diff is collapsed. Click to expand it.
...@@ -15,21 +15,23 @@ ...@@ -15,21 +15,23 @@
15 "homepage": "https://bitbucket.org/vel1024/capstone2#readme", 15 "homepage": "https://bitbucket.org/vel1024/capstone2#readme",
16 "dependencies": { 16 "dependencies": {
17 "@prisma/client": "^2.0.0-beta.2", 17 "@prisma/client": "^2.0.0-beta.2",
18 + "@sendgrid/mail": "^7.0.1",
18 "bcryptjs": "^2.4.3", 19 "bcryptjs": "^2.4.3",
20 + "country-data": "0.0.31",
19 "dotenv": "^8.2.0", 21 "dotenv": "^8.2.0",
20 "graphql-tools": "^4.0.7", 22 "graphql-tools": "^4.0.7",
21 "graphql-yoga": "^1.18.3", 23 "graphql-yoga": "^1.18.3",
22 "jsonwebtoken": "^8.5.1", 24 "jsonwebtoken": "^8.5.1",
23 "merge-graphql-schemas": "^1.7.7", 25 "merge-graphql-schemas": "^1.7.7",
24 - "nodemailer": "^6.4.6", 26 + "passport": "^0.4.1",
25 - "nodemailer-sendgrid-transport": "^0.2.0", 27 + "passport-jwt": "^4.0.0",
26 "twilio": "^3.42.2" 28 "twilio": "^3.42.2"
27 }, 29 },
28 "devDependencies": { 30 "devDependencies": {
29 "@babel/core": "^7.9.0", 31 "@babel/core": "^7.9.0",
30 "@babel/node": "^7.8.7", 32 "@babel/node": "^7.8.7",
31 "@babel/preset-env": "^7.9.0", 33 "@babel/preset-env": "^7.9.0",
32 - "@prisma/cli": "^2.0.0-beta.2", 34 + "@prisma/cli": "^2.0.0-beta.3",
33 "morgan": "^1.10.0", 35 "morgan": "^1.10.0",
34 "nodemon": "^2.0.2" 36 "nodemon": "^2.0.2"
35 } 37 }
......
1 -# Migration `20200419160117-init`
2 -
3 -This migration has been generated by sdy at 4/19/2020, 4:01:17 PM.
4 -You can check out the [state of the schema](./schema.prisma) after the migration.
5 -
6 -## Database Steps
7 -
8 -```sql
9 -CREATE TABLE `chat_schema`.`User` (
10 - `avatarUrl` varchar(191) ,
11 - `bio` varchar(191) ,
12 - `createdAt` datetime DEFAULT CURRENT_TIMESTAMP ,
13 - `email` varchar(191) NOT NULL ,
14 - `id` int NOT NULL AUTO_INCREMENT,
15 - `loginSecret` varchar(191) ,
16 - `name` varchar(191) NOT NULL ,
17 - `password` varchar(191) NOT NULL ,
18 - PRIMARY KEY (`id`)
19 -)
20 -DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
21 -
22 -CREATE TABLE `chat_schema`.`Room` (
23 - `id` int NOT NULL AUTO_INCREMENT,
24 - PRIMARY KEY (`id`)
25 -)
26 -DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
27 -
28 -CREATE TABLE `chat_schema`.`Category` (
29 - `id` int NOT NULL AUTO_INCREMENT,
30 - `name` varchar(191) DEFAULT '' ,
31 - PRIMARY KEY (`id`)
32 -)
33 -DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
34 -
35 -CREATE TABLE `chat_schema`.`Message` (
36 - `id` int NOT NULL AUTO_INCREMENT,
37 - `senderId` int NOT NULL ,
38 - `text` varchar(191) DEFAULT '' ,
39 - PRIMARY KEY (`id`)
40 -)
41 -DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
42 -
43 -CREATE TABLE `chat_schema`.`_RoomToUser` (
44 - `A` int NOT NULL ,
45 - `B` int NOT NULL
46 -)
47 -DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
48 -
49 -CREATE TABLE `chat_schema`.`_CategoryToRoom` (
50 - `A` int NOT NULL ,
51 - `B` int NOT NULL
52 -)
53 -DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
54 -
55 -CREATE UNIQUE INDEX `User.email` ON `chat_schema`.`User`(`email`)
56 -
57 -CREATE UNIQUE INDEX `_RoomToUser_AB_unique` ON `chat_schema`.`_RoomToUser`(`A`,`B`)
58 -
59 -CREATE INDEX `_RoomToUser_B_index` ON `chat_schema`.`_RoomToUser`(`B`)
60 -
61 -CREATE UNIQUE INDEX `_CategoryToRoom_AB_unique` ON `chat_schema`.`_CategoryToRoom`(`A`,`B`)
62 -
63 -CREATE INDEX `_CategoryToRoom_B_index` ON `chat_schema`.`_CategoryToRoom`(`B`)
64 -
65 -ALTER TABLE `chat_schema`.`Message` ADD FOREIGN KEY (`senderId`) REFERENCES `chat_schema`.`User`(`id`) ON DELETE CASCADE ON UPDATE CASCADE
66 -
67 -ALTER TABLE `chat_schema`.`_RoomToUser` ADD FOREIGN KEY (`A`) REFERENCES `chat_schema`.`Room`(`id`) ON DELETE CASCADE ON UPDATE CASCADE
68 -
69 -ALTER TABLE `chat_schema`.`_RoomToUser` ADD FOREIGN KEY (`B`) REFERENCES `chat_schema`.`User`(`id`) ON DELETE CASCADE ON UPDATE CASCADE
70 -
71 -ALTER TABLE `chat_schema`.`_CategoryToRoom` ADD FOREIGN KEY (`A`) REFERENCES `chat_schema`.`Category`(`id`) ON DELETE CASCADE ON UPDATE CASCADE
72 -
73 -ALTER TABLE `chat_schema`.`_CategoryToRoom` ADD FOREIGN KEY (`B`) REFERENCES `chat_schema`.`Room`(`id`) ON DELETE CASCADE ON UPDATE CASCADE
74 -
75 -DROP TABLE `chat_schema`.`_migration`;
76 -
77 -DROP TABLE `chat_schema`.`test`;
78 -```
79 -
80 -## Changes
81 -
82 -```diff
83 -diff --git schema.prisma schema.prisma
84 -migration ..20200419160117-init
85 ---- datamodel.dml
86 -+++ datamodel.dml
87 -@@ -1,0 +1,41 @@
88 -+generator client {
89 -+ provider = "prisma-client-js"
90 -+ binaryTargets = ["native", "debian-openssl-1.1.x"]
91 -+}
92 -+
93 -+datasource db {
94 -+ provider = "mysql"
95 -+ url = env("DATABASE_URL")
96 -+}
97 -+
98 -+model User {
99 -+ id Int @default(autoincrement()) @id
100 -+ avatarUrl String?
101 -+ email String @unique
102 -+ password String
103 -+ name String
104 -+ loginSecret String?
105 -+ bio String?
106 -+ rooms Room[] @relation(references: [id])
107 -+ messages Message[]
108 -+ createdAt DateTime? @default(now())
109 -+}
110 -+
111 -+model Room {
112 -+ id Int @default(autoincrement()) @id
113 -+ participants User[] @relation(references: [id])
114 -+ categories Category[] @relation(references: [id])
115 -+}
116 -+
117 -+model Category {
118 -+ id Int @default(autoincrement()) @id
119 -+ name String? @default("")
120 -+ rooms Room[] @relation(references: [id])
121 -+}
122 -+
123 -+model Message {
124 -+ id Int @default(autoincrement()) @id
125 -+ text String? @default("")
126 -+ sender User @relation(fields: [senderId], references: [id])
127 -+ senderId Int
128 -+}
129 -```
130 -
131 -
1 -generator client {
2 - provider = "prisma-client-js"
3 - binaryTargets = ["native", "debian-openssl-1.1.x"]
4 -}
5 -
6 -datasource db {
7 - provider = "mysql"
8 - url = "***"
9 -}
10 -
11 -model User {
12 - id Int @default(autoincrement()) @id
13 - avatarUrl String?
14 - email String @unique
15 - password String
16 - name String
17 - loginSecret String?
18 - bio String?
19 - rooms Room[] @relation(references: [id])
20 - messages Message[]
21 - createdAt DateTime? @default(now())
22 -}
23 -
24 -model Room {
25 - id Int @default(autoincrement()) @id
26 - participants User[] @relation(references: [id])
27 - categories Category[] @relation(references: [id])
28 -}
29 -
30 -model Category {
31 - id Int @default(autoincrement()) @id
32 - name String? @default("")
33 - rooms Room[] @relation(references: [id])
34 -}
35 -
36 -model Message {
37 - id Int @default(autoincrement()) @id
38 - text String? @default("")
39 - sender User @relation(fields: [senderId], references: [id])
40 - senderId Int
41 -}
...\ No newline at end of file ...\ No newline at end of file
This diff is collapsed. Click to expand it.
1 -# Migration `20200424124259-init`
2 -
3 -This migration has been generated by sdy at 4/24/2020, 12:42:59 PM.
4 -You can check out the [state of the schema](./schema.prisma) after the migration.
5 -
6 -## Database Steps
7 -
8 -```sql
9 -CREATE TABLE `chat_schema`.`User` (
10 - `avatarUrl` varchar(191) ,
11 - `bio` varchar(191) ,
12 - `createdAt` datetime DEFAULT CURRENT_TIMESTAMP ,
13 - `email` varchar(191) NOT NULL ,
14 - `emailSecret` varchar(191) ,
15 - `id` int NOT NULL AUTO_INCREMENT,
16 - `name` varchar(191) NOT NULL ,
17 - `password` varchar(191) NOT NULL ,
18 - `phoneNumber` int ,
19 - `phoneSecret` varchar(191) ,
20 - PRIMARY KEY (`id`)
21 -)
22 -DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
23 -
24 -CREATE TABLE `chat_schema`.`Room` (
25 - `id` int NOT NULL AUTO_INCREMENT,
26 - PRIMARY KEY (`id`)
27 -)
28 -DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
29 -
30 -CREATE TABLE `chat_schema`.`Category` (
31 - `id` int NOT NULL AUTO_INCREMENT,
32 - `name` varchar(191) DEFAULT '' ,
33 - PRIMARY KEY (`id`)
34 -)
35 -DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
36 -
37 -CREATE TABLE `chat_schema`.`Message` (
38 - `id` int NOT NULL AUTO_INCREMENT,
39 - `senderId` int NOT NULL ,
40 - `text` varchar(191) DEFAULT '' ,
41 - PRIMARY KEY (`id`)
42 -)
43 -DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
44 -
45 -CREATE TABLE `chat_schema`.`_RoomToUser` (
46 - `A` int NOT NULL ,
47 - `B` int NOT NULL
48 -)
49 -DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
50 -
51 -CREATE TABLE `chat_schema`.`_CategoryToRoom` (
52 - `A` int NOT NULL ,
53 - `B` int NOT NULL
54 -)
55 -DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
56 -
57 -CREATE UNIQUE INDEX `User.email` ON `chat_schema`.`User`(`email`)
58 -
59 -CREATE UNIQUE INDEX `_RoomToUser_AB_unique` ON `chat_schema`.`_RoomToUser`(`A`,`B`)
60 -
61 -CREATE INDEX `_RoomToUser_B_index` ON `chat_schema`.`_RoomToUser`(`B`)
62 -
63 -CREATE UNIQUE INDEX `_CategoryToRoom_AB_unique` ON `chat_schema`.`_CategoryToRoom`(`A`,`B`)
64 -
65 -CREATE INDEX `_CategoryToRoom_B_index` ON `chat_schema`.`_CategoryToRoom`(`B`)
66 -
67 -ALTER TABLE `chat_schema`.`Message` ADD FOREIGN KEY (`senderId`) REFERENCES `chat_schema`.`User`(`id`) ON DELETE CASCADE ON UPDATE CASCADE
68 -
69 -ALTER TABLE `chat_schema`.`_RoomToUser` ADD FOREIGN KEY (`A`) REFERENCES `chat_schema`.`Room`(`id`) ON DELETE CASCADE ON UPDATE CASCADE
70 -
71 -ALTER TABLE `chat_schema`.`_RoomToUser` ADD FOREIGN KEY (`B`) REFERENCES `chat_schema`.`User`(`id`) ON DELETE CASCADE ON UPDATE CASCADE
72 -
73 -ALTER TABLE `chat_schema`.`_CategoryToRoom` ADD FOREIGN KEY (`A`) REFERENCES `chat_schema`.`Category`(`id`) ON DELETE CASCADE ON UPDATE CASCADE
74 -
75 -ALTER TABLE `chat_schema`.`_CategoryToRoom` ADD FOREIGN KEY (`B`) REFERENCES `chat_schema`.`Room`(`id`) ON DELETE CASCADE ON UPDATE CASCADE
76 -
77 -DROP TABLE `chat_schema`.`_categorytoroom`;
78 -
79 -DROP TABLE `chat_schema`.`_migration`;
80 -
81 -DROP TABLE `chat_schema`.`_roomtouser`;
82 -
83 -DROP TABLE `chat_schema`.`category`;
84 -
85 -DROP TABLE `chat_schema`.`message`;
86 -
87 -DROP TABLE `chat_schema`.`room`;
88 -
89 -DROP TABLE `chat_schema`.`test`;
90 -
91 -DROP TABLE `chat_schema`.`user`;
92 -```
93 -
94 -## Changes
95 -
96 -```diff
97 -diff --git schema.prisma schema.prisma
98 -migration 20200419160117-init..20200424124259-init
99 ---- datamodel.dml
100 -+++ datamodel.dml
101 -@@ -4,18 +4,20 @@
102 - }
103 - datasource db {
104 - provider = "mysql"
105 -- url = "***"
106 -+ url = env("DATABASE_URL")
107 - }
108 - model User {
109 - id Int @default(autoincrement()) @id
110 - avatarUrl String?
111 - email String @unique
112 - password String
113 - name String
114 -- loginSecret String?
115 -+ phoneNumber Int?
116 -+ emailSecret String?
117 -+ phoneSecret String?
118 - bio String?
119 - rooms Room[] @relation(references: [id])
120 - messages Message[]
121 - createdAt DateTime? @default(now())
122 -```
123 -
124 -
1 -generator client {
2 - provider = "prisma-client-js"
3 - binaryTargets = ["native", "debian-openssl-1.1.x"]
4 -}
5 -
6 -datasource db {
7 - provider = "mysql"
8 - url = "***"
9 -}
10 -
11 -model User {
12 - id Int @default(autoincrement()) @id
13 - avatarUrl String?
14 - email String @unique
15 - password String
16 - name String
17 - phoneNumber Int?
18 - emailSecret String?
19 - phoneSecret String?
20 - bio String?
21 - rooms Room[] @relation(references: [id])
22 - messages Message[]
23 - createdAt DateTime? @default(now())
24 -}
25 -
26 -model Room {
27 - id Int @default(autoincrement()) @id
28 - participants User[] @relation(references: [id])
29 - categories Category[] @relation(references: [id])
30 -}
31 -
32 -model Category {
33 - id Int @default(autoincrement()) @id
34 - name String? @default("")
35 - rooms Room[] @relation(references: [id])
36 -}
37 -
38 -model Message {
39 - id Int @default(autoincrement()) @id
40 - text String? @default("")
41 - sender User @relation(fields: [senderId], references: [id])
42 - senderId Int
43 -}
...\ No newline at end of file ...\ No newline at end of file
1 -{
2 - "version": "0.3.14-fixed",
3 - "steps": [
4 - {
5 - "tag": "CreateField",
6 - "model": "User",
7 - "field": "phoneNumber",
8 - "type": "Int",
9 - "arity": "Optional"
10 - },
11 - {
12 - "tag": "CreateField",
13 - "model": "User",
14 - "field": "emailSecret",
15 - "type": "String",
16 - "arity": "Optional"
17 - },
18 - {
19 - "tag": "CreateField",
20 - "model": "User",
21 - "field": "phoneSecret",
22 - "type": "String",
23 - "arity": "Optional"
24 - },
25 - {
26 - "tag": "DeleteField",
27 - "model": "User",
28 - "field": "loginSecret"
29 - }
30 - ]
31 -}
...\ No newline at end of file ...\ No newline at end of file
1 -# IF THERE'S A GIT CONFLICT IN THIS FILE, DON'T SOLVE IT MANUALLY!
2 -# INSTEAD EXECUTE `prisma migrate fix`
3 -# Prisma Migrate lockfile v1
4 -# Read more about conflict resolution here: TODO
5 -
6 -20200419160117-init
7 -20200424124259-init
...\ No newline at end of file ...\ No newline at end of file
1 generator client { 1 generator client {
2 provider = "prisma-client-js" 2 provider = "prisma-client-js"
3 - binaryTargets = ["native", "debian-openssl-1.1.x"] 3 + binaryTargets = ["native", "debian-openssl-1.1.x", "darwin"]
4 } 4 }
5 5
6 datasource db { 6 datasource db {
...@@ -9,18 +9,19 @@ datasource db { ...@@ -9,18 +9,19 @@ datasource db {
9 } 9 }
10 10
11 model User { 11 model User {
12 - id Int @default(autoincrement()) @id 12 + id Int @default(autoincrement()) @id
13 - avatarUrl String? 13 + avatarUrl String? @default("")
14 - email String @unique 14 + email String @unique
15 - password String 15 + password String
16 - name String 16 + name String
17 - phoneNumber String? @unique 17 + phoneNum String @default("") @unique
18 - emailSecret String? 18 + emailSecret String? @default("")
19 - phoneSecret String? 19 + phoneSecret String? @default("")
20 - bio String? 20 + bio String? @default("")
21 - rooms Room[] @relation(references: [id]) 21 + rooms Room[] @relation(references: [id])
22 - messages Message[] 22 + sendMessage Message[] @relation("Sender")
23 - createdAt DateTime? @default(now()) 23 + receiveMessage Message[] @relation("Receiver")
24 + createdAt DateTime? @default(now())
24 } 25 }
25 26
26 model Room { 27 model Room {
...@@ -36,8 +37,11 @@ model Category { ...@@ -36,8 +37,11 @@ model Category {
36 } 37 }
37 38
38 model Message { 39 model Message {
39 - id Int @default(autoincrement()) @id 40 + id Int @default(autoincrement()) @id
40 - text String? @default("") 41 + text String? @default("")
41 - sender User @relation(fields: [senderId], references: [id]) 42 + from User[] @relation("Sender", references: [id])
42 - senderId Int 43 + to User[] @relation("Receiver", references: [id])
44 + room Room @relation(fields: [roomId], references: [id])
45 + roomId Int
46 + createdAt DateTime? @default(now())
43 } 47 }
...\ No newline at end of file ...\ No newline at end of file
......
1 +type Mutation {
2 + newMessage(receiverId: Int, message: String!, roomId: Int): Message!
3 +}
1 +import { isAuthenticated, prisma } from "../../../utils";
2 +import { ONE_TO_ONE_MESSAGE } from "../../../topics";
3 +
4 +export default {
5 + Mutation: {
6 + newMessage: async (_, args, { request, pubsub }) => {
7 + isAuthenticated(request);
8 + const { user } = request;
9 + const { receiverId, message, roomId } = args;
10 + let room = await prisma.room.findOne({
11 + where: {
12 + id: roomId,
13 + },
14 + });
15 + room = await prisma.room.update({
16 + where: {
17 + id: roomId,
18 + },
19 + data: {
20 + participants: {
21 + connect: [{ id: user.id }, { id: receiverId }],
22 + },
23 + },
24 + });
25 + // 방이 없는 경우
26 + if (room === undefined || room === null) {
27 + // 보내는 사람과 받는 사람이 다른 경우
28 + if (user.id !== receiverId) {
29 + room = await prisma.room.create({
30 + data: {
31 + participants: {
32 + connect: [{ id: receiverId }, { id: user.id }],
33 + },
34 + },
35 + });
36 + } else {
37 + // 자기 자신에게 보내는 경우
38 + room = await prisma.room.create({
39 + data: {
40 + participants: {
41 + connect: [{ id: user.id }],
42 + },
43 + },
44 + });
45 + }
46 + }
47 + if (!room) {
48 + throw new Error("There is no room");
49 + }
50 + const subMessage = await prisma.message.create({
51 + data: {
52 + text: message,
53 + to: {
54 + connect: {
55 + id: receiverId,
56 + },
57 + },
58 + from: {
59 + connect: {
60 + id: user.id,
61 + },
62 + },
63 + room: {
64 + connect: {
65 + id: room.id,
66 + },
67 + },
68 + },
69 + });
70 + console.log(subMessage);
71 + pubsub.publish(ONE_TO_ONE_MESSAGE, subMessage);
72 + return subMessage;
73 + },
74 + },
75 +};
1 type Subscription { 1 type Subscription {
2 - sendMessage(message: String!): Message! 2 + subMessage: Message
3 } 3 }
......
1 -import { prisma } from "../../../utils"; 1 +import { ONE_TO_ONE_MESSAGE } from "../../../topics";
2 2
3 export default { 3 export default {
4 Subscription: { 4 Subscription: {
5 - sendMessage: async (_, args) => { 5 + subMessage: {
6 - const { message } = args; 6 + subscribe: async (_, __, { pubsub }) => {
7 + return pubsub.asyncIterator(ONE_TO_ONE_MESSAGE);
8 + },
7 }, 9 },
8 }, 10 },
9 }; 11 };
......
1 +type Mutation {
2 + createRoom(participantsId: [Int], categories: [Int]): Room!
3 +}
1 +import { prisma } from "../../../utils";
2 +
3 +export default {
4 + Mutation: {
5 + createRoom: async (_, args) => {
6 + const { participantsId, categories } = args;
7 + let newRoom, participantId;
8 + if (participantsId !== undefined) {
9 + newRoom = await prisma.room.create({
10 + data: {
11 + participants: {
12 + connect: {
13 + id: (participantId = participantsId.forEach(
14 + (cur, _, __) => cur
15 + )),
16 + },
17 + },
18 + },
19 + });
20 + }
21 + if (categories !== undefined) {
22 + newRoom = await prisma.room.create({
23 + data: {
24 + categories: {
25 + connect: {
26 + id: (category = categories.forEach((cur, _, __) => cur)),
27 + },
28 + },
29 + },
30 + });
31 + }
32 + return newRoom;
33 + },
34 + },
35 +};
...@@ -5,5 +5,6 @@ type Mutation { ...@@ -5,5 +5,6 @@ type Mutation {
5 password: String! 5 password: String!
6 bio: String 6 bio: String
7 avatarUrl: String 7 avatarUrl: String
8 + phoneNum: String
8 ): AuthPayload! 9 ): AuthPayload!
9 } 10 }
......
1 -import { prisma, generateToken } from "../../../utils"; 1 +import { prisma, generateToken, changePhoneNumber } from "../../../utils";
2 import bcrypt from "bcryptjs"; 2 import bcrypt from "bcryptjs";
3 3
4 export default { 4 export default {
5 Mutation: { 5 Mutation: {
6 createAccount: async (_, args) => { 6 createAccount: async (_, args) => {
7 - const { name, password, email, bio = "", avatarUrl = "" } = args; 7 + const { name, password, email, bio, avatarUrl, phoneNum } = args;
8 const encryptPw = await bcrypt.hash(password, 10); 8 const encryptPw = await bcrypt.hash(password, 10);
9 + // TODO: Find user's country code and change new phone number value
10 + const newPhoneNumber = await changePhoneNumber(phoneNum, "+82");
9 const user = await prisma.user.create({ 11 const user = await prisma.user.create({
10 data: { 12 data: {
11 name, 13 name,
...@@ -13,6 +15,7 @@ export default { ...@@ -13,6 +15,7 @@ export default {
13 bio, 15 bio,
14 avatarUrl, 16 avatarUrl,
15 password: encryptPw, 17 password: encryptPw,
18 + phoneNum: newPhoneNumber,
16 }, 19 },
17 }); 20 });
18 const token = generateToken(user.id); 21 const token = generateToken(user.id);
......
1 +type Mutation {
2 + deleteUser(email: String!): User!
3 +}
1 +import { isAuthenticated, prisma } from "../../../utils";
2 +
3 +export default {
4 + Mutation: {
5 + deleteUser: async (_, args, { request }) => {
6 + isAuthenticated(request);
7 + const { email } = args;
8 + return prisma.user.delete({
9 + where: {
10 + email,
11 + },
12 + });
13 + },
14 + },
15 +};
1 type Mutation { 1 type Mutation {
2 editProfile( 2 editProfile(
3 + email: String!
3 name: String 4 name: String
4 - email: String
5 bio: String 5 bio: String
6 avatarUrl: String 6 avatarUrl: String
7 + phoneNum: String
7 ): User! 8 ): User!
8 } 9 }
......
1 -import { prisma } from "../../../utils"; 1 +import { isAuthenticated, prisma } from "../../../utils";
2 2
3 export default { 3 export default {
4 Mutation: { 4 Mutation: {
5 - editProfile: async (_, args, { request }) => {}, 5 + editProfile: async (_, args, { request }) => {
6 + isAuthenticated(request);
7 + const { name, email, avatarUrl, bio, phoneNum } = args;
8 + const user = await prisma.user.findOne({
9 + where: {
10 + email,
11 + },
12 + });
13 + if (user) {
14 + const updateUser = await prisma.user.update({
15 + where: {
16 + email,
17 + },
18 + data: {
19 + name,
20 + bio,
21 + avatarUrl,
22 + phoneNum,
23 + },
24 + });
25 + return updateUser;
26 + } else {
27 + throw new Error("There is no such a user");
28 + }
29 + },
6 }, 30 },
7 }; 31 };
......
1 type Query { 1 type Query {
2 - findEmail(phoneNumber: String!): User! 2 + findEmail(phoneNum: String!): Boolean!
3 } 3 }
......
1 -import { prisma, generateSecret } from "../../../utils"; 1 +import { prisma, isAuthenticated, changePhoneNumber } from "../../../utils";
2 import twilio from "twilio"; 2 import twilio from "twilio";
3 3
4 export default { 4 export default {
5 Query: { 5 Query: {
6 findEmail: async (_, args) => { 6 findEmail: async (_, args) => {
7 - const { phoneNumber } = args; 7 + const { phoneNum } = args;
8 - const accountSid = process.env.TWILIO_SID; 8 + const changeNum = await changePhoneNumber(phoneNum, "+82");
9 - const authToken = process.env.TWILIO_AUTH_TOKEN; 9 + const user = await prisma.user.findOne({
10 - const client = new twilio(accountSid, authToken);
11 - const randomWords = generateSecret();
12 - client.messages
13 - .create({
14 - body: `Please enter this word : ${randomWords}`,
15 - to: `${phoneNumber}`,
16 - from: "KhuChat",
17 - })
18 - .then((message) => console.log(message.sid));
19 -
20 - const user = await prisma.user.update({
21 where: { 10 where: {
22 - phoneNumber, 11 + phoneNum: changeNum,
23 - },
24 - data: {
25 - phoneSecret: randomWords,
26 }, 12 },
27 }); 13 });
28 - return user; 14 + if (user && isAuthenticated) {
15 + const accountSid = process.env.TWILIO_SID;
16 + const authToken = process.env.TWILIO_AUTH_TOKEN;
17 + const twilioPhone = process.env.TWILIO_PHONE_NUMBER;
18 + const client = new twilio(accountSid, authToken);
19 + client.messages
20 + .create({
21 + body: `Your Email is : ${user.email}`,
22 + to: `${changeNum}`,
23 + from: `${twilioPhone}`,
24 + })
25 + .then((message) => {
26 + console.log(message.sid);
27 + });
28 + return true;
29 + } else {
30 + throw new Error("You need to login first");
31 + }
29 }, 32 },
30 }, 33 },
31 }; 34 };
......
1 -import { prisma, generateSecret, sendSecretMail } from "../../../utils"; 1 +import { prisma, generateSecret, sendEmail } from "../../../utils";
2 import bcrypt from "bcryptjs"; 2 import bcrypt from "bcryptjs";
3 3
4 export default { 4 export default {
...@@ -6,15 +6,14 @@ export default { ...@@ -6,15 +6,14 @@ export default {
6 requestEmailSecret: async (_, args) => { 6 requestEmailSecret: async (_, args) => {
7 const { email } = args; 7 const { email } = args;
8 const emailSecret = generateSecret(); 8 const emailSecret = generateSecret();
9 - const encryptSecret = await bcrypt.hash(emailSecret, 10);
10 try { 9 try {
11 - await sendSecretMail(email, emailSecret); 10 + await sendEmail(email, emailSecret);
12 await prisma.user.update({ 11 await prisma.user.update({
13 where: { 12 where: {
14 email, 13 email,
15 }, 14 },
16 data: { 15 data: {
17 - emailSecret: encryptSecret, 16 + emailSecret,
18 }, 17 },
19 }); 18 });
20 return true; 19 return true;
......
1 type Mutation { 1 type Mutation {
2 resetPassword( 2 resetPassword(
3 - secret: String! 3 + emailSecret: String!
4 email: String! 4 email: String!
5 passwordOne: String! 5 passwordOne: String!
6 passwordTwo: String! 6 passwordTwo: String!
......
1 -import { prisma } from "../../../utils"; 1 +import { prisma, isAuthenticated } from "../../../utils";
2 import bcrypt from "bcryptjs"; 2 import bcrypt from "bcryptjs";
3 3
4 export default { 4 export default {
5 Mutation: { 5 Mutation: {
6 - resetPassword: async (_, args) => { 6 + resetPassword: async (_, args, { request }) => {
7 - const { secret, email, passwordOne, passwordTwo } = args; 7 + isAuthenticated(request);
8 + const { emailSecret, email, passwordOne, passwordTwo } = args;
8 const user = await prisma.user.findOne({ 9 const user = await prisma.user.findOne({
9 where: { 10 where: {
10 email, 11 email,
11 }, 12 },
12 }); 13 });
13 - const encryptSecret = await bcrypt.hash(user.emailSecret, 10); 14 + if (user.emailSecret !== emailSecret) {
14 - if (encryptSecret !== secret) {
15 throw new Error( 15 throw new Error(
16 "not vaild secret value!, input another value or resend email" 16 "not vaild secret value!, input another value or resend email"
17 ); 17 );
...@@ -20,13 +20,14 @@ export default { ...@@ -20,13 +20,14 @@ export default {
20 // For check new password is right, the two things must be same. 20 // For check new password is right, the two things must be same.
21 throw new Error("the two password don't match each other, try again"); 21 throw new Error("the two password don't match each other, try again");
22 } else { 22 } else {
23 + const encyptPW = await bcrypt.hash(passwordOne, 10);
23 await prisma.user.update({ 24 await prisma.user.update({
24 where: { 25 where: {
25 email, 26 email,
26 }, 27 },
27 data: { 28 data: {
28 emailSecret: "", 29 emailSecret: "",
29 - password: passwordOne, 30 + password: encyptPW,
30 }, 31 },
31 }); 32 });
32 } 33 }
......
1 type Query { 1 type Query {
2 - searchUser(name: String!): [User!]! 2 + searchUser(term: String!): [User!]!
3 } 3 }
......
...@@ -3,8 +3,24 @@ import { prisma } from "../../../utils"; ...@@ -3,8 +3,24 @@ import { prisma } from "../../../utils";
3 export default { 3 export default {
4 Query: { 4 Query: {
5 searchUser: async (_, args) => { 5 searchUser: async (_, args) => {
6 - const { name } = args; 6 + const { term } = args;
7 - const user = await prisma.user.findOne({}); 7 + try {
8 + return prisma.user.findMany({
9 + where: {
10 + OR: [
11 + {
12 + name: { contains: term },
13 + },
14 + {
15 + email: { contains: term },
16 + },
17 + ],
18 + },
19 + });
20 + } catch (error) {
21 + console.log(error);
22 + return null;
23 + }
8 }, 24 },
9 }, 25 },
10 }; 26 };
......
1 type Query { 1 type Query {
2 - seeProfile(id: String!): User! 2 + seeProfile(email: String!): User!
3 } 3 }
......
1 -import { prisma } from "../../../utils"; 1 +import { prisma, isAuthenticated } from "../../../utils";
2 2
3 export default { 3 export default {
4 Query: { 4 Query: {
5 - seeProfile: async (_, args) => { 5 + seeProfile: async (_, args, { request }) => {
6 - const { id } = args; 6 + isAuthenticated(request);
7 + const { email } = args;
8 + return prisma.user.findOne({
9 + where: {
10 + email,
11 + },
12 + });
7 }, 13 },
8 }, 14 },
9 }; 15 };
......
1 type User { 1 type User {
2 id: ID! 2 id: ID!
3 avatarUrl: String 3 avatarUrl: String
4 - name: String 4 + name: String!
5 email: String! 5 email: String!
6 - password: String 6 + password: String!
7 - phoneNumber: String
8 emailSecret: String 7 emailSecret: String
8 + phoneNum: String!
9 phoneSecret: String 9 phoneSecret: String
10 bio: String 10 bio: String
11 rooms: [Room] 11 rooms: [Room]
12 createdAt: String 12 createdAt: String
13 messages: [Message] 13 messages: [Message]
14 + from: Message
15 + to: [Message]
14 } 16 }
15 17
16 type Room { 18 type Room {
...@@ -27,8 +29,12 @@ type Category { ...@@ -27,8 +29,12 @@ type Category {
27 29
28 type Message { 30 type Message {
29 id: ID! 31 id: ID!
30 - text: String 32 + text: String!
31 - sender: User 33 + from: User
34 + to: [User]
35 + room: Room!
36 + createdAt: String
37 + updatedAt: String
32 } 38 }
33 39
34 type AuthPayload { 40 type AuthPayload {
......
1 +import passport from "passport";
2 +import { Strategy, ExtractJwt } from "passport-jwt";
3 +import { prisma } from "./utils";
4 +
5 +const JWTOptions = {
6 + jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
7 + secretOrKey: process.env.JWT_SECRET,
8 +};
9 +
10 +const verifyUser = async (payload, done) => {
11 + try {
12 + const user = await prisma.user.findOne({
13 + where: {
14 + id: payload.id,
15 + },
16 + });
17 + if (user !== null) {
18 + return done(null, user);
19 + } else {
20 + return done(null, false);
21 + }
22 + } catch (error) {
23 + return done(null, false);
24 + }
25 +};
26 +
27 +export const authenticateJWT = (req, res, next) => {
28 + passport.authenticate("jwt", { session: false }, (error, user) => {
29 + if (user) {
30 + req.user = user;
31 + }
32 + next();
33 + })(req, res, next);
34 +};
35 +
36 +passport.use(new Strategy(JWTOptions, verifyUser));
37 +passport.initialize();
1 import dotenv from "dotenv"; 1 import dotenv from "dotenv";
2 dotenv.config(); 2 dotenv.config();
3 -import { GraphQLServer } from "graphql-yoga"; 3 +import { GraphQLServer, PubSub } from "graphql-yoga";
4 import morgan from "morgan"; 4 import morgan from "morgan";
5 import schema from "./schema"; 5 import schema from "./schema";
6 -import { prisma } from "./utils"; 6 +import "./passport";
7 +import { authenticateJWT } from "./passport";
7 8
8 const PORT = process.env.PORT; 9 const PORT = process.env.PORT;
9 10
11 +const pubsub = new PubSub();
12 +
10 const server = new GraphQLServer({ 13 const server = new GraphQLServer({
11 schema, 14 schema,
12 - context: (request) => { 15 + context: ({ request }) => ({ request, pubsub }),
13 - return {
14 - ...request,
15 - prisma,
16 - };
17 - },
18 }); 16 });
19 17
20 server.express.use(morgan("dev")); 18 server.express.use(morgan("dev"));
19 +server.express.use(authenticateJWT);
21 20
22 server.start(() => console.log(`server is running : http://localhost:${PORT}`)); 21 server.start(() => console.log(`server is running : http://localhost:${PORT}`));
......
1 +export const ONE_TO_ONE_MESSAGE = "one-to-one message";
1 +import dotenv from "dotenv";
2 +dotenv.config();
1 import { PrismaClient } from "@prisma/client"; 3 import { PrismaClient } from "@prisma/client";
2 import { nouns, adjectives } from "./words"; 4 import { nouns, adjectives } from "./words";
3 import jwt from "jsonwebtoken"; 5 import jwt from "jsonwebtoken";
4 -import nodemailer from "nodemailer"; 6 +import sgMail from "@sendgrid/mail";
5 -import sgTransport from "nodemailer-sendgrid-transport"; 7 +sgMail.setApiKey(process.env.SENDGRID_API_KEY);
6 8
7 export const prisma = new PrismaClient(); 9 export const prisma = new PrismaClient();
8 10
...@@ -16,30 +18,45 @@ export const getUserId = (context) => { ...@@ -16,30 +18,45 @@ export const getUserId = (context) => {
16 throw new Error("There is no vaild user"); 18 throw new Error("There is no vaild user");
17 }; 19 };
18 20
21 +export const isAuthenticated = (request) => {
22 + if (!request.user) {
23 + throw new Error("You need to login First");
24 + }
25 + return;
26 +};
27 +
28 +export const changePhoneNumber = (phoneNum, locationNum) => {
29 + var leftStr = locationNum;
30 + var rightStr = phoneNum.slice(1, phoneNum.length);
31 + var newStr = leftStr + rightStr;
32 + return newStr;
33 +};
34 +
19 export const generateSecret = () => { 35 export const generateSecret = () => {
20 const randomNumber = Math.floor(Math.random() * adjectives.length); 36 const randomNumber = Math.floor(Math.random() * adjectives.length);
21 return `${adjectives[randomNumber]} ${nouns[randomNumber]}`; 37 return `${adjectives[randomNumber]} ${nouns[randomNumber]}`;
22 }; 38 };
23 39
24 -const sendEmail = (email) => { 40 +export const generateToken = (id) => jwt.sign({ id }, process.env.JWT_SECRET);
25 - const options = {
26 - auth: {
27 - api_user: process.env.SENDGRID_USERNAME,
28 - api_password: process.env.SENDGRID_PASSWORD,
29 - },
30 - };
31 - const client = nodemailer.createTransport(sgTransport(options));
32 - return client.sendMail(email);
33 -};
34 41
35 -export const sendSecretMail = (address, emailSecret, value) => { 42 +export const createEMessage = (userEmail, emailSecret) => {
36 - const email = { 43 + const emailMessage = {
37 - from: "vel1024@khu.ac.kr", 44 + to: `${userEmail}`,
38 - to: address, 45 + from: "KhuChat@KhuChat.com",
39 - subject: `Authentication key for forgotten ${value}`, 46 + subject: "Email from KhuChat",
40 - html: `Hello, This is khuchat, authentication key is <b>${emailSecret}</b>, copy and paste it, Thanks.`, 47 + html: `We send email for reset your password, enter this key : <b>${emailSecret}</b>`,
41 }; 48 };
42 - return sendEmail(email); 49 + return emailMessage;
43 }; 50 };
44 51
45 -export const generateToken = (id) => jwt.sign({ id }, process.env.JWT_SECRET); 52 +export const sendEmail = (userEmail, emailSecret) => {
53 + sgMail.send(createEMessage(userEmail, emailSecret)).then(
54 + () => {},
55 + (error) => {
56 + console.error(error);
57 + if (error.response) {
58 + console.error(error.response.body);
59 + }
60 + }
61 + );
62 +};
......
...@@ -1174,6 +1174,35 @@ ...@@ -1174,6 +1174,35 @@
1174 "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", 1174 "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz",
1175 "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" 1175 "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg=="
1176 }, 1176 },
1177 + "@fortawesome/fontawesome-common-types": {
1178 + "version": "0.2.28",
1179 + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.28.tgz",
1180 + "integrity": "sha512-gtis2/5yLdfI6n0ia0jH7NJs5i/Z/8M/ZbQL6jXQhCthEOe5Cr5NcQPhgTvFxNOtURE03/ZqUcEskdn2M+QaBg=="
1181 + },
1182 + "@fortawesome/fontawesome-svg-core": {
1183 + "version": "1.2.28",
1184 + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.28.tgz",
1185 + "integrity": "sha512-4LeaNHWvrneoU0i8b5RTOJHKx7E+y7jYejplR7uSVB34+mp3Veg7cbKk7NBCLiI4TyoWS1wh9ZdoyLJR8wSAdg==",
1186 + "requires": {
1187 + "@fortawesome/fontawesome-common-types": "^0.2.28"
1188 + }
1189 + },
1190 + "@fortawesome/free-solid-svg-icons": {
1191 + "version": "5.13.0",
1192 + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.13.0.tgz",
1193 + "integrity": "sha512-IHUgDJdomv6YtG4p3zl1B5wWf9ffinHIvebqQOmV3U+3SLw4fC+LUCCgwfETkbTtjy5/Qws2VoVf6z/ETQpFpg==",
1194 + "requires": {
1195 + "@fortawesome/fontawesome-common-types": "^0.2.28"
1196 + }
1197 + },
1198 + "@fortawesome/react-fontawesome": {
1199 + "version": "0.1.9",
1200 + "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.9.tgz",
1201 + "integrity": "sha512-49V3WNysLZU5fZ3sqSuys4nGRytsrxJktbv3vuaXkEoxv22C6T7TEG0TW6+nqVjMnkfCQd5xOnmJoZHMF78tOw==",
1202 + "requires": {
1203 + "prop-types": "^15.7.2"
1204 + }
1205 + },
1177 "@hapi/address": { 1206 "@hapi/address": {
1178 "version": "2.1.4", 1207 "version": "2.1.4",
1179 "resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz", 1208 "resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz",
......
...@@ -3,6 +3,9 @@ ...@@ -3,6 +3,9 @@
3 "version": "0.1.0", 3 "version": "0.1.0",
4 "private": true, 4 "private": true,
5 "dependencies": { 5 "dependencies": {
6 + "@fortawesome/fontawesome-svg-core": "^1.2.28",
7 + "@fortawesome/free-solid-svg-icons": "^5.13.0",
8 + "@fortawesome/react-fontawesome": "^0.1.9",
6 "@testing-library/jest-dom": "^4.2.4", 9 "@testing-library/jest-dom": "^4.2.4",
7 "@testing-library/react": "^9.3.2", 10 "@testing-library/react": "^9.3.2",
8 "@testing-library/user-event": "^7.1.2", 11 "@testing-library/user-event": "^7.1.2",
......
...@@ -7,6 +7,9 @@ const Header = Styled.div` ...@@ -7,6 +7,9 @@ const Header = Styled.div`
7 justify-content: center; 7 justify-content: center;
8 margin: 10px 0px; 8 margin: 10px 0px;
9 font-size: 25px; 9 font-size: 25px;
10 + font-family: 'Source Code Pro';
11 + font-weight: 200;
12 + font-size: 40px;
10 `; 13 `;
11 14
12 export default () => { 15 export default () => {
......
1 import React from "react"; 1 import React from "react";
2 import Styled from "styled-components"; 2 import Styled from "styled-components";
3 +//import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
4 +//import { faList, faAddressBook } from "@fortawesome/free-solid-svg-icons";
5 +import PropTypes from "prop-types";
3 6
4 -const MenuBox = Styled.div` 7 +const Container = Styled.div`
5 - width: 30%; 8 + width: 400px;
6 - display: flex; 9 + height: 100%;
7 - justify-content: center;
8 - align-items: center;
9 - margin-top: 10px;
10 - border-top: 1px solid black;
11 - border-right: 1px solid black;
12 `; 10 `;
13 11
14 -const MenuList = Styled.ol` 12 +const Switch = ({ checked = false, isChecked, onClick }) => (
15 -`; 13 + <Container checked={checked} onClick={onClick} isChecked={isChecked} />
16 - 14 +);
17 -const MenuItem = Styled.li`
18 -`;
19 15
20 -const Link = Styled.a` 16 +Switch.propTypes = {
21 -`; 17 + checked: PropTypes.bool,
18 + isChecked: PropTypes.func,
19 + onClick: PropTypes.func,
20 +};
22 21
23 export default () => { 22 export default () => {
24 - return ( 23 + return <Switch />;
25 - <MenuBox>
26 - <MenuList>
27 - <MenuItem>
28 - <Link href="">1. What is KHU Chat?</Link>
29 - </MenuItem>
30 - </MenuList>
31 - </MenuBox>
32 - );
33 }; 24 };
......
1 +import { useState } from "react";
2 +
3 +export default (args) => {
4 + const [value, setValue] = useState(args);
5 +
6 + const onClick = (e) => {};
7 +};
...@@ -2,6 +2,10 @@ import { createGlobalStyle } from "styled-components"; ...@@ -2,6 +2,10 @@ import { createGlobalStyle } from "styled-components";
2 import reset from "styled-reset"; 2 import reset from "styled-reset";
3 3
4 export default createGlobalStyle` 4 export default createGlobalStyle`
5 + @import url('https://fonts.googleapis.com/css2?family=Nanum+Brush+Script&display=swap');
6 + @import url('https://fonts.googleapis.com/css2?family=Do+Hyeon&family=Nanum+Gothic+Coding:wght@400;700&family=Nanum+Pen+Script&family=Sunflower:wght@300;500;700&display=swap');
7 + @import url('https://fonts.googleapis.com/css2?family=Source+Code+Pro:ital,wght@0,200;0,300;0,400;0,500;0,600;0,700;0,900;1,200;1,300;1,400;1,500;1,600;1,700;1,900&display=swap');
8 +
5 ${reset} 9 ${reset}
6 * { 10 * {
7 box-sizing: border-box; 11 box-sizing: border-box;
......
1 +{
2 + "name": "capstone",
3 + "version": "1.0.0",
4 + "lockfileVersion": 1,
5 + "requires": true,
6 + "dependencies": {
7 + "@prisma/cli": {
8 + "version": "2.0.0-beta.3",
9 + "resolved": "https://registry.npmjs.org/@prisma/cli/-/cli-2.0.0-beta.3.tgz",
10 + "integrity": "sha512-VZxeTLLMenhkAyaY4tvTqChcW6QtUkiVvZj7N6A2vi6HsM1Tnf0fIZsiH1h3e/LEnaaDGIUC8F4Gkh2SeyeyCg==",
11 + "dev": true
12 + }
13 + }
14 +}
...@@ -15,5 +15,8 @@ ...@@ -15,5 +15,8 @@
15 "name": "sdy, kms", 15 "name": "sdy, kms",
16 "email": "vel1024@khu.ac.kr" 16 "email": "vel1024@khu.ac.kr"
17 }, 17 },
18 - "license": "ISC" 18 + "license": "ISC",
19 + "devDependencies": {
20 + "@prisma/cli": "^2.0.0-beta.3"
21 + }
19 } 22 }
......