From 4ef6000a986f70b78ebceae673123bbee1f73ab8 Mon Sep 17 00:00:00 2001 From: "Sunghum Paik (Brian)" Date: Mon, 15 Jan 2024 15:29:42 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=9D=BC=EB=B6=80=20=EA=B8=B0=EB=8A=A5?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 42 ++++++++++++++++++++++++++++++ package.json | 2 ++ src/app.module.ts | 1 - src/common/configs/multerConfig.ts | 16 ++++++++++++ src/main.ts | 1 + src/metadata.ts | 2 +- 6 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 src/common/configs/multerConfig.ts diff --git a/package-lock.json b/package-lock.json index 12a3636..dbf9750 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ "@nestjs/jwt": "10.1.0", "@nestjs/passport": "10.0.0", "@nestjs/platform-express": "10.1.0", + "@nestjs/serve-static": "^4.0.0", "@nestjs/swagger": "7.1.2", "@prisma/client": "^5.0.0", "bcrypt": "5.1.0", @@ -44,6 +45,7 @@ "@types/chance": "1.1.3", "@types/express": "4.17.17", "@types/jest": "^29.5.3", + "@types/multer": "^1.4.11", "@types/node": "^20.3.1", "@types/supertest": "2.0.12", "@typescript-eslint/eslint-plugin": "^5.59.11", @@ -2162,6 +2164,37 @@ "yarn": ">= 1.13.0" } }, + "node_modules/@nestjs/serve-static": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@nestjs/serve-static/-/serve-static-4.0.0.tgz", + "integrity": "sha512-8cTrNV2ngdHIjiLNsXePnw0+KY1ThrZGz/WeyAG5gIvmZNDbnZBOrPoYlKL+MOzlXlQStxR5jKLYmn+nJeoncQ==", + "dependencies": { + "path-to-regexp": "0.2.5" + }, + "peerDependencies": { + "@fastify/static": "^6.5.0", + "@nestjs/common": "^9.0.0 || ^10.0.0", + "@nestjs/core": "^9.0.0 || ^10.0.0", + "express": "^4.18.1", + "fastify": "^4.7.0" + }, + "peerDependenciesMeta": { + "@fastify/static": { + "optional": true + }, + "express": { + "optional": true + }, + "fastify": { + "optional": true + } + } + }, + "node_modules/@nestjs/serve-static/node_modules/path-to-regexp": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.2.5.tgz", + "integrity": "sha512-l6qtdDPIkmAmzEO6egquYDfqQGPMRNGjYtrU13HAXb3YSRrt7HSb1sJY0pKp6o2bAa86tSB6iwaW2JbthPKr7Q==" + }, "node_modules/@nestjs/swagger": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-7.1.2.tgz", @@ -3422,6 +3455,15 @@ "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==", "dev": true }, + "node_modules/@types/multer": { + "version": "1.4.11", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.11.tgz", + "integrity": "sha512-svK240gr6LVWvv3YGyhLlA+6LRRWA4mnGIU7RcNmgjBYFl6665wcXrRfxGp5tEPVHUNm5FMcmq7too9bxCwX/w==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, "node_modules/@types/node": { "version": "20.4.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.2.tgz", diff --git a/package.json b/package.json index 4fdbc73..5e2d7a2 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "@nestjs/jwt": "10.1.0", "@nestjs/passport": "10.0.0", "@nestjs/platform-express": "10.1.0", + "@nestjs/serve-static": "^4.0.0", "@nestjs/swagger": "7.1.2", "@prisma/client": "^5.0.0", "bcrypt": "5.1.0", @@ -85,6 +86,7 @@ "@types/chance": "1.1.3", "@types/express": "4.17.17", "@types/jest": "^29.5.3", + "@types/multer": "^1.4.11", "@types/node": "^20.3.1", "@types/supertest": "2.0.12", "@typescript-eslint/eslint-plugin": "^5.59.11", diff --git a/src/app.module.ts b/src/app.module.ts index 4248e48..281df8b 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -8,7 +8,6 @@ import { AuthModule } from './auth/auth.module'; import { UsersModule } from './users/users.module'; import { PostsModule } from './posts/posts.module'; import config from './common/configs/config'; - @Module({ imports: [ ConfigModule.forRoot({ isGlobal: true, load: [config] }), diff --git a/src/common/configs/multerConfig.ts b/src/common/configs/multerConfig.ts new file mode 100644 index 0000000..1db7e07 --- /dev/null +++ b/src/common/configs/multerConfig.ts @@ -0,0 +1,16 @@ +import { diskStorage } from 'multer'; +import { extname } from 'path'; + +// multer 설정 +export const multerConfig = { + storage: diskStorage({ + destination: './uploads', // 저장 위치 + filename: (req, file, cb) => { + const randomName = Array(32) + .fill(null) + .map(() => Math.round(Math.random() * 16).toString(16)) + .join(''); + cb(null, `${randomName}${extname(file.originalname)}`); // 파일명 + }, + }), +}; diff --git a/src/main.ts b/src/main.ts index df93ffb..6b08886 100644 --- a/src/main.ts +++ b/src/main.ts @@ -45,6 +45,7 @@ async function bootstrap() { .build(); const document = SwaggerModule.createDocument(app, options); + // vercel 배포 시 스웨거 css 강제 인풋설정 SwaggerModule.setup(swaggerConfig.path || 'api', app, document, { customSiteTitle: 'Pinemarket API', customfavIcon: diff --git a/src/metadata.ts b/src/metadata.ts index 0a5c2a0..242a989 100644 --- a/src/metadata.ts +++ b/src/metadata.ts @@ -5,5 +5,5 @@ export default async () => { ["./users/dto/response-user-dto"]: await import("./users/dto/response-user-dto"), ["./users/models/user.model"]: await import("./users/models/user.model") }; - return { "@nestjs/swagger/plugin": { "models": [[import("./posts/dto/pagenation-query.dto"), { "PaginationQueryDto": { page: { required: false, type: () => Number, default: 1, minimum: 1 }, limit: { required: false, type: () => Number, default: 10, minimum: 1 }, query: { required: false, type: () => String }, orderBy: { required: false, type: () => String }, direction: { required: false, type: () => Object, default: "asc" } } }]], "controllers": [[import("./app.controller"), { "AppController": { "getHello": { type: String }, "getHelloName": { type: String } } }], [import("./auth/auth.controller"), { "AuthController": { "signup": {}, "login": {}, "refreshToken": { type: t["./auth/models/token.model"].Token }, "getUser": { type: Object } } }], [import("./users/users.controller"), { "UserController": { "getProfile": { type: t["./users/dto/response-user-dto"].UserResponseDto }, "updateUser": { type: Object }, "changePassword": { type: Object } } }], [import("./posts/posts.controller"), { "PostsController": { "createPost": { type: Object }, "getPublishedPosts": {}, "getUserPosts": { type: [Object] }, "getPost": { type: Object } } }]] }, "@nestjs/graphql/plugin": { "models": [[import("./auth/dto/signup.input"), { "SignupInput": { email: {}, password: {}, firstname: { nullable: true }, lastname: { nullable: true } } }], [import("./auth/models/token.model"), { "Token": { accessToken: {}, refreshToken: {} } }], [import("./common/models/base.model"), { "BaseModel": { id: {}, createdAt: {}, updatedAt: {} } }], [import("./posts/models/post.model"), { "Post": { title: {}, content: { nullable: true }, published: {}, author: { nullable: true } } }], [import("./users/models/user.model"), { "User": { email: {}, firstname: { nullable: true }, lastname: { nullable: true }, role: {}, posts: { nullable: true } } }], [import("./auth/models/auth.model"), { "Auth": { user: { type: () => t["./users/models/user.model"].User } } }], [import("./auth/dto/login.input"), { "LoginInput": { email: {}, password: {} } }], [import("./auth/dto/refresh-token.input"), { "RefreshTokenInput": { token: {} } }], [import("./users/dto/change-password.input"), { "ChangePasswordInput": { oldPassword: {}, newPassword: {} } }], [import("./users/dto/update-user.input"), { "UpdateUserInput": { firstname: { nullable: true }, lastname: { nullable: true } } }], [import("./posts/dto/createPost.input"), { "CreatePostInput": { content: {}, title: {} } }], [import("./common/pagination/pagination.args"), { "PaginationArgs": { skip: { nullable: true, type: () => Number }, after: { nullable: true, type: () => String }, before: { nullable: true, type: () => String }, first: { nullable: true, type: () => Number }, last: { nullable: true, type: () => Number } } }], [import("./posts/args/post-id.args"), { "PostIdArgs": { postId: { type: () => String } } }], [import("./posts/args/user-id.args"), { "UserIdArgs": { userId: { type: () => String } } }], [import("./common/pagination/page-info.model"), { "PageInfo": { endCursor: { nullable: true }, hasNextPage: {}, hasPreviousPage: {}, startCursor: { nullable: true } } }], [import("./posts/models/post-connection.model"), { "PostConnection": {} }], [import("./posts/dto/post-order.input"), { "PostOrder": { field: {} } }]] } }; + return { "@nestjs/swagger/plugin": { "models": [[import("./posts/dto/pagenation-query.dto"), { "PaginationQueryDto": { page: { required: false, type: () => Number, default: 1, minimum: 1 }, limit: { required: false, type: () => Number, default: 10, minimum: 1 }, query: { required: false, type: () => String }, orderBy: { required: false, type: () => String }, direction: { required: false, type: () => Object, default: "asc" } } }]], "controllers": [[import("./app.controller"), { "AppController": { "getHello": { type: String }, "getHelloName": { type: String } } }], [import("./auth/auth.controller"), { "AuthController": { "signup": {}, "login": {}, "refreshToken": { type: t["./auth/models/token.model"].Token }, "getUser": { type: Object } } }], [import("./users/users.controller"), { "UserController": { "getProfile": { type: t["./users/dto/response-user-dto"].UserResponseDto }, "updateUser": { type: Object }, "changePassword": { type: Object } } }], [import("./posts/posts.controller"), { "PostsController": { "createPost": { type: Object }, "getPublishedPosts": {}, "getUserPosts": { type: [Object] }, "getPost": { type: Object } } }]] }, "@nestjs/graphql/plugin": { "models": [[import("./auth/dto/signup.input"), { "SignupInput": { email: {}, password: {}, firstname: { nullable: true }, lastname: { nullable: true } } }], [import("./auth/models/token.model"), { "Token": { accessToken: {}, refreshToken: {} } }], [import("./common/models/base.model"), { "BaseModel": { id: {}, createdAt: {}, updatedAt: {} } }], [import("./posts/models/post.model"), { "Post": { title: {}, content: { nullable: true }, published: {}, imgUrl: { nullable: true }, author: { nullable: true } } }], [import("./users/models/user.model"), { "User": { email: {}, username: { nullable: true }, role: {}, posts: { nullable: true } } }], [import("./auth/models/auth.model"), { "Auth": { user: { type: () => t["./users/models/user.model"].User } } }], [import("./auth/dto/login.input"), { "LoginInput": { email: {}, password: {} } }], [import("./auth/dto/refresh-token.input"), { "RefreshTokenInput": { token: {} } }], [import("./users/dto/change-password.input"), { "ChangePasswordInput": { oldPassword: {}, newPassword: {} } }], [import("./users/dto/update-user.input"), { "UpdateUserInput": { username: { nullable: true } } }], [import("./posts/dto/createPost.input"), { "CreatePostInput": { content: {}, title: {}, imgUrl: {} } }], [import("./common/pagination/pagination.args"), { "PaginationArgs": { skip: { nullable: true, type: () => Number }, after: { nullable: true, type: () => String }, before: { nullable: true, type: () => String }, first: { nullable: true, type: () => Number }, last: { nullable: true, type: () => Number } } }], [import("./posts/args/post-id.args"), { "PostIdArgs": { postId: { type: () => String } } }], [import("./posts/args/user-id.args"), { "UserIdArgs": { userId: { type: () => String } } }], [import("./common/pagination/page-info.model"), { "PageInfo": { endCursor: { nullable: true }, hasNextPage: {}, hasPreviousPage: {}, startCursor: { nullable: true } } }], [import("./posts/models/post-connection.model"), { "PostConnection": {} }], [import("./posts/dto/post-order.input"), { "PostOrder": { field: {} } }]] } }; }; \ No newline at end of file