minsung

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

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