diff --git a/.vscode/snippets.code-snippets b/.vscode/snippets.code-snippets new file mode 100644 index 0000000..97f282e --- /dev/null +++ b/.vscode/snippets.code-snippets @@ -0,0 +1,27 @@ +{ + "RequestDTO": { + "prefix": "ReqDTO", + "body": [ + "import { BaseRequestDTO } from '@BaseRequestDTO';", + "", + "export class ${1:DtoName}RequestDTO extends BaseRequestDTO {", + "$0", + "}" + ] + }, + "RequestField": { + "prefix": "ReqField", + "body": [ + "@${1:Decorator}", + "${2:field}!: ${3:type};" + ] + }, + "ResponseDTO": { + "prefix": "ResDTO", + "body": [ + "import type { BaseResponseDTO } from '@BaseResponseDTO';", + "", + "export type ${1:DtoName}ResponseDTO = BaseResponseDTO<${2:data}>;" + ] + } +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 93293b0..79ded71 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,9 @@ "version": "1.0.9", "license": "ISC", "dependencies": { + "@swc/core": "^1.15.5", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.3", "date-fns": "^4.1.0", "reflect-metadata": "^0.2.2", "tsup": "^8.5.1" @@ -755,6 +758,220 @@ "win32" ] }, + "node_modules/@swc/core": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.5.tgz", + "integrity": "sha512-VRy+AEO0zqUkwV9uOgqXtdI5tNj3y3BZI+9u28fHNjNVTtWYVNIq3uYhoGgdBOv7gdzXlqfHKuxH5a9IFAvopQ==", + "hasInstallScript": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@swc/counter": "^0.1.3", + "@swc/types": "^0.1.25" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.15.5", + "@swc/core-darwin-x64": "1.15.5", + "@swc/core-linux-arm-gnueabihf": "1.15.5", + "@swc/core-linux-arm64-gnu": "1.15.5", + "@swc/core-linux-arm64-musl": "1.15.5", + "@swc/core-linux-x64-gnu": "1.15.5", + "@swc/core-linux-x64-musl": "1.15.5", + "@swc/core-win32-arm64-msvc": "1.15.5", + "@swc/core-win32-ia32-msvc": "1.15.5", + "@swc/core-win32-x64-msvc": "1.15.5" + }, + "peerDependencies": { + "@swc/helpers": ">=0.5.17" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.5.tgz", + "integrity": "sha512-RvdpUcXrIz12yONzOdQrJbEnq23cOc2IHOU1eB8kPxPNNInlm4YTzZEA3zf3PusNpZZLxwArPVLCg0QsFQoTYw==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-x64": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.15.5.tgz", + "integrity": "sha512-ufJnz3UAff/8G5OfqZZc5cTQfGtXyXVLTB8TGT0xjkvEbfFg8jZUMDBnZT/Cn0k214JhMjiLCNl0A8aY/OKsYQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.5.tgz", + "integrity": "sha512-Yqu92wIT0FZKLDWes+69kBykX97hc8KmnyFwNZGXJlbKUGIE0hAIhbuBbcY64FGSwey4aDWsZ7Ojk89KUu9Kzw==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.5.tgz", + "integrity": "sha512-3gR3b5V1abe/K1GpD0vVyZgqgV+ykuB5QNecDYzVroX4QuN+amCzQaNSsVM8Aj6DbShQCBTh3hGHd2f3vZ8gCw==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.5.tgz", + "integrity": "sha512-Of+wmVh5h47tTpN9ghHVjfL0CJrgn99XmaJjmzWFW7agPdVY6gTDgkk6zQ6q4hcDQ7hXb0BGw6YFpuanBzNPow==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.5.tgz", + "integrity": "sha512-98kuPS0lZVgjmc/2uTm39r1/OfwKM0PM13ZllOAWi5avJVjRd/j1xA9rKeUzHDWt+ocH9mTCQsAT1jjKSq45bg==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.5.tgz", + "integrity": "sha512-Rk+OtNQP3W/dZExL74LlaakXAQn6/vbrgatmjFqJPO4RZkq+nLo5g7eDUVjyojuERh7R2yhqNvZ/ZZQe8JQqqA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.5.tgz", + "integrity": "sha512-e3RTdJ769+PrN25iCAlxmsljEVu6iIWS7sE21zmlSiipftBQvSAOWuCDv2A8cH9lm5pSbZtwk8AUpIYCNsj2oQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.5.tgz", + "integrity": "sha512-NmOdl6kyAw6zMz36zCdopTgaK2tcLA53NhUsTRopBc/796Fp87XdsslRHglybQ1HyXIGOQOKv2Y14IUbeci4BA==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.5.tgz", + "integrity": "sha512-EPXJRf0A8eOi8woXf/qgVIWRl9yeSl0oN1ykGZNCGI7oElsfxUobJFmpJFJoVqKFfd1l0c+GPmWsN2xavTFkNw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "license": "Apache-2.0" + }, + "node_modules/@swc/types": { + "version": "0.1.25", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.25.tgz", + "integrity": "sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==", + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3" + } + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -771,6 +988,12 @@ "undici-types": "~7.16.0" } }, + "node_modules/@types/validator": { + "version": "13.15.10", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.10.tgz", + "integrity": "sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==", + "license": "MIT" + }, "node_modules/acorn": { "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", @@ -828,6 +1051,23 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/class-transformer": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", + "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==", + "license": "MIT" + }, + "node_modules/class-validator": { + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.3.tgz", + "integrity": "sha512-rXXekcjofVN1LTOSw+u4u9WXVEUvNBVjORW154q/IdmYWy1nMbOU9aNtZB0t8m+FJQ9q91jlr2f9CwwUFdFMRA==", + "license": "MIT", + "dependencies": { + "@types/validator": "^13.15.3", + "libphonenumber-js": "^1.11.1", + "validator": "^13.15.20" + } + }, "node_modules/commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -972,6 +1212,12 @@ "node": ">=10" } }, + "node_modules/libphonenumber-js": { + "version": "1.12.31", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.31.tgz", + "integrity": "sha512-Z3IhgVgrqO1S5xPYM3K5XwbkDasU67/Vys4heW+lfSBALcUZjeIIzI8zCLifY+OCzSq+fpDdywMDa7z+4srJPQ==", + "license": "MIT" + }, "node_modules/lilconfig": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", @@ -1370,6 +1616,15 @@ "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", "dev": true, "license": "MIT" + }, + "node_modules/validator": { + "version": "13.15.23", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.23.tgz", + "integrity": "sha512-4yoz1kEWqUjzi5zsPbAS/903QXSYp0UOtHsPpp7p9rHAw/W+dkInskAE386Fat3oKRROwO98d9ZB0G4cObgUyw==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } } } } diff --git a/package.json b/package.json index fe9ff83..a60fd59 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,8 @@ "types": "dist/index.d.ts", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", - "build": "tsup src/index.ts --dts --format cjs,esm" + "build": "tsup src/index.ts --dts --format cjs,esm", + "gen:dto": "node scripts/generate-dto.js" }, "repository": { "type": "git", @@ -27,6 +28,9 @@ "typescript": "^5.9.3" }, "dependencies": { + "@swc/core": "^1.15.5", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.3", "date-fns": "^4.1.0", "reflect-metadata": "^0.2.2", "tsup": "^8.5.1" diff --git a/scripts/generate-dto.js b/scripts/generate-dto.js new file mode 100644 index 0000000..8620cc1 --- /dev/null +++ b/scripts/generate-dto.js @@ -0,0 +1,39 @@ +const fs = require('fs'); +const path = require('path'); + +const targetProject = process.argv[2]; +if (!targetProject) { + console.error('targetProject required'); + process.exit(1); +} + +const targetApi = process.argv[3]; +if (!targetApi) { + console.error('targetApi required'); + process.exit(1); +} + +const name = process.argv[4]; +if (!name) { + console.error('name required'); + process.exit(1); +} + +const nameKebab = name + .replace(/([a-z0-9])([A-Z])/g, '$1-$2') + .replace(/([A-Z]+)([A-Z][a-z])/g, '$1-$2') + .toLowerCase(); + +const baseDir = path.resolve('src', targetProject, 'http/dto', targetApi, name); + +fs.mkdirSync(baseDir, {recursive: true}); + +fs.writeFileSync( + path.join(baseDir, `${nameKebab}-request.dto.ts`), + '' +); + +fs.writeFileSync( + path.join(baseDir, `${nameKebab}-response.dto.ts`), + '' +); \ No newline at end of file diff --git a/src/common/format/PasswordFormat.ts b/src/common/format/PasswordFormat.ts new file mode 100644 index 0000000..b4982fe --- /dev/null +++ b/src/common/format/PasswordFormat.ts @@ -0,0 +1,2 @@ +export const PasswordFormat = /^[a-z](?=.*[0-9])(?=.*[!@#$%^])[a-zA-z0-9!@#$%^]{8,12}$/; +export const PasswordFormatString = /^[a-z](?=.*[0-9])(?=.*[!@#$%^])[a-zA-z0-9!@#$%^]{8,12}$/.source; \ No newline at end of file diff --git a/src/common/validator/CustomValidators.ts b/src/common/validator/CustomValidators.ts new file mode 100644 index 0000000..dbf92ee --- /dev/null +++ b/src/common/validator/CustomValidators.ts @@ -0,0 +1,36 @@ +import { ValidatorConstraint, type ValidationArguments, type ValidatorConstraintInterface } from "class-validator"; + +@ValidatorConstraint({ name: 'accountCheckDuplicationValueValidator', async: false }) +export class AccountCheckDuplicationValueValidator implements ValidatorConstraintInterface { + validate(value: any, args: ValidationArguments) { + const { type } = args.object as any; + + if (type === 'email') { + return typeof value === 'string' && /\S+@\S+\.\S+/.test(value); + } + + if (type === 'accountId') { + return typeof value === 'string'; + } + + return false; + } +} + +@ValidatorConstraint({ name: 'loginIdValidator', async: false }) +export class LoginIdValidator implements ValidatorConstraintInterface { + validate(value: any, args: ValidationArguments) { + const { type } = args.object as any; + + if (type === 'email') { + return typeof value === 'string' && /\S+@\S+\.\S+/.test(value); + } + + if (type === 'accountId') { + return typeof value === 'string'; + } + + return false; + } +} + diff --git a/src/index.ts b/src/index.ts index 9273030..c6588d1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,4 +9,6 @@ export * from './common/format/DateTimeFormat'; export * from './scheduler/http/HttpResponseMessage'; -export * from './scheduler/http/HttpApiUrl'; \ No newline at end of file +export * from './scheduler/http/HttpApiUrl'; + +export * as DTO from './scheduler/http/dto'; \ No newline at end of file diff --git a/src/scheduler/http/HttpApiUrl.ts b/src/scheduler/http/HttpApiUrl.ts index bf97d03..afd1b18 100644 --- a/src/scheduler/http/HttpApiUrl.ts +++ b/src/scheduler/http/HttpApiUrl.ts @@ -39,4 +39,4 @@ export const HttpApiUrl = { read: '/read', delete: '/delete' } -} \ No newline at end of file +} as const; \ No newline at end of file diff --git a/src/scheduler/http/dto/account/checkDuplication/check-duplication-request.dto.ts b/src/scheduler/http/dto/account/checkDuplication/check-duplication-request.dto.ts new file mode 100644 index 0000000..c5b9239 --- /dev/null +++ b/src/scheduler/http/dto/account/checkDuplication/check-duplication-request.dto.ts @@ -0,0 +1,11 @@ +import { IsIn, Validate } from 'class-validator'; +import { BaseRequestDTO } from '@BaseRequestDTO'; +import { AccountCheckDuplicationValueValidator } from 'src/common/validator/CustomValidators'; + +export class CheckDuplicationRequestDTO extends BaseRequestDTO { + @IsIn(['email', 'accountId']) + type!: 'email' | 'accountId'; + + @Validate(AccountCheckDuplicationValueValidator) + value!: string; +} \ No newline at end of file diff --git a/src/scheduler/http/dto/account/checkDuplication/check-duplication-response.dto.ts b/src/scheduler/http/dto/account/checkDuplication/check-duplication-response.dto.ts new file mode 100644 index 0000000..1e38257 --- /dev/null +++ b/src/scheduler/http/dto/account/checkDuplication/check-duplication-response.dto.ts @@ -0,0 +1,3 @@ +import type { BaseResponseDTO } from '@BaseResponseDTO'; + +export type CheckDuplicationResponseDTO = BaseResponseDTO<{ isDuplicated: boolean }>; \ No newline at end of file diff --git a/src/scheduler/http/dto/account/login/login-request.dto.ts b/src/scheduler/http/dto/account/login/login-request.dto.ts new file mode 100644 index 0000000..e50cddc --- /dev/null +++ b/src/scheduler/http/dto/account/login/login-request.dto.ts @@ -0,0 +1,16 @@ +import { IsIn, IsString, Matches, Validate } from "class-validator"; +import { BaseRequestDTO } from "../../base/base-request.dto"; +import { LoginIdValidator } from "src/common/validator/CustomValidators"; +import { PasswordFormat } from "src/common/format/PasswordFormat"; + +export class LoginRequestDTO extends BaseRequestDTO { + @IsIn(['email', 'accountId']) + type!: 'email' | 'accountId'; + + @Validate(LoginIdValidator) + id!: string; + + @IsString() + @Matches(PasswordFormat) + password!: string; +} \ No newline at end of file diff --git a/src/scheduler/http/dto/account/login/login-response.dto.ts b/src/scheduler/http/dto/account/login/login-response.dto.ts new file mode 100644 index 0000000..8124f56 --- /dev/null +++ b/src/scheduler/http/dto/account/login/login-response.dto.ts @@ -0,0 +1,8 @@ +import type { BaseResponseDTO } from '@BaseResponseDTO'; + +export type LoginResponseDTO = BaseResponseDTO< + { + accessToken: string; + refreshToken: string; + } +>; \ No newline at end of file diff --git a/src/scheduler/http/dto/account/refreshAccessToken/refresh-access-token-response.dto.ts b/src/scheduler/http/dto/account/refreshAccessToken/refresh-access-token-response.dto.ts new file mode 100644 index 0000000..e9250a0 --- /dev/null +++ b/src/scheduler/http/dto/account/refreshAccessToken/refresh-access-token-response.dto.ts @@ -0,0 +1,8 @@ +import type { BaseResponseDTO } from '@BaseResponseDTO'; + +export type RefreshAccessTokenResponseDTO = BaseResponseDTO< + { + accessToken: string; + refreshToken: string; + } +>; \ No newline at end of file diff --git a/src/scheduler/http/dto/account/resetPassword/reset-password-request.dto.ts b/src/scheduler/http/dto/account/resetPassword/reset-password-request.dto.ts new file mode 100644 index 0000000..ea0c2fc --- /dev/null +++ b/src/scheduler/http/dto/account/resetPassword/reset-password-request.dto.ts @@ -0,0 +1,12 @@ +import { IsEmail, IsString, Matches } from "class-validator"; +import { BaseRequestDTO } from "../../base/base-request.dto"; +import { PasswordFormat } from "src/common/format/PasswordFormat"; + +export class ResetPasswordRequestDTO extends BaseRequestDTO { + @IsEmail() + email!: string; + + @IsString() + @Matches(PasswordFormat) + password!: string; +} \ No newline at end of file diff --git a/src/scheduler/http/dto/account/resetPassword/reset-password-response.dto.ts b/src/scheduler/http/dto/account/resetPassword/reset-password-response.dto.ts new file mode 100644 index 0000000..ee77d4d --- /dev/null +++ b/src/scheduler/http/dto/account/resetPassword/reset-password-response.dto.ts @@ -0,0 +1,3 @@ +import type { BaseResponseDTO } from '@BaseResponseDTO'; + +export type ResetPasswordResponseDTO = BaseResponseDTO<{ }>; \ No newline at end of file diff --git a/src/scheduler/http/dto/account/sendEmailVerificationCode/send-email-verification-code-request.dto.ts b/src/scheduler/http/dto/account/sendEmailVerificationCode/send-email-verification-code-request.dto.ts new file mode 100644 index 0000000..c33d00b --- /dev/null +++ b/src/scheduler/http/dto/account/sendEmailVerificationCode/send-email-verification-code-request.dto.ts @@ -0,0 +1,7 @@ +import { IsEmail } from "class-validator"; +import { BaseRequestDTO } from "../../base/base-request.dto"; + +export class SendEmailVerificationCodeRequestDTO extends BaseRequestDTO { + @IsEmail() + email!: string; +} \ No newline at end of file diff --git a/src/scheduler/http/dto/account/sendEmailVerificationCode/sned-email-verification-code-response.dto.ts b/src/scheduler/http/dto/account/sendEmailVerificationCode/sned-email-verification-code-response.dto.ts new file mode 100644 index 0000000..c24dc00 --- /dev/null +++ b/src/scheduler/http/dto/account/sendEmailVerificationCode/sned-email-verification-code-response.dto.ts @@ -0,0 +1,3 @@ +import type { BaseResponseDTO } from '@BaseResponseDTO'; + +export type SendEmailVerificationCodeResponseDTO = BaseResponseDTO<{ }>; \ No newline at end of file diff --git a/src/scheduler/http/dto/account/sendPasswordResetCode/send-password-reset-code-request.dto.ts b/src/scheduler/http/dto/account/sendPasswordResetCode/send-password-reset-code-request.dto.ts new file mode 100644 index 0000000..5bf0ed8 --- /dev/null +++ b/src/scheduler/http/dto/account/sendPasswordResetCode/send-password-reset-code-request.dto.ts @@ -0,0 +1,7 @@ +import { IsEmail } from "class-validator"; +import { BaseRequestDTO } from "../../base/base-request.dto"; + +export class SendPasswordResetCodeRequestDTO extends BaseRequestDTO { + @IsEmail() + email!: string; +} \ No newline at end of file diff --git a/src/scheduler/http/dto/account/sendPasswordResetCode/send-password-reset-code-response.dto.ts b/src/scheduler/http/dto/account/sendPasswordResetCode/send-password-reset-code-response.dto.ts new file mode 100644 index 0000000..4e41a1f --- /dev/null +++ b/src/scheduler/http/dto/account/sendPasswordResetCode/send-password-reset-code-response.dto.ts @@ -0,0 +1,3 @@ +import type { BaseResponseDTO } from '@BaseResponseDTO'; + +export type SendPasswordResetCodeResponseDTO = BaseResponseDTO<{ }>; \ No newline at end of file diff --git a/src/scheduler/http/dto/account/signup/signup-request.dto.ts b/src/scheduler/http/dto/account/signup/signup-request.dto.ts new file mode 100644 index 0000000..a4f9bac --- /dev/null +++ b/src/scheduler/http/dto/account/signup/signup-request.dto.ts @@ -0,0 +1,21 @@ +import { BaseRequestDTO } from '@BaseRequestDTO'; +import { IsEmail, IsString, Matches } from 'class-validator'; +import { PasswordFormat } from 'src/common/format/PasswordFormat'; + +export class SignupRequestDTO extends BaseRequestDTO { + @IsEmail() + email!: string; + + @IsString() + name!: string; + + @IsString() + nickname!: string; + + @IsString() + accoundId!: string; + + @IsString() + @Matches(PasswordFormat) + password!: string; +} \ No newline at end of file diff --git a/src/scheduler/http/dto/account/signup/signup-response.dto.ts b/src/scheduler/http/dto/account/signup/signup-response.dto.ts new file mode 100644 index 0000000..ebe3e13 --- /dev/null +++ b/src/scheduler/http/dto/account/signup/signup-response.dto.ts @@ -0,0 +1,3 @@ +import type { BaseResponseDTO } from '@BaseResponseDTO'; + +export type SignupResponseDTO = BaseResponseDTO<{ }>; \ No newline at end of file diff --git a/src/scheduler/http/dto/account/verifyEmailVerificationCode/verify-email-verification-code-request.dto.ts b/src/scheduler/http/dto/account/verifyEmailVerificationCode/verify-email-verification-code-request.dto.ts new file mode 100644 index 0000000..f2253d7 --- /dev/null +++ b/src/scheduler/http/dto/account/verifyEmailVerificationCode/verify-email-verification-code-request.dto.ts @@ -0,0 +1,10 @@ +import { BaseRequestDTO } from '@BaseRequestDTO'; +import { IsEmail, IsString } from 'class-validator'; + +export class VerifyEmailVerificationCodeRequestDTO extends BaseRequestDTO { + @IsEmail() + email!: string; + + @IsString() + code!: string; +} \ No newline at end of file diff --git a/src/scheduler/http/dto/account/verifyEmailVerificationCode/verify-email-verification-code-response.dto.ts b/src/scheduler/http/dto/account/verifyEmailVerificationCode/verify-email-verification-code-response.dto.ts new file mode 100644 index 0000000..3322474 --- /dev/null +++ b/src/scheduler/http/dto/account/verifyEmailVerificationCode/verify-email-verification-code-response.dto.ts @@ -0,0 +1,3 @@ +import type { BaseResponseDTO } from '@BaseResponseDTO'; + +export type VerifyEmailVerificationCodeResponseDTO = BaseResponseDTO<{ verified: boolean }>; \ No newline at end of file diff --git a/src/scheduler/http/dto/account/verifyPasswordResetCode/verify-password-reset-code-request.dto.ts b/src/scheduler/http/dto/account/verifyPasswordResetCode/verify-password-reset-code-request.dto.ts new file mode 100644 index 0000000..4e6bde1 --- /dev/null +++ b/src/scheduler/http/dto/account/verifyPasswordResetCode/verify-password-reset-code-request.dto.ts @@ -0,0 +1,10 @@ +import { BaseRequestDTO } from '@BaseRequestDTO'; +import { IsEmail, IsString } from 'class-validator'; + +export class VerifyPasswordResetCodeRequestDTO extends BaseRequestDTO { + @IsEmail() + email!: string; + + @IsString() + code!: string; +} \ No newline at end of file diff --git a/src/scheduler/http/dto/account/verifyPasswordResetCode/verify-password-reset-code.response.dto.ts b/src/scheduler/http/dto/account/verifyPasswordResetCode/verify-password-reset-code.response.dto.ts new file mode 100644 index 0000000..6c43ddd --- /dev/null +++ b/src/scheduler/http/dto/account/verifyPasswordResetCode/verify-password-reset-code.response.dto.ts @@ -0,0 +1,3 @@ +import type { BaseResponseDTO } from '@BaseResponseDTO'; + +export type VerifyPasswordResetCodeResponseDTO = BaseResponseDTO<{ verified: boolean }>; \ No newline at end of file diff --git a/src/scheduler/http/dto/base/base-request.dto.ts b/src/scheduler/http/dto/base/base-request.dto.ts new file mode 100644 index 0000000..7e3cfb0 --- /dev/null +++ b/src/scheduler/http/dto/base/base-request.dto.ts @@ -0,0 +1,9 @@ +import { validate, type ValidationError } from "class-validator"; + +export class BaseRequestDTO { + async validateObject(): Promise { + const errors = await validate(this); + + return errors; + } +} \ No newline at end of file diff --git a/src/scheduler/http/dto/base/base-response.dto.ts b/src/scheduler/http/dto/base/base-response.dto.ts new file mode 100644 index 0000000..2bf2d4a --- /dev/null +++ b/src/scheduler/http/dto/base/base-response.dto.ts @@ -0,0 +1,19 @@ +export type SuccessResponse = { + success: true; + message: string; + data: T; + + error?: never; + code?: never; +} + +export type ErrorResponse = { + success: false; + error: string; + code: string; + + message?: never; + data?: never; +} + +export type BaseResponseDTO = SuccessResponse | ErrorResponse; \ No newline at end of file diff --git a/src/scheduler/http/dto/index.ts b/src/scheduler/http/dto/index.ts new file mode 100644 index 0000000..6843a03 --- /dev/null +++ b/src/scheduler/http/dto/index.ts @@ -0,0 +1,33 @@ +export { CheckDuplicationRequestDTO as CheckDuplicationRequest } from './account/checkDuplication/check-duplication-request.dto'; +export type { CheckDuplicationResponseDTO as CheckDuplicationResponse } from './account/checkDuplication/check-duplication-response.dto'; + +export { LoginRequestDTO as LoginRequest } from './account/login/login-request.dto'; +export type { LoginResponseDTO as LoginResponse } from './account/login/login-response.dto'; + +export type { RefreshAccessTokenResponseDTO as RefreshAccessTokenResponse } from './account/refreshAccessToken/refresh-access-token-response.dto'; + +export { ResetPasswordRequestDTO as ResetPasswordRequest } from './account/resetPassword/reset-password-request.dto'; +export type { ResetPasswordResponseDTO as ResetPasswordResponse } from './account/resetPassword/reset-password-response.dto'; + +export { SendEmailVerificationCodeRequestDTO as SendEmailVerificationCodeRequest } from './account/sendEmailVerificationCode/send-email-verification-code-request.dto'; +export type { SendEmailVerificationCodeResponseDTO as SendEmailVerificationCodeResponse } from './account/sendEmailVerificationCode/sned-email-verification-code-response.dto'; + +export { SendPasswordResetCodeRequestDTO as SendPasswordResetCodeRequest } from './account/sendPasswordResetCode/send-password-reset-code-request.dto'; +export type { SendPasswordResetCodeResponseDTO as SendPasswordResetCodeResponse } from './account/sendPasswordResetCode/send-password-reset-code-response.dto'; + +export { SignupRequestDTO as SignupRequest } from './account/signup/signup-request.dto'; +export type { SignupResponseDTO as SignupResponse } from './account/signup/signup-response.dto'; + +export { VerifyEmailVerificationCodeRequestDTO as VerifyEmailVerificationCodeRequest } from './account/verifyEmailVerificationCode/verify-email-verification-code-request.dto'; +export type { VerifyEmailVerificationCodeResponseDTO as VerifyEmailVerificationCodeResponse } from './account/verifyEmailVerificationCode/verify-email-verification-code-response.dto'; + +export { VerifyPasswordResetCodeRequestDTO as VerifyPasswordResetCodeRequest } from './account/verifyPasswordResetCode/verify-password-reset-code-request.dto'; +export type { VerifyPasswordResetCodeResponseDTO as VerifyPasswordResetCodeResponse } from './account/verifyPasswordResetCode/verify-password-reset-code.response.dto'; + +export { ScheduleCreateRequestDTO as ScheduleCreateRequest } from './schedule/create/create-request.dto'; +export type { ScheduleCreateResponseDTO as ScheduleCreateResponse } from './schedule/create/create-response.dto'; + +export { ScheduleListRequestDTO as ScheduleListRequest } from './schedule/list/list-request.dto'; +export type { ScheduleListResponseDTO as ScheduleListResponse, ScheduleList } from './schedule/list/list-response.dto'; + +export type { ScheduleDetailResponseDTO as ScheduleDetailResponse, ScheduleDetail } from './schedule/detail/detail-response.dto'; \ No newline at end of file diff --git a/src/scheduler/http/dto/schedule/create/create-request.dto.ts b/src/scheduler/http/dto/schedule/create/create-request.dto.ts new file mode 100644 index 0000000..505070b --- /dev/null +++ b/src/scheduler/http/dto/schedule/create/create-request.dto.ts @@ -0,0 +1,32 @@ +import { BaseRequestDTO } from '@BaseRequestDTO'; +import { IsArray, IsDateString, IsIn, IsString } from 'class-validator'; +import { ScheduleTypeArray } from 'src/scheduler/type/schedule/ScheduleType'; + +export class ScheduleCreateRequestDTO extends BaseRequestDTO { + @IsString() + name!: string; + + @IsDateString() + startDate!: string; + + @IsDateString() + endDate!: string; + + @IsIn(ScheduleTypeArray) + type!: string; + + @IsString() + style!: string; + + @IsString() + stratTime!: string; + + @IsString() + endTime!: string; + + @IsString() + dayList?: string; + + @IsArray() + participantList?: string[]; +} \ No newline at end of file diff --git a/src/scheduler/http/dto/schedule/create/create-response.dto.ts b/src/scheduler/http/dto/schedule/create/create-response.dto.ts new file mode 100644 index 0000000..743e4b1 --- /dev/null +++ b/src/scheduler/http/dto/schedule/create/create-response.dto.ts @@ -0,0 +1,3 @@ +import type { BaseResponseDTO } from '@BaseResponseDTO'; + +export type ScheduleCreateResponseDTO = BaseResponseDTO<{ }>; \ No newline at end of file diff --git a/src/scheduler/http/dto/schedule/detail/detail-request.dto.ts b/src/scheduler/http/dto/schedule/detail/detail-request.dto.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/scheduler/http/dto/schedule/detail/detail-response.dto.ts b/src/scheduler/http/dto/schedule/detail/detail-response.dto.ts new file mode 100644 index 0000000..48af253 --- /dev/null +++ b/src/scheduler/http/dto/schedule/detail/detail-response.dto.ts @@ -0,0 +1,22 @@ +import type { BaseResponseDTO } from '@BaseResponseDTO'; +import type { ScheduleType } from 'src/scheduler/type/schedule/ScheduleType'; + +export type ScheduleDetail = { + id: string; + name: string; + startDate: Date; + endDate: Date; + status: string; + content?: string; + isDeleted: boolean; + type: ScheduleType; + createdAt: string; + owner: string; + style: string; + startTime: string; + endTime: string; + dayList?: string; + participantList?: string; +} + +export type ScheduleDetailResponseDTO = BaseResponseDTO; \ No newline at end of file diff --git a/src/scheduler/http/dto/schedule/list/list-request.dto.ts b/src/scheduler/http/dto/schedule/list/list-request.dto.ts new file mode 100644 index 0000000..6fe93e4 --- /dev/null +++ b/src/scheduler/http/dto/schedule/list/list-request.dto.ts @@ -0,0 +1,40 @@ +import { BaseRequestDTO } from '@BaseRequestDTO'; +import { IsArray, IsDateString, IsIn, IsNumberString, IsString, ValidateIf } from 'class-validator'; +import { ScheduleStatusArray } from 'src/scheduler/type/schedule/ScheduleStatus'; +import { ScheduleTypeArray } from 'src/scheduler/type/schedule/ScheduleType'; + +export class ScheduleListRequestDTO extends BaseRequestDTO { + @ValidateIf(o => o.date !== undefined) + @IsDateString() + date?: string; + + @ValidateIf(o => o.startDate !== undefined) + @IsDateString() + startDate?: string; + + @ValidateIf(o => o.endDate !== undefined) + @IsDateString() + endDate?: string; + + @ValidateIf(o => o.styleList !== undefined) + @IsArray() + styleList?: string[]; + + @ValidateIf(o => o.typeList !== undefined) + @IsArray() + @IsIn(ScheduleTypeArray, { each: true}) + typeList?: string[]; + + @ValidateIf(o => o.status !== undefined) + @IsString() + @IsIn(ScheduleStatusArray) + status?: string; + + @ValidateIf(o => o.name !== undefined) + @IsString() + name?: string; + + @ValidateIf(o => o.dayList !== undefined) + @IsNumberString() + dayList?: string; +} \ No newline at end of file diff --git a/src/scheduler/http/dto/schedule/list/list-response.dto.ts b/src/scheduler/http/dto/schedule/list/list-response.dto.ts new file mode 100644 index 0000000..d944fb5 --- /dev/null +++ b/src/scheduler/http/dto/schedule/list/list-response.dto.ts @@ -0,0 +1,15 @@ +import type { BaseResponseDTO } from '@BaseResponseDTO'; +import type { ScheduleStatus } from "src/scheduler/type/schedule/ScheduleStatus"; +import type { ScheduleType } from "src/scheduler/type/schedule/ScheduleType"; + +export type ScheduleList = { + name: string; + id: string; + startDate: Date; + endDate: Date; + type: ScheduleType; + style: string; + status: ScheduleStatus; +} + +export type ScheduleListResponseDTO = BaseResponseDTO; \ No newline at end of file diff --git a/src/scheduler/type/schedule/ScheduleDay.ts b/src/scheduler/type/schedule/ScheduleDay.ts new file mode 100644 index 0000000..f831954 --- /dev/null +++ b/src/scheduler/type/schedule/ScheduleDay.ts @@ -0,0 +1,13 @@ +export const ScheduleDay: Record = { + 1: '일', + 2: '월', + 3: '화', + 4: '수', + 5: '목', + 6: '금', + 7: '토' +} as const; + +export const ScheduleDayArray = '1234567'.split(''); + +export const ScheduleDayLabelArray = '일월화수목금토'.split(''); \ No newline at end of file diff --git a/src/scheduler/type/schedule/ScheduleStatus.ts b/src/scheduler/type/schedule/ScheduleStatus.ts new file mode 100644 index 0000000..949869d --- /dev/null +++ b/src/scheduler/type/schedule/ScheduleStatus.ts @@ -0,0 +1,8 @@ +export type ScheduleStatus = 'yet' | 'completed'; + +export const ScheduleStatusLabel: Record = { + 'yet': '미완료', + 'completed': '완료' +} as const; + +export const ScheduleStatusArray = ['yet', 'completed'] as const; \ No newline at end of file diff --git a/src/scheduler/type/schedule/ScheduleType.ts b/src/scheduler/type/schedule/ScheduleType.ts new file mode 100644 index 0000000..200ea33 --- /dev/null +++ b/src/scheduler/type/schedule/ScheduleType.ts @@ -0,0 +1,11 @@ +export type ScheduleType = 'once' | 'daily' | 'weekly' | 'monthly' | 'annual'; + +export const ScheduleTypeLabel: Record = { + 'once': '반복없음', + 'daily': '매일', + 'weekly': '매주', + 'monthly': '매달', + 'annual': '매년' +} as const; + +export const ScheduleTypeArray = ['once', 'daily', 'weekly', 'monthly', 'annual'] as const; \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 50a3e67..695bccc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -44,7 +44,14 @@ "moduleDetection": "force", "skipLibCheck": true, "baseUrl": ".", + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "paths": { + "@/*": ["src./*"], + "@BaseRequestDTO": ["src/scheduler/http/dto/base/base-request.dto.ts"], + "@BaseResponseDTO": ["src/scheduler/http/dto/base/base-response.dto.ts"] + } }, - "include": ["src/**/*"], + "include": ["src/**/*", "scripts"], "exclude": ["node-modules", "dist", "**/*.spec.ts"] }