Compare commits

...

31 Commits

Author SHA1 Message Date
ac850adfcf issue #
- 팔로우 DTO 구현 중
2025-12-17 23:50:37 +09:00
geonhee-min
b4525bd13d 1.0.23 2025-12-17 09:49:47 +09:00
geonhee-min
963c78c35a issue #
- 커스텀 데코레이터, class-validator 적용
2025-12-17 09:49:42 +09:00
geonhee-min
0a23cb071c 1.0.22 2025-12-17 08:44:49 +09:00
geonhee-min
5fbcaca39b issue #
- SchedulerDTO export type 이슈 수정
2025-12-17 08:44:43 +09:00
geonhee-min
c746f4e29c 1.0.21 2025-12-16 16:52:52 +09:00
geonhee-min
624c871d7f issue #
- BaseRequest/Response export
2025-12-16 16:52:44 +09:00
geonhee-min
adec968612 1.0.20 2025-12-16 16:25:11 +09:00
geonhee-min
290d87ff1c issue #
- class-transform 적용
2025-12-16 16:25:02 +09:00
geonhee-min
70ae2c3fad 1.0.19 2025-12-16 15:17:27 +09:00
geonhee-min
b04f1c057a issue #
- ScheduleListResponseDTO data 타입 오류 수정
2025-12-16 15:17:20 +09:00
geonhee-min
81e16eec34 1.0.18 2025-12-16 15:14:13 +09:00
geonhee-min
87542664cc issue #
- ScheduleCreateRequestDTO type 타입 수정
2025-12-16 15:14:06 +09:00
geonhee-min
636e01aea7 issue #
- ScheduleCreateRequestDTO content 누락 수정
2025-12-16 15:13:25 +09:00
geonhee-min
293a6d5c59 1.0.17 2025-12-16 15:05:15 +09:00
geonhee-min
b5b765b484 issue #
- Scheduler-type export 변경
2025-12-16 15:05:11 +09:00
geonhee-min
41def4b196 1.0.16 2025-12-16 14:58:58 +09:00
geonhee-min
40bb72cad6 issue #
- scheduler/type export
2025-12-16 14:58:51 +09:00
geonhee-min
942a8cb7be 1.0.15 2025-12-16 14:55:58 +09:00
geonhee-min
e357da90b6 issue #
- ScheduleCreateDTO stratTime -> startTime 오타 수정
2025-12-16 14:55:55 +09:00
geonhee-min
fb5c188972 1.0.14 2025-12-16 14:50:20 +09:00
geonhee-min
a08682ef68 issue #
- SignupRequestDTO accoundId -> accountId 오타 수정
2025-12-16 14:50:14 +09:00
geonhee-min
7ceb9eb639 1.0.13 2025-12-16 14:24:33 +09:00
geonhee-min
e9c6b0cdf8 1.0.12 2025-12-16 14:24:15 +09:00
geonhee-min
624616e182 issue #
- PasswordReset 관련 API 이름 수정
2025-12-16 14:24:13 +09:00
geonhee-min
ae019c85b0 1.0.11 2025-12-16 14:11:24 +09:00
geonhee-min
4a52e049f2 issue #
- DTO ->SchedulerDTO 내보내기 이름 변경
2025-12-16 14:11:12 +09:00
geonhee-min
7c1a39cf9c 1.0.10 2025-12-16 14:06:34 +09:00
geonhee-min
1c8b3685b8 issue #
DTO 공용화
2025-12-16 14:06:28 +09:00
geonhee-min
ab0a229104 1.0.9 2025-12-15 09:39:33 +09:00
geonhee-min
c372497b80 issue #
- refresh Access Token Api url 수정
2025-12-15 09:39:23 +09:00
49 changed files with 884 additions and 10 deletions

27
.vscode/snippets.code-snippets vendored Normal file
View File

@@ -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}>;"
]
}
}

259
package-lock.json generated
View File

@@ -1,14 +1,17 @@
{
"name": "@baekyangdan/core-utils",
"version": "1.0.8",
"version": "1.0.23",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@baekyangdan/core-utils",
"version": "1.0.8",
"version": "1.0.23",
"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"
}
}
}
}

View File

@@ -1,12 +1,13 @@
{
"name": "@baekyangdan/core-utils",
"version": "1.0.8",
"version": "1.0.23",
"description": "Common Repo",
"main": "dist/index.js",
"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"

39
scripts/generate-dto.js Normal file
View File

@@ -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`),
''
);

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -9,4 +9,8 @@ export * from './common/format/DateTimeFormat';
export * from './scheduler/http/HttpResponseMessage';
export * from './scheduler/http/HttpApiUrl';
export * from './scheduler/http/HttpApiUrl';
export * as SchedulerDTO from './scheduler/http/dto';
export * as Type from './scheduler/type/schedule';

View File

@@ -0,0 +1,12 @@
type Decorator = (target: Object, propertyKey: string | symbol) => void;
export function composeDecorators(...decorators: Decorator[]): Decorator {
return (
target: Object,
property: string | symbol
) => {
for (const decorator of decorators) {
decorator(target, property);
}
}
}

View File

@@ -0,0 +1,11 @@
import { getMessage } from "../type/class-validator/MessageType";
import { composeDecorators } from "./composer";
import { IsNotEmpty } from "class-validator";
export function IsRequired() {
const notEmptyMessage = getMessage('REQUIRED');
return composeDecorators(
IsNotEmpty({ message: notEmptyMessage })
);
}

View File

@@ -0,0 +1,23 @@
import { getMessage } from "../type/class-validator/MessageType";
import { composeDecorators } from "./composer";
import { IsIn, IsArray } from "class-validator";
export function IsValid(allowedValues: ReadonlyArray<any>, isArray: boolean = false) {
const allowedValuesString = allowedValues.join(', ');
if (isArray) {
return composeDecorators(
IsArray({ message: `TYPE: The type of $property must be array.`}),
IsIn(allowedValues, {
each: true,
message: `${getMessage('INVALID_ARRAY')} Valid values: ${allowedValuesString}`
})
);
}
return composeDecorators(
IsIn(allowedValues, {
message: `${getMessage('INVALID')} Valid values: ${allowedValuesString}`
})
);
}

View File

@@ -5,12 +5,12 @@ export const HttpApiUrl = {
checkDuplication: '/check-duplication',
sendEmailVerificationCode: '/send-email-verification-code',
verifyEmailVerificationCode: '/verify-email-verification-code',
sendResetPasswordCode: '/send-reset-password-code',
verifyResetPasswordCode: '/verify-reset-password-code',
sendPasswordResetCode: '/send-password-reset-code',
verifyPasswordResetCode: '/verify-password-reset-code',
resetPassword: '/reset-password',
signup: '/signup',
login: '/login',
refreshToken: '/refresh-token'
refreshAccessToken: '/refresh-access-token'
},
Schedule: {
base: '/schedule',
@@ -39,4 +39,4 @@ export const HttpApiUrl = {
read: '/read',
delete: '/delete'
}
}
} as const;

View File

@@ -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;
}

View File

@@ -0,0 +1,3 @@
import type { BaseResponseDTO } from '@BaseResponseDTO';
export type CheckDuplicationResponseDTO = BaseResponseDTO<{ isDuplicated: boolean }>;

View File

@@ -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;
}

View File

@@ -0,0 +1,8 @@
import type { BaseResponseDTO } from '@BaseResponseDTO';
export type LoginResponseDTO = BaseResponseDTO<
{
accessToken: string;
refreshToken: string;
}
>;

View File

@@ -0,0 +1,8 @@
import type { BaseResponseDTO } from '@BaseResponseDTO';
export type RefreshAccessTokenResponseDTO = BaseResponseDTO<
{
accessToken: string;
refreshToken: string;
}
>;

View File

@@ -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;
}

View File

@@ -0,0 +1,3 @@
import type { BaseResponseDTO } from '@BaseResponseDTO';
export type ResetPasswordResponseDTO = BaseResponseDTO<{ }>;

View File

@@ -0,0 +1,7 @@
import { IsEmail } from "class-validator";
import { BaseRequestDTO } from "../../base/base-request.dto";
export class SendEmailVerificationCodeRequestDTO extends BaseRequestDTO {
@IsEmail()
email!: string;
}

View File

@@ -0,0 +1,3 @@
import type { BaseResponseDTO } from '@BaseResponseDTO';
export type SendEmailVerificationCodeResponseDTO = BaseResponseDTO<{ }>;

View File

@@ -0,0 +1,7 @@
import { IsEmail } from "class-validator";
import { BaseRequestDTO } from "../../base/base-request.dto";
export class SendPasswordResetCodeRequestDTO extends BaseRequestDTO {
@IsEmail()
email!: string;
}

View File

@@ -0,0 +1,3 @@
import type { BaseResponseDTO } from '@BaseResponseDTO';
export type SendPasswordResetCodeResponseDTO = BaseResponseDTO<{ }>;

View File

@@ -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()
accountId!: string;
@IsString()
@Matches(PasswordFormat)
password!: string;
}

View File

@@ -0,0 +1,3 @@
import type { BaseResponseDTO } from '@BaseResponseDTO';
export type SignupResponseDTO = BaseResponseDTO<{ }>;

View File

@@ -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;
}

View File

@@ -0,0 +1,3 @@
import type { BaseResponseDTO } from '@BaseResponseDTO';
export type VerifyEmailVerificationCodeResponseDTO = BaseResponseDTO<{ verified: boolean }>;

View File

@@ -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;
}

View File

@@ -0,0 +1,3 @@
import type { BaseResponseDTO } from '@BaseResponseDTO';
export type VerifyPasswordResetCodeResponseDTO = BaseResponseDTO<{ verified: boolean }>;

View File

@@ -0,0 +1,9 @@
import { validate, type ValidationError } from "class-validator";
export class BaseRequestDTO {
async validateObject(): Promise<ValidationError[]> {
const errors = await validate(this);
return errors;
}
}

View File

@@ -0,0 +1,19 @@
export type SuccessResponse<T> = {
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<T> = SuccessResponse<T> | ErrorResponse;

View File

@@ -0,0 +1,16 @@
import { IsRequired } from "src/scheduler/decorator/required-decorator";
import { IsString } from 'class-validator';
import { getMessage } from "src/scheduler/type/class-validator/MessageType";
import type { BaseResponseDTO } from "../../base/base-response.dto"
export class FollowList {
@IsRequired()
@IsString({ message: getMessage('TYPE') })
id!: string;
@IsRequired()
@IsString({ message: getMessage('TYPE') })
name!: string;
}
export type FollowListResponse = BaseResponseDTO<FollowList[]>;

View File

@@ -0,0 +1,36 @@
export { BaseRequestDTO as BaseRequest } from './base/base-request.dto';
export { type BaseResponseDTO as BaseResponse } from './base/base-response.dto';
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';

View File

@@ -0,0 +1,36 @@
import { BaseRequestDTO } from '@BaseRequestDTO';
import { Type as TransformType } from 'class-transformer';
import { IsArray, IsDateString, IsIn, IsString } from 'class-validator';
import { TypeArray, type Type } from 'src/scheduler/type/schedule/ScheduleType';
export class ScheduleCreateRequestDTO extends BaseRequestDTO {
@IsString()
name!: string;
@IsString()
content!: string;
@TransformType(() => Date)
startDate!: Date;
@TransformType(() => Date)
endDate!: Date;
@IsIn(TypeArray)
type!: Type;
@IsString()
style!: string;
@IsString()
startTime!: string;
@IsString()
endTime!: string;
@IsString()
dayList?: string;
@IsArray()
participantList?: string[];
}

View File

@@ -0,0 +1,3 @@
import type { BaseResponseDTO } from '@BaseResponseDTO';
export type ScheduleCreateResponseDTO = BaseResponseDTO<{ }>;

View File

@@ -0,0 +1,74 @@
import type { BaseResponseDTO } from '@BaseResponseDTO';
import type { Status } from 'src/scheduler/type/schedule/ScheduleStatus';
import type { Type } from 'src/scheduler/type/schedule/ScheduleType';
import { Type as TransformType } from 'class-transformer';
import { IsRequired } from 'src/scheduler/decorator/required-decorator';
import { IsArray, IsBoolean, IsDate, IsOptional, IsString, ValidateIf } from 'class-validator';
import { getMessage } from 'src/scheduler/type/class-validator/MessageType';
export class ScheduleDetail {
@IsRequired()
@IsString({ message: getMessage('TYPE')})
id!: string;
@IsRequired()
@IsString({ message: getMessage('TYPE')})
name!: string;
@IsRequired()
@IsString({ message: getMessage('TYPE') })
status!: Status;
@ValidateIf(o => o.content !== undefined && o.content !== null)
@IsString({ message: getMessage('TYPE') })
content?: string;
@IsRequired()
@IsBoolean({ message: getMessage('TYPE')})
isDeleted!: boolean;
@IsRequired()
@IsString({ message: getMessage('TYPE') })
type!: Type;
@IsRequired()
@IsString({ message: getMessage('TYPE') })
createdAt!: string;
@IsRequired()
@IsString({ message: getMessage('TYPE') })
owner!: string;
@IsRequired()
@IsString({ message: getMessage('TYPE') })
style!: string;
@IsRequired()
@IsString({ message: getMessage('TYPE') })
startTime!: string;
@IsRequired()
@IsString({ message: getMessage('TYPE') })
endTime!: string;
@ValidateIf(o => o.dayList !== undefined && o.dayList !== null)
@IsString({ message: getMessage('TYPE') })
dayList?: string;
@ValidateIf(o => o.participantList !== undefined && o.participantList !== null)
@IsArray()
@IsString({ each: true, message: getMessage('TYPE') })
participantList?: string[];
@TransformType(() => Date)
@IsRequired()
@IsDate({ message: getMessage('TYPE') })
startDate!: Date;
@TransformType(() => Date)
@IsRequired()
@IsDate({ message: getMessage('TYPE') })
endDate!: Date;
}
export type ScheduleDetailResponseDTO = BaseResponseDTO<ScheduleDetail>;

View File

@@ -0,0 +1,44 @@
import { BaseRequestDTO } from '@BaseRequestDTO';
import { Type } from 'class-transformer';
import { IsArray, IsDate, IsIn, IsNumberString, IsString, ValidateIf } from 'class-validator';
import { StatusArray } from 'src/scheduler/type/schedule/ScheduleStatus';
import { TypeArray } from 'src/scheduler/type/schedule/ScheduleType';
export class ScheduleListRequestDTO extends BaseRequestDTO {
@ValidateIf(o => o.date !== undefined)
@Type(() => Date)
@IsDate()
date?: Date;
@ValidateIf(o => o.startDate !== undefined)
@Type(() => Date)
@IsDate()
startDate?: Date;
@ValidateIf(o => o.endDate !== undefined)
@Type(() => Date)
@IsDate()
endDate?: Date;
@ValidateIf(o => o.styleList !== undefined)
@IsArray()
styleList?: string[];
@ValidateIf(o => o.typeList !== undefined)
@IsArray()
@IsIn(TypeArray, { each: true})
typeList?: string[];
@ValidateIf(o => o.status !== undefined)
@IsString()
@IsIn(StatusArray)
status?: string;
@ValidateIf(o => o.name !== undefined)
@IsString()
name?: string;
@ValidateIf(o => o.dayList !== undefined)
@IsNumberString()
dayList?: string;
}

View File

@@ -0,0 +1,41 @@
import type { BaseResponseDTO } from '@BaseResponseDTO';
import type { Status } from "src/scheduler/type/schedule/ScheduleStatus";
import type { Type } from "src/scheduler/type/schedule/ScheduleType";
import { Type as TransformType } from 'class-transformer';
import { IsDate, IsString } from 'class-validator';
import { IsRequired } from 'src/scheduler/decorator/required-decorator';
import { getMessage } from 'src/scheduler/type/class-validator/MessageType';
export class ScheduleList {
@IsRequired()
@IsString({ message: getMessage('TYPE') })
name!: string;
@IsRequired()
@IsString({ message: getMessage('TYPE')})
id!: string;
@IsRequired()
@IsString({ message: getMessage('TYPE')})
type!: Type;
@IsRequired()
@IsString({ message: getMessage('TYPE')})
style!: string;
@IsRequired()
@IsString({ message: getMessage('TYPE')})
status!: Status;
@TransformType(() => Date)
@IsRequired()
@IsDate()
startDate!: Date;
@TransformType(() => Date)
@IsRequired()
@IsDate()
endDate!: Date;
}
export type ScheduleListResponseDTO = BaseResponseDTO<ScheduleList[]>;

View File

@@ -0,0 +1,10 @@
export type MessageType = 'REQUIRED' | 'TYPE' | 'INVALID' | 'INVALID_ARRAY';
export const getMessage = (type: MessageType) => {
switch (type) {
case 'REQUIRED': return `REQUIRED: $property is required entity.`;
case 'TYPE': return `TYPE: The type of $property is invalid.`;
case 'INVALID': return `INVALID: The value of $property($value) is invalid.`;
case 'INVALID_ARRAY': return `INVALID_ARRAY: The $property array includes invalid value($value).`
}
}

View File

@@ -0,0 +1,13 @@
export const Day: Record<string, string> = {
1: '일',
2: '월',
3: '화',
4: '수',
5: '목',
6: '금',
7: '토'
} as const;
export const DayArray = '1234567'.split('');
export const DayLabelArray = '일월화수목금토'.split('');

View File

@@ -0,0 +1,8 @@
export type Status = 'yet' | 'completed';
export const StatusLabel: Record<Status, string> = {
'yet': '미완료',
'completed': '완료'
} as const;
export const StatusArray = ['yet', 'completed'] as const;

View File

@@ -0,0 +1,11 @@
export type Type = 'once' | 'daily' | 'weekly' | 'monthly' | 'annual';
export const TypeLabel: Record<Type, string> = {
'once': '반복없음',
'daily': '매일',
'weekly': '매주',
'monthly': '매달',
'annual': '매년'
} as const;
export const TypeArray = ['once', 'daily', 'weekly', 'monthly', 'annual'] as const;

View File

@@ -0,0 +1,3 @@
export * from './ScheduleDay';
export * from './ScheduleStatus';
export * from './ScheduleType';

View File

@@ -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"]
}