Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parkj12b/issue72 #78

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions .env.example

This file was deleted.

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ node_modules/

.env*
!.env.example
**/backup.sql
5 changes: 3 additions & 2 deletions backend/src/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm'; // Add this line
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { HistoriesModule } from './histories/histories.module';
import { dbConfig } from './config';
import { UsersModule } from './users/users.module';

@Module({
imports: [TypeOrmModule.forRoot(dbConfig), HistoriesModule],
imports: [TypeOrmModule.forRoot(dbConfig), HistoriesModule, UsersModule],
controllers: [AppController],
providers: [AppService],
})
Expand Down
13 changes: 13 additions & 0 deletions backend/src/common/dto/dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { createZodDto } from '@anatine/zod-nestjs';
import {
findOneSchema,

Check warning on line 3 in backend/src/common/dto/dto.ts

View workflow job for this annotation

GitHub Actions / build (20)

'findOneSchema' is defined but never used

Check warning on line 3 in backend/src/common/dto/dto.ts

View workflow job for this annotation

GitHub Actions / autofix

'findOneSchema' is defined but never used

Check warning on line 3 in backend/src/common/dto/dto.ts

View workflow job for this annotation

GitHub Actions / build (22)

'findOneSchema' is defined but never used
paginationRequestSchema,

Check warning on line 4 in backend/src/common/dto/dto.ts

View workflow job for this annotation

GitHub Actions / build (20)

'paginationRequestSchema' is defined but never used

Check warning on line 4 in backend/src/common/dto/dto.ts

View workflow job for this annotation

GitHub Actions / autofix

'paginationRequestSchema' is defined but never used

Check warning on line 4 in backend/src/common/dto/dto.ts

View workflow job for this annotation

GitHub Actions / build (22)

'paginationRequestSchema' is defined but never used
paginationMetaSchema,
} from '../schema/schema';

class PaginationMetaDto extends createZodDto(paginationMetaSchema) {}

export class PaginationDto<Tdata> {
data: Tdata;
meta: PaginationMetaDto;
}
28 changes: 28 additions & 0 deletions backend/src/common/schema/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { extendApi } from '@anatine/zod-openapi';

Check warning on line 1 in backend/src/common/schema/schema.ts

View workflow job for this annotation

GitHub Actions / build (20)

'extendApi' is defined but never used

Check warning on line 1 in backend/src/common/schema/schema.ts

View workflow job for this annotation

GitHub Actions / autofix

'extendApi' is defined but never used

Check warning on line 1 in backend/src/common/schema/schema.ts

View workflow job for this annotation

GitHub Actions / build (22)

'extendApi' is defined but never used
import { z } from 'zod';

export const findOneSchema = z.coerce.number().int().min(0);

export const paginationRequestSchema = z.object({
page: z.coerce.number().int().min(1).default(1),
limit: z.coerce.number().int().min(10).default(10),
});

/**
* @description pagination meta schema
*
* @current_page current page
* @limit number of items per page
* @total total number of items
* @total_pages total number of pages
* @next next page number
* @prev previous page number
*/
export const paginationMetaSchema = z.object({
limit: z.coerce.number().int().min(1),
total: z.coerce.number().int().min(0),
current_page: z.coerce.number().int().min(1),
total_pages: z.coerce.number().int().min(1),
next: z.coerce.number().int().min(1).nullable(),
prev: z.coerce.number().int().min(1).nullable(),
});
25 changes: 25 additions & 0 deletions backend/src/common/utils/paginate.utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { PaginationDto } from '../dto/dto';

export async function paginate<T>(
data: T,
total: number,
page: number,
limit: number,
): Promise<PaginationDto<T>> {
const totalPages = Math.ceil(total / limit);
const currentPage = page;
const nextPage = page < totalPages ? page + 1 : null;
const prevPage = page > 1 ? page - 1 : null;

const paginationResponse = new PaginationDto<T>();
paginationResponse.data = data;
paginationResponse.meta = {
limit: limit,
total: total,
current_page: currentPage,
total_pages: totalPages,
next: nextPage,
prev: prevPage,
};
return paginationResponse;
}
24 changes: 24 additions & 0 deletions backend/src/common/utils/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { z } from 'zod';

export function isStringInArrayCaseInsensitive(
str: string,
arr: string[],
): boolean {
return arr.some((item) => item.toLowerCase() === str.toLowerCase());
}

type EnumLike = {
[k: string]: string | number;
[nu: number]: string;
};

export const zCoercedEnum = <T extends EnumLike>(e: T) =>
z.preprocess((val) => {
const target = String(val)?.toLowerCase();
for (const key in Object.values(e)) {
if (String(key)?.toLowerCase() === target) {
return key;
}
}
return null;
}, z.nativeEnum(e));
3 changes: 2 additions & 1 deletion backend/src/config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { TypeOrmModuleOptions } from '@nestjs/typeorm';
import * as dotenv from 'dotenv';
import * as path from 'path';

dotenv.config();

Expand All @@ -10,6 +11,6 @@ export const dbConfig = {
username: process.env.MYSQL_USER,
password: process.env.MYSQL_PASSWORD,
database: process.env.MYSQL_DATABASE,
entities: [__dirname + '/entities/*.{ts,js}'],
entities: [path.join(__dirname, '../**/dist/entities/*{.ts,.js}')],
synchronize: false,
} satisfies TypeOrmModuleOptions;
2 changes: 1 addition & 1 deletion backend/src/entities/VLendingForSearchUser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export class VLendingForSearchUser {
duedate: Date;

@ViewColumn()
overDueDay: Date;
overDueDay: number;

@ViewColumn()
reservedNum: number;
Expand Down
7 changes: 7 additions & 0 deletions backend/src/users/dto/users.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,20 @@ import {
getAPIVersionResponseSchema,
getMyUserInfoResponseSchema,
getUsersRequestSchema,
getUsersResponseInnerSchema,
getUsersResponseSchema,
myUpdateUsersRequestSchema,
updateUsersParamSchema,
updateUsersRequestSchema,
updateUsersResponseSchema,
} from '../schema/users.schema';

// export class GetUserRequestDto extends createZodDto(updateUsersParamSchema) {}

export class GetUserResponseDto extends createZodDto(
getUsersResponseInnerSchema,
) {}

export class GetUsersRequestDto extends createZodDto(getUsersRequestSchema) {}

export class GetUsersResponseDto extends createZodDto(getUsersResponseSchema) {}
Expand Down
107 changes: 74 additions & 33 deletions backend/src/users/schema/users.schema.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,62 @@
import { extendApi } from '@anatine/zod-openapi';
import { paginationRequestSchema } from 'src/common/schema/schema';
import { zCoercedEnum } from 'src/common/utils/utils';
import { z } from 'zod';

export const getUsersRequestSchema = z.object({
nicknameOrEmail: z.string().describe('검색할 유저의 nickname or email'),
page: z.coerce.number().int().describe('페이지').min(1),
limit: z.coerce
const getUserSearchSchema = z.object({
search: z.string().optional().describe('검색할 유저의 nickname or email'),
});

export enum UserInclude {
LENDINGS = 'lendings',
RESERVATIONS = 'reservations',
}

export const getUserRequestSchema = z.object({
include: z
.preprocess(
(value) => {
const arrayValue = Array.isArray(value) ? value : value ? [value] : [];
return arrayValue.map((item) =>
typeof item === 'string' ? item.toLowerCase() : item,
);
},
z.array(z.enum([UserInclude.LENDINGS, UserInclude.RESERVATIONS])),
)
.optional()
.nullable()
.describe('포함할 데이터'),
});

export const getUsersRequestSchema = getUserSearchSchema
.merge(paginationRequestSchema)
.merge(getUserRequestSchema);

const user = z.object({
id: z.coerce.number().int().describe('유저 번호').min(0),
email: z.string().describe('이메일'),
nickname: z.string().describe('닉네임').nullable(),
intraId: z.coerce
.number()
.int()
.describe(' 한 페이지에 들어올 검색결과 수 ')
.min(1),
id: z.coerce.number().int().describe('유저의 id').min(0),
.describe('인트라 고유 번호')
.min(0)
.nullable(),
slack: z.string().describe('slack 멤버 Id').nullable(),
penaltyEndDate: z.date().describe('패널티 끝나는 날짜'),
role: z.coerce.number().int().describe('권한'),
});

const lending = z.object({
userId: z.coerce.number(),
bookInfoId: z.coerce.number(),
const VlendingForSearchUser = z.object({
userId: z.coerce.number().int(),
bookInfoId: z.coerce.number().int(),
lendDate: z.coerce.date(),
lendingCondition: z.string(),
image: z.string(),
author: z.string(),
title: z.string(),
image: z.string().url('이미지 URL이 아닙니다.'),
author: z.string().min(1),
title: z.string().min(1),
duedate: z.coerce.date(),
overDueDay: z.coerce.number(),
overDueDay: z.coerce.number().int().min(0).describe('연체된 날수'),
reservedNum: z.coerce.number().min(0),
});

Expand All @@ -37,18 +72,19 @@ const VUserReservations = z.object({
userId: z.coerce.number().min(0),
});

const getUsersResponseInnerSchema = z.object({
id: z.coerce.number().int().describe('유저 번호').min(0),
email: z.string().describe('이메일'),
nickname: z.string().describe('닉네임'),
intraId: z.coerce.number().int().describe('인트라 고유 번호').min(0),
slack: z.string().describe('slack 멤버 Id'),
penaltyEndDate: z.string().describe('패널티 끝나는 날짜'),
overDueDay: z.coerce.number().describe('현재 연체된 날수').min(0),
role: z.coerce.number().int().describe('권한'),
reservations: z.array(VUserReservations).describe('해당 유저의 예약 정보'),
lendings: z.array(lending).describe('해당 유저의 대출 정보'),
});
export const getUsersResponseInnerSchema = z
.object({
overDueDay: z.coerce.number().int().optional().describe('현재 연체된 날수'),
reservations: z
.array(VUserReservations)
.optional()
.describe('해당 유저의 예약 정보'),
lendings: z
.array(VlendingForSearchUser)
.optional()
.describe('해당 유저의 대출 정보'),
})
.merge(user);

const getUsersResponseMetaSchema = z.object({
totalItems: z.coerce.number().int().describe('전체 검색 결과 수'),
Expand All @@ -58,10 +94,12 @@ const getUsersResponseMetaSchema = z.object({
currentPage: z.coerce.number().int().describe('현재 페이지'),
});

export const getUsersResponseSchema = z.object({
items: z.array(getUsersResponseInnerSchema).describe('유저 정보 목록'),
meta: getUsersResponseMetaSchema.describe('유저 수와 관련된 정보'),
});
export const getUsersResponseSchema = z
.array(getUsersResponseInnerSchema)
.default([])
.describe('유저 정보 목록');

export const getUsersResponseArraySchema = z.array(getUsersResponseInnerSchema);

export const createUsersRequestSchema = z.object({
email: z.string().describe('이메일'),
Expand Down Expand Up @@ -109,7 +147,10 @@ export const getMyUserInfoResponseSchema = z.object({
example: [],
},
),
lendings: extendApi(z.array(lending).describe('해당 유저의 대출 정보'), {
example: [],
}),
lendings: extendApi(
z.array(VlendingForSearchUser).describe('해당 유저의 대출 정보'),
{
example: [],
},
),
});
Loading
Loading