- 디렉토리 구조 개선
This commit is contained in:
geonhee-min
2025-12-10 17:13:28 +09:00
parent f451306c90
commit 9578b37c64
19 changed files with 84 additions and 48 deletions

View File

@@ -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",

View File

@@ -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")),

View File

@@ -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],
}) })

View File

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

View File

@@ -45,7 +45,7 @@ async function bootstrap() {
app.register(fastifyCookie, { app.register(fastifyCookie, {
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();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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