Compare commits
3 Commits
f451306c90
...
34c33202c6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
34c33202c6 | ||
|
|
bb79557876 | ||
|
|
9578b37c64 |
@@ -1,7 +1,7 @@
|
|||||||
import { defineConfig } from 'drizzle-kit';
|
import { defineConfig } from 'drizzle-kit';
|
||||||
import dotenv from 'dotenv';
|
import dotenv from 'dotenv';
|
||||||
|
|
||||||
dotenv.config();
|
dotenv.config({ path: `.env.${process.env.NODE_ENV}` });
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
dialect: "postgresql",
|
dialect: "postgresql",
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export const userBlockingIdSeq = pgSequence("user_blocking_id_seq", { startWith
|
|||||||
export const emailAddressIdSeq = pgSequence("email_address_id_seq", { startWith: "1", increment: "1", minValue: "1", maxValue: "9223372036854775807", cache: "1", cycle: false })
|
export const emailAddressIdSeq = pgSequence("email_address_id_seq", { startWith: "1", increment: "1", minValue: "1", maxValue: "9223372036854775807", cache: "1", cycle: false })
|
||||||
|
|
||||||
export const comment = pgTable("comment", {
|
export const comment = pgTable("comment", {
|
||||||
id: uuid().primaryKey().notNull(),
|
id: uuid().default(sql`uuid_generate_v4()`).primaryKey().notNull(),
|
||||||
content: text(),
|
content: text(),
|
||||||
createdAt: date("created_at"),
|
createdAt: date("created_at"),
|
||||||
isDeleted: boolean("is_deleted").default(false),
|
isDeleted: boolean("is_deleted").default(false),
|
||||||
@@ -72,19 +72,20 @@ export const participant = pgTable("participant", {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
export const schedule = pgTable("schedule", {
|
export const schedule = pgTable("schedule", {
|
||||||
id: uuid().primaryKey().notNull(),
|
id: uuid().default(sql`uuid_generate_v4()`).primaryKey().notNull(),
|
||||||
name: varchar(),
|
name: varchar().notNull(),
|
||||||
startDate: date("start_date"),
|
startDate: date("start_date").notNull(),
|
||||||
endDate: date("end_date"),
|
endDate: date("end_date").notNull(),
|
||||||
status: varchar(),
|
status: varchar().default('yet').notNull(),
|
||||||
content: text(),
|
content: text(),
|
||||||
isDeleted: boolean("is_deleted").default(false),
|
isDeleted: boolean("is_deleted").default(false).notNull(),
|
||||||
type: varchar(),
|
type: varchar().notNull(),
|
||||||
createdAt: date("created_at"),
|
createdAt: date("created_at"),
|
||||||
owner: uuid(),
|
owner: uuid().notNull(),
|
||||||
style: varchar(),
|
style: varchar().notNull(),
|
||||||
startTime: time("start_time"),
|
startTime: time("start_time").notNull(),
|
||||||
endTime: time("end_time"),
|
endTime: time("end_time").notNull(),
|
||||||
|
dayList: varchar("day_list"),
|
||||||
}, (table) => [
|
}, (table) => [
|
||||||
index("schedule_enddatetime_idx").using("btree", table.endDate.asc().nullsLast().op("date_ops")),
|
index("schedule_enddatetime_idx").using("btree", table.endDate.asc().nullsLast().op("date_ops")),
|
||||||
index("schedule_name_idx").using("btree", table.name.asc().nullsLast().op("text_ops"), table.content.asc().nullsLast().op("text_ops")),
|
index("schedule_name_idx").using("btree", table.name.asc().nullsLast().op("text_ops"), table.content.asc().nullsLast().op("text_ops")),
|
||||||
|
|||||||
@@ -6,9 +6,10 @@ import { RedisModule } from './redis/redis.module';
|
|||||||
import { AccountModule } from './modules/account/account.module';
|
import { AccountModule } from './modules/account/account.module';
|
||||||
import { MailerModule } from './util/mailer/mailer.module';
|
import { MailerModule } from './util/mailer/mailer.module';
|
||||||
import { AppConfigModule } from './config/config.module';
|
import { AppConfigModule } from './config/config.module';
|
||||||
|
import { ScheduleModule } from './modules/schedule/schedule.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [AppConfigModule, DbModule, RedisModule, MailerModule, AccountModule],
|
imports: [AppConfigModule, DbModule, RedisModule, MailerModule, AccountModule, ScheduleModule],
|
||||||
controllers: [AppController],
|
controllers: [AppController],
|
||||||
providers: [AppService],
|
providers: [AppService],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
HttpException,
|
HttpException,
|
||||||
HttpStatus
|
HttpStatus
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
|
import { JsonWebTokenError, TokenExpiredError } from '@nestjs/jwt';
|
||||||
import { FastifyReply, FastifyRequest } from 'fastify';
|
import { FastifyReply, FastifyRequest } from 'fastify';
|
||||||
|
|
||||||
@Catch()
|
@Catch()
|
||||||
@@ -13,6 +14,39 @@ export class AllExceptionsFilter implements ExceptionFilter {
|
|||||||
const ctx = host.switchToHttp();
|
const ctx = host.switchToHttp();
|
||||||
const response = ctx.getResponse<FastifyReply>();
|
const response = ctx.getResponse<FastifyReply>();
|
||||||
const request = ctx.getRequest<FastifyRequest>();
|
const request = ctx.getRequest<FastifyRequest>();
|
||||||
|
console.log(exception);
|
||||||
|
|
||||||
|
// TokenExpiredError
|
||||||
|
if (exception instanceof TokenExpiredError) {
|
||||||
|
const status = HttpStatus.UNAUTHORIZED;
|
||||||
|
|
||||||
|
const responseBody = {
|
||||||
|
statusCode: status,
|
||||||
|
message: 'Access Token Expired',
|
||||||
|
code: 'AccessTokenExpired',
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
path: ctx.getRequest().url
|
||||||
|
};
|
||||||
|
|
||||||
|
response.status(status).send(responseBody);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// JsonWebTokenError
|
||||||
|
if (exception instanceof JsonWebTokenError) {
|
||||||
|
const status = HttpStatus.UNAUTHORIZED;
|
||||||
|
|
||||||
|
const responseBody = {
|
||||||
|
statusCode: status,
|
||||||
|
message: 'Invalid Token',
|
||||||
|
code: 'InvalidToken',
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
path: ctx.getRequest().url
|
||||||
|
};
|
||||||
|
|
||||||
|
response.status(status).send(responseBody);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let status =
|
let status =
|
||||||
exception instanceof HttpException
|
exception instanceof HttpException
|
||||||
|
|||||||
@@ -46,6 +46,6 @@ async function bootstrap() {
|
|||||||
secret: process.env.JWT_SECRET
|
secret: process.env.JWT_SECRET
|
||||||
});
|
});
|
||||||
await app.listen(process.env.PORT ?? 3000, '0.0.0.0', () => { process.env.NODE_ENV !== 'prod' && console.log(`servier is running on ${process.env.PORT}`) });
|
await app.listen(process.env.PORT ?? 3000, '0.0.0.0', () => { process.env.NODE_ENV !== 'prod' && console.log(`servier is running on ${process.env.PORT}`) });
|
||||||
|
// await app.listen(process.env.PORT || 3000, () => { process.env.NODE_ENV !== 'prod' && console.log(`service is running on ${process.env.PORT}`)});
|
||||||
}
|
}
|
||||||
bootstrap();
|
bootstrap();
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export class AuthService {
|
|||||||
constructor(private readonly jwtService: JwtService) {}
|
constructor(private readonly jwtService: JwtService) {}
|
||||||
|
|
||||||
generateTokens(id: string) {
|
generateTokens(id: string) {
|
||||||
const accessToken = this.jwtService.sign({id: id}, { expiresIn: '5s' });
|
const accessToken = this.jwtService.sign({id: id}, { expiresIn: '1m' });
|
||||||
const refreshToken = this.jwtService.sign({id: id}, { expiresIn: '7d' });
|
const refreshToken = this.jwtService.sign({id: id}, { expiresIn: '7d' });
|
||||||
|
|
||||||
return { accessToken, refreshToken };
|
return { accessToken, refreshToken };
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { ExecutionContext, Injectable, UnauthorizedException } from "@nestjs/common";
|
import { ExecutionContext, Injectable, UnauthorizedException } from "@nestjs/common";
|
||||||
import { Reflector } from "@nestjs/core";
|
import { Reflector } from "@nestjs/core";
|
||||||
import { TokenExpiredError } from "@nestjs/jwt";
|
import { JsonWebTokenError, TokenExpiredError } from "@nestjs/jwt";
|
||||||
import { AuthGuard } from "@nestjs/passport";
|
import { AuthGuard } from "@nestjs/passport";
|
||||||
import { IS_PUBLIC_KEY } from "src/common/decorators/public.decorator";
|
import { IS_PUBLIC_KEY } from "src/common/decorators/public.decorator";
|
||||||
|
|
||||||
@@ -23,23 +23,18 @@ export class JwtAccessAuthGuard extends AuthGuard('access-token') {
|
|||||||
return super.canActivate(context);
|
return super.canActivate(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleRequest(err: any, user:any, info:any) {
|
handleRequest(err: any, user:any, info:any, context: ExecutionContext) {
|
||||||
if (info instanceof TokenExpiredError) {
|
|
||||||
throw new UnauthorizedException({
|
|
||||||
statusCode: 401,
|
|
||||||
message: 'Access Token Expired',
|
|
||||||
code: 'AccessTokenExpired'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (err || !user) {
|
if (err || !user) {
|
||||||
throw new UnauthorizedException({
|
if (info instanceof TokenExpiredError) {
|
||||||
statusCode: 401,
|
throw info;
|
||||||
message: 'Invalid Token',
|
}
|
||||||
code: 'InvalidToken'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (info instanceof JsonWebTokenError) {
|
||||||
|
throw info;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw err || new JsonWebTokenError('Unauthorized');
|
||||||
|
}
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,14 @@ import { Injectable, UnauthorizedException } from "@nestjs/common";
|
|||||||
import { ConfigService } from "@nestjs/config";
|
import { ConfigService } from "@nestjs/config";
|
||||||
import { PassportStrategy } from "@nestjs/passport";
|
import { PassportStrategy } from "@nestjs/passport";
|
||||||
import { ExtractJwt, Strategy } from "passport-jwt";
|
import { ExtractJwt, Strategy } from "passport-jwt";
|
||||||
|
import { AccountRepo } from "src/modules/account/account.repo";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class JwtAccessStrategy extends PassportStrategy(Strategy, "access-token") {
|
export class JwtAccessStrategy extends PassportStrategy(Strategy, "access-token") {
|
||||||
constructor(configService: ConfigService) {
|
constructor(
|
||||||
|
configService: ConfigService,
|
||||||
|
private accountRepo: AccountRepo
|
||||||
|
) {
|
||||||
super({
|
super({
|
||||||
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||||
secretOrKey: configService.get<string>('JWT_SECRET')!
|
secretOrKey: configService.get<string>('JWT_SECRET')!
|
||||||
@@ -13,10 +17,11 @@ export class JwtAccessStrategy extends PassportStrategy(Strategy, "access-token"
|
|||||||
}
|
}
|
||||||
|
|
||||||
async validate(payload: any) {
|
async validate(payload: any) {
|
||||||
const token = ExtractJwt.fromAuthHeaderAsBearerToken();
|
console.log(payload);
|
||||||
if (!token) {
|
const user = await this.accountRepo.findById(payload.id);
|
||||||
|
if (!user) {
|
||||||
throw new UnauthorizedException();
|
throw new UnauthorizedException();
|
||||||
}
|
}
|
||||||
return { id: payload.id };
|
return user;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -26,7 +26,7 @@ export class AccountRepo {
|
|||||||
email: string,
|
email: string,
|
||||||
password: string
|
password: string
|
||||||
) {
|
) {
|
||||||
return this
|
return await this
|
||||||
.db
|
.db
|
||||||
.insert(schema.account)
|
.insert(schema.account)
|
||||||
.values({
|
.values({
|
||||||
@@ -42,7 +42,7 @@ export class AccountRepo {
|
|||||||
type: 'email' | 'accountId'
|
type: 'email' | 'accountId'
|
||||||
, id: string
|
, id: string
|
||||||
) {
|
) {
|
||||||
return this
|
return await this
|
||||||
.db
|
.db
|
||||||
.select()
|
.select()
|
||||||
.from(schema.account)
|
.from(schema.account)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { BaseResponseDto } from "../base-response.dto";
|
import { BaseResponseDto } from "../../../../common/dto/base-response.dto";
|
||||||
|
|
||||||
export class CheckDuplicationResponseDto extends BaseResponseDto {
|
export class CheckDuplicationResponseDto extends BaseResponseDto {
|
||||||
isDuplicated: boolean;
|
isDuplicated: boolean;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { BaseResponseDto } from "../base-response.dto";
|
import { BaseResponseDto } from "../../../../common/dto/base-response.dto";
|
||||||
|
|
||||||
export class LoginResponseDto extends BaseResponseDto {
|
export class LoginResponseDto extends BaseResponseDto {
|
||||||
accessToken?: string;
|
accessToken?: string;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { BaseResponseDto } from "../base-response.dto";
|
import { BaseResponseDto } from "../../../../common/dto/base-response.dto";
|
||||||
|
|
||||||
export class RefreshAccessTokenResponseDto extends BaseResponseDto{
|
export class RefreshAccessTokenResponseDto extends BaseResponseDto{
|
||||||
accessToken: string;
|
accessToken: string;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { BaseResponseDto } from "../base-response.dto";
|
import { BaseResponseDto } from "../../../../common/dto/base-response.dto";
|
||||||
|
|
||||||
export class ResetPasswordResponseDto extends BaseResponseDto {
|
export class ResetPasswordResponseDto extends BaseResponseDto {
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { BaseResponseDto } from "../base-response.dto";
|
import { BaseResponseDto } from "../../../../common/dto/base-response.dto";
|
||||||
|
|
||||||
export class SendEmailVerificationCodeResponseDto extends BaseResponseDto{
|
export class SendEmailVerificationCodeResponseDto extends BaseResponseDto{
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { BaseResponseDto } from "../base-response.dto";
|
import { BaseResponseDto } from "../../../../common/dto/base-response.dto";
|
||||||
|
|
||||||
export class SendResetPasswordCodeResponseDto extends BaseResponseDto {
|
export class SendResetPasswordCodeResponseDto extends BaseResponseDto {
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { BaseResponseDto } from "../base-response.dto";
|
import { BaseResponseDto } from "../../../../common/dto/base-response.dto";
|
||||||
|
|
||||||
export class SignupResponseDto extends BaseResponseDto {
|
export class SignupResponseDto extends BaseResponseDto {
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { BaseResponseDto } from "../base-response.dto";
|
import { BaseResponseDto } from "../../../../common/dto/base-response.dto";
|
||||||
|
|
||||||
export class VerifyEmailVerificationCodeResponseDto extends BaseResponseDto{
|
export class VerifyEmailVerificationCodeResponseDto extends BaseResponseDto{
|
||||||
verified: boolean;
|
verified: boolean;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { BaseResponseDto } from "../base-response.dto";
|
import { BaseResponseDto } from "../../../../common/dto/base-response.dto";
|
||||||
|
|
||||||
export class VerifyResetPasswordCodeResponseDto extends BaseResponseDto {
|
export class VerifyResetPasswordCodeResponseDto extends BaseResponseDto {
|
||||||
verified: boolean;
|
verified: boolean;
|
||||||
|
|||||||
@@ -1,17 +1,14 @@
|
|||||||
import { IsArray, IsDate, IsString } from '@nestjs/class-validator';
|
import { IsArray, IsDateString, IsString } from '@nestjs/class-validator';
|
||||||
|
|
||||||
export class CreateRequestDto {
|
export class CreateRequestDto {
|
||||||
@IsString()
|
@IsString()
|
||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
@IsDate()
|
@IsDateString()
|
||||||
startDate: Date;
|
startDate: string;
|
||||||
|
|
||||||
@IsDate()
|
@IsDateString()
|
||||||
endDate: Date;
|
endDate: string;
|
||||||
|
|
||||||
@IsString()
|
|
||||||
status: string;
|
|
||||||
|
|
||||||
@IsString()
|
@IsString()
|
||||||
content: string;
|
content: string;
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import { BaseResponseDto } from "src/common/dto/base-response.dto";
|
||||||
|
|
||||||
|
export class CreateResponseDto extends BaseResponseDto {}
|
||||||
23
src/modules/schedule/dto/detail/detail-response.dto.ts
Normal file
23
src/modules/schedule/dto/detail/detail-response.dto.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { BaseResponseDto } from "src/common/dto/base-response.dto";
|
||||||
|
|
||||||
|
class ScheduleDetail {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
startDate: Date;
|
||||||
|
endDate: Date;
|
||||||
|
status: string;
|
||||||
|
content?: string | null;
|
||||||
|
isDeleted: boolean;
|
||||||
|
type: string;
|
||||||
|
createdAt: string | null;
|
||||||
|
owner: string;
|
||||||
|
style: string;
|
||||||
|
startTime: string;
|
||||||
|
endTime: string;
|
||||||
|
dayList?: string | null;
|
||||||
|
participantList?: string[] | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DetailResponseDto extends BaseResponseDto {
|
||||||
|
data?: ScheduleDetail | null;
|
||||||
|
}
|
||||||
7
src/modules/schedule/dto/index.ts
Normal file
7
src/modules/schedule/dto/index.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export { CreateRequestDto as CreateRequest } from './create/create-request.dto';
|
||||||
|
export { CreateResponseDto as CreateResponse } from './create/create-response.dto'
|
||||||
|
|
||||||
|
export { ListRequestDto as ListRequest } from './list/list-request.dto';
|
||||||
|
export { ListResponseDto as ListResponse } from './list/list-response.dto';
|
||||||
|
|
||||||
|
export { DetailResponseDto as DetailResponse } from './detail/detail-response.dto';
|
||||||
21
src/modules/schedule/dto/list/list-request.dto.ts
Normal file
21
src/modules/schedule/dto/list/list-request.dto.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { IsArray, IsDateString, IsString } from "@nestjs/class-validator";
|
||||||
|
|
||||||
|
export class ListRequestDto {
|
||||||
|
@IsDateString()
|
||||||
|
startDate?: string;
|
||||||
|
|
||||||
|
@IsDateString()
|
||||||
|
endDate?: string;
|
||||||
|
|
||||||
|
@IsArray()
|
||||||
|
styleList?: string[];
|
||||||
|
|
||||||
|
@IsArray()
|
||||||
|
typeList?: string[];
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
status?: 'yet' | 'completed' | undefined;
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
name?: string;
|
||||||
|
}
|
||||||
15
src/modules/schedule/dto/list/list-response.dto.ts
Normal file
15
src/modules/schedule/dto/list/list-response.dto.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { BaseResponseDto } from "src/common/dto/base-response.dto";
|
||||||
|
|
||||||
|
class ScheduleList {
|
||||||
|
name: string;
|
||||||
|
id: string;
|
||||||
|
startDate: Date;
|
||||||
|
endDate: Date;
|
||||||
|
type: string;
|
||||||
|
style: string;
|
||||||
|
status: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ListResponseDto extends BaseResponseDto {
|
||||||
|
data: ScheduleList[];
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { Body, Controller, Get, Param, Post, Req, UseGuards } from "@nestjs/common";
|
||||||
|
import { JwtAccessAuthGuard } from "src/middleware/auth/guard/access-token.guard";
|
||||||
|
import { ScheduleService } from "./schedule.service";
|
||||||
|
import * as DTO from './dto';
|
||||||
|
@UseGuards(JwtAccessAuthGuard)
|
||||||
|
@Controller('schedule')
|
||||||
|
export class ScheduleController {
|
||||||
|
constructor(private readonly scheduleService: ScheduleService) {}
|
||||||
|
|
||||||
|
@Post('/')
|
||||||
|
async getList(@Req() req, @Body() data: DTO.ListRequest): Promise<DTO.ListResponse> {
|
||||||
|
const result = await this.scheduleService.getList(req.user.id, data);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('/:id')
|
||||||
|
async getDetail(@Param('id') id: string): Promise<DTO.DetailResponse> {
|
||||||
|
const result = await this.scheduleService.getDetail(id);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('/create')
|
||||||
|
async create(@Req() req, @Body() data: DTO.CreateRequest): Promise<DTO.CreateResponse> {
|
||||||
|
const result = await this.scheduleService.create(req.user.id, data);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { forwardRef, Module } from "@nestjs/common";
|
||||||
|
import { AuthModule } from "src/middleware/auth/auth.module";
|
||||||
|
import { ScheduleController } from "./schedule.controller";
|
||||||
|
import { ScheduleService } from "./schedule.service";
|
||||||
|
import { ScheduleRepo } from "./schedule.repo";
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [forwardRef(() => AuthModule)],
|
||||||
|
controllers: [ScheduleController],
|
||||||
|
providers: [ScheduleService, ScheduleRepo],
|
||||||
|
exports: [ScheduleService, ScheduleRepo]
|
||||||
|
})
|
||||||
|
export class ScheduleModule {}
|
||||||
@@ -1,43 +1,99 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import * as schema from 'drizzle/schema';
|
import * as schema from 'drizzle/schema';
|
||||||
import { countDistinct, and, eq } from 'drizzle-orm';
|
import { countDistinct, and, eq, gt, gte, lte, like, inArray } from 'drizzle-orm';
|
||||||
import { NodePgDatabase } from 'drizzle-orm/node-postgres';
|
import { NodePgDatabase } from 'drizzle-orm/node-postgres';
|
||||||
|
import * as DTO from './dto';
|
||||||
|
import { Converter } from 'src/util/converter';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ScheduleRepo {
|
export class ScheduleRepo {
|
||||||
constructor(@Inject('DRIZZLE') private readonly db: NodePgDatabase<typeof schema>) {}
|
constructor(@Inject('DRIZZLE') private readonly db: NodePgDatabase<typeof schema>) {}
|
||||||
|
|
||||||
async getList(accountId: string) {
|
async getList(accountId: string, data: DTO.ListRequest) {
|
||||||
|
const { startDate, endDate, name, status, styleList, typeList } = data;
|
||||||
|
const schedule = schema.schedule;
|
||||||
|
|
||||||
|
const result = await this
|
||||||
|
.db
|
||||||
|
.select({
|
||||||
|
id: schedule.id,
|
||||||
|
name: schedule.name,
|
||||||
|
startDate: schedule.startDate,
|
||||||
|
endDate: schedule.endDate,
|
||||||
|
status: schedule.status,
|
||||||
|
style: schedule.style,
|
||||||
|
type: schedule.style,
|
||||||
|
})
|
||||||
|
.from(schedule)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(schedule.owner, accountId),
|
||||||
|
startDate ? gte(schedule.startDate, Converter.formatDateToSqlDate(startDate)) : undefined,
|
||||||
|
endDate ? lte(schedule.endDate, Converter.formatDateToSqlDate(endDate)) : undefined,
|
||||||
|
name ? like(schedule.name, `%${name}%`) : undefined,
|
||||||
|
(typeList && typeList.length > 0) ? inArray(schedule.type, typeList) : undefined,
|
||||||
|
(styleList && styleList.length > 0) ? inArray(schedule.style, styleList) : undefined,
|
||||||
|
status ? eq(schedule.status, status) : undefined,
|
||||||
|
eq(schedule.isDeleted, false)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
const resultData = result.map((schedule) => {
|
||||||
|
return {
|
||||||
|
id: schedule.id,
|
||||||
|
name: schedule.name,
|
||||||
|
type: schedule.type,
|
||||||
|
style: schedule.style,
|
||||||
|
status: schedule.status,
|
||||||
|
startDate: new Date(schedule.startDate),
|
||||||
|
endDate: new Date(schedule.endDate)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return resultData;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getDetail(id: string) {
|
||||||
|
const schedule = schema.schedule;
|
||||||
const result = await this
|
const result = await this
|
||||||
.db
|
.db
|
||||||
.select()
|
.select()
|
||||||
.from(schema.schedule)
|
.from(schedule)
|
||||||
.where(
|
.where(
|
||||||
and(
|
and(
|
||||||
eq(schema.schedule.owner, accountId),
|
eq(schedule.id, id),
|
||||||
eq(schema.schedule.isDeleted, false)
|
eq(schedule.isDeleted, false)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getDetail(id: string) {
|
async create(
|
||||||
const result = await this
|
accountId: string,
|
||||||
|
name: string,
|
||||||
|
startDate: string,
|
||||||
|
endDate: string,
|
||||||
|
startTime: string,
|
||||||
|
endTime: string,
|
||||||
|
style: string,
|
||||||
|
content: string,
|
||||||
|
type: string
|
||||||
|
) {
|
||||||
|
return await this
|
||||||
.db
|
.db
|
||||||
.select()
|
.insert(schema.schedule)
|
||||||
.from(schema.schedule)
|
.values({
|
||||||
.where(
|
name: name,
|
||||||
and(
|
content: content,
|
||||||
eq(schema.schedule.id, id),
|
owner: accountId,
|
||||||
eq(schema.schedule.isDeleted, false)
|
startDate: startDate,
|
||||||
)
|
endDate: endDate,
|
||||||
);
|
startTime: startTime,
|
||||||
|
endTime: endTime,
|
||||||
return result[0];
|
status: 'yet',
|
||||||
}
|
style: style,
|
||||||
|
type: type
|
||||||
async create() {
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
import { Injectable } from "@nestjs/common";
|
||||||
|
import { ScheduleRepo } from "./schedule.repo";
|
||||||
|
import * as DTO from './dto';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ScheduleService {
|
||||||
|
constructor(
|
||||||
|
private readonly scheduleRepo: ScheduleRepo
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async getList(accountId: string, data: DTO.ListRequest): Promise<DTO.ListResponse> {
|
||||||
|
const result = await this.scheduleRepo.getList(accountId, data);
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
data: result
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async getDetail(id: string) {
|
||||||
|
const result = await this.scheduleRepo.getDetail(id);
|
||||||
|
|
||||||
|
if (result.length < 1) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: '존재하지 않는 일정입니다.'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
...result[0],
|
||||||
|
startDate: new Date(result[0].startDate),
|
||||||
|
endDate: new Date(result[0].endDate)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
data: data
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async create(accountId: string, data: DTO.CreateRequest): Promise<DTO.CreateResponse> {
|
||||||
|
const { name, content, startDate, endDate, startTime, endTime, style, type } = data;
|
||||||
|
|
||||||
|
const result = await this.scheduleRepo.create(
|
||||||
|
accountId,
|
||||||
|
name,
|
||||||
|
startDate,
|
||||||
|
endDate,
|
||||||
|
startTime,
|
||||||
|
endTime,
|
||||||
|
style,
|
||||||
|
content,
|
||||||
|
type
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.rowCount) {
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: "일정이 생성되었습니다."
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: "일정 생성에 실패하였습니다."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,4 +8,13 @@ export class Converter {
|
|||||||
static comparePassword(rawPassword: string, hashedPassword: string) {
|
static comparePassword(rawPassword: string, hashedPassword: string) {
|
||||||
return bcrypt.compareSync(rawPassword, hashedPassword);
|
return bcrypt.compareSync(rawPassword, hashedPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static formatDateToSqlDate(date: string): string {
|
||||||
|
const targetDate = new Date(date);
|
||||||
|
const year = targetDate.getUTCFullYear();
|
||||||
|
const month = (targetDate.getUTCMonth() + 1).toString().padStart(2, '0');
|
||||||
|
const day = (targetDate.getUTCDate()).toString().padStart(2, '0');
|
||||||
|
|
||||||
|
return `${year}-${month}-${day}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user