diff --git a/package-lock.json b/package-lock.json index a904fce..ba025ae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,7 @@ "cross-env": "^7.0.3", "dotenv": "^16.4.7", "express": "^4.21.1", + "jsonwebtoken": "^9.0.2", "multer": "^1.4.5-lts.1", "mysql2": "^3.11.4", "nodemailer": "^6.9.16", @@ -39,6 +40,7 @@ "@types/bcrypt": "^5.0.2", "@types/express": "^5.0.0", "@types/jest": "^29.5.14", + "@types/jsonwebtoken": "^9.0.7", "@types/multer": "^1.4.12", "@types/node": "^22.9.0", "@types/nodemailer": "^6.4.17", @@ -2464,6 +2466,15 @@ "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0" } }, + "node_modules/@nestjs/jwt/node_modules/@types/jsonwebtoken": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.5.tgz", + "integrity": "sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@nestjs/mapped-types": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.0.6.tgz", @@ -3085,9 +3096,10 @@ "license": "MIT" }, "node_modules/@types/jsonwebtoken": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.5.tgz", - "integrity": "sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA==", + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.7.tgz", + "integrity": "sha512-ugo316mmTYBl2g81zDFnZ7cfxlut3o+/EQdaP7J8QN2kY6lJ22hmQYCK5EHcJHbrW+dkCGSCPgbG8JtYj6qSrg==", + "dev": true, "license": "MIT", "dependencies": { "@types/node": "*" diff --git a/package.json b/package.json index 7788dec..83cb1df 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "cross-env": "^7.0.3", "dotenv": "^16.4.7", "express": "^4.21.1", + "jsonwebtoken": "^9.0.2", "multer": "^1.4.5-lts.1", "mysql2": "^3.11.4", "nodemailer": "^6.9.16", @@ -48,6 +49,7 @@ "@types/bcrypt": "^5.0.2", "@types/express": "^5.0.0", "@types/jest": "^29.5.14", + "@types/jsonwebtoken": "^9.0.7", "@types/multer": "^1.4.12", "@types/node": "^22.9.0", "@types/nodemailer": "^6.4.17", diff --git a/src/database/factories/category.factory.ts b/src/database/factories/category.factory.ts new file mode 100644 index 0000000..d5ae2d7 --- /dev/null +++ b/src/database/factories/category.factory.ts @@ -0,0 +1,9 @@ +import { setSeederFactory } from "typeorm-extension"; + +import { CategoryEntity } from "@APP/entities/category.entity"; + +export default setSeederFactory(CategoryEntity, async () => { + const category = new CategoryEntity(); + + return category; +}); diff --git a/src/database/factories/member.factory.ts b/src/database/factories/member.factory.ts new file mode 100644 index 0000000..9ec95bd --- /dev/null +++ b/src/database/factories/member.factory.ts @@ -0,0 +1,18 @@ +import * as bcrypt from "bcrypt"; +import { setSeederFactory } from "typeorm-extension"; + +import { MemberEntity } from "../../entities/member.entity"; + +export default setSeederFactory(MemberEntity, async (faker) => { + const hashedPassword = await bcrypt.hash("123456", 10); + + const member = new MemberEntity(); + member.name = faker.person.firstName(); + member.nickname = faker.person.lastName(); + member.email = faker.internet.email(); + member.password = hashedPassword; + member.isEmailVerified = true; + member.profileImage = Math.random() > 0.5 ? faker.image.avatar() : null; + + return member; +}); diff --git a/src/database/factories/refresh-token.factory.ts b/src/database/factories/refresh-token.factory.ts new file mode 100644 index 0000000..3ef48f9 --- /dev/null +++ b/src/database/factories/refresh-token.factory.ts @@ -0,0 +1,24 @@ +import * as bcrypt from "bcrypt"; +import jwt from "jsonwebtoken"; +import { setSeederFactory } from "typeorm-extension"; + +import { ENV_JWT_SECRET_KEY } from "@APP/common/constants/env-keys.const"; +import { RefreshTokenEntity } from "@APP/entities/refresh-token.entity"; + +export default setSeederFactory(RefreshTokenEntity, async () => { + const payload = { + email: "Alisha_Dibbert@hotmail.com", + sub: 8, + type: "refresh", + }; + + const secret = process.env[ENV_JWT_SECRET_KEY] || "secret"; + const expiresIn = "3600"; + + const refreshToken = new RefreshTokenEntity(); + + const token = jwt.sign(payload, secret, { expiresIn }); + refreshToken.token = await bcrypt.hash(token, 10); + + return refreshToken; +}); diff --git a/src/database/factories/verification-code.factory.ts b/src/database/factories/verification-code.factory.ts new file mode 100644 index 0000000..c74ce6a --- /dev/null +++ b/src/database/factories/verification-code.factory.ts @@ -0,0 +1,16 @@ +import * as crypto from "crypto"; +import { setSeederFactory } from "typeorm-extension"; + +import { VerificationCodeEntity } from "@APP/entities/verification-code.entity"; + +export default setSeederFactory(VerificationCodeEntity, () => { + const verificationCode = crypto + .randomBytes(3) + .toString("hex") + .toUpperCase(); + + const verificationCodeEntity = new VerificationCodeEntity(); + verificationCodeEntity.code = verificationCode; + + return verificationCodeEntity; +}); diff --git a/src/database/seeds/category.seeder.ts b/src/database/seeds/category.seeder.ts new file mode 100644 index 0000000..f86aacc --- /dev/null +++ b/src/database/seeds/category.seeder.ts @@ -0,0 +1,38 @@ +import { DataSource } from "typeorm"; +import { Seeder, SeederFactoryManager } from "typeorm-extension"; + +import { CategoryEntity } from "@APP/entities/category.entity"; + +const CATEGORY_NAMES = [ + "치킨", + "고기", + "한식", + "중식", + "일식", + "양식", + "족발,보쌈", + "찜,탕,찌개", + "피자", + "아시안", + "도시락", + "분식", + "카페,디저트", + "패스트푸드", + "야식", + "기타", +]; + +export default class CategorySeeder implements Seeder { + public async run( + _dataSource: DataSource, + factoryManager: SeederFactoryManager, + ): Promise { + const categoryFactory = factoryManager.get(CategoryEntity); + + await Promise.all( + CATEGORY_NAMES.map((name) => categoryFactory.save({ name })), + ); + + console.log(`카테고리 생성 완료`); + } +} diff --git a/src/database/seeds/refresh-token.seeder.ts b/src/database/seeds/refresh-token.seeder.ts new file mode 100644 index 0000000..2848a29 --- /dev/null +++ b/src/database/seeds/refresh-token.seeder.ts @@ -0,0 +1,17 @@ +import { DataSource } from "typeorm"; +import { Seeder, SeederFactoryManager } from "typeorm-extension"; + +import { RefreshTokenEntity } from "@APP/entities/refresh-token.entity"; + +export default class RefreshTokenSeeder implements Seeder { + public async run( + _dataSource: DataSource, + factoryManager: SeederFactoryManager, + ): Promise { + const refreshTokenFactory = factoryManager.get(RefreshTokenEntity); + + await refreshTokenFactory.save(); + + console.log(`리프레시토큰 생성 완료`); + } +} diff --git a/src/database/seeds/verification-code.seeder.ts b/src/database/seeds/verification-code.seeder.ts new file mode 100644 index 0000000..b3e2e29 --- /dev/null +++ b/src/database/seeds/verification-code.seeder.ts @@ -0,0 +1,19 @@ +import { DataSource } from "typeorm"; +import { Seeder, SeederFactoryManager } from "typeorm-extension"; + +import { VerificationCodeEntity } from "@APP/entities/verification-code.entity"; + +export default class VerificationCodeSeeder implements Seeder { + public async run( + _dataSource: DataSource, + factoryManager: SeederFactoryManager, + ): Promise { + const verificationCodeFactory = factoryManager.get( + VerificationCodeEntity, + ); + + await verificationCodeFactory.save(); + + console.log(`인증코드 생성 완료`); + } +}