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

[Feat] 프로젝트 관련 API 구현 #63

Merged
merged 11 commits into from
Nov 12, 2024
4 changes: 3 additions & 1 deletion apps/server/config/typeorm.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { TypeOrmModuleOptions, TypeOrmOptionsFactory } from '@nestjs/typeorm';
import { Task } from '@/task/domain/task.entity';
import { Section } from '@/task/domain/section.entity';
import { Account } from '@/account/entity/account.entity';
import { Project } from '@/project/entity/project.entity';
import { Contributor } from '@/project/entity/contributor.entity';

@Injectable()
export class TypeormConfig implements TypeOrmOptionsFactory {
Expand All @@ -17,7 +19,7 @@ export class TypeormConfig implements TypeOrmOptionsFactory {
username: this.configService.get<string>('DATABASE_USER'),
password: this.configService.get<string>('DATABASE_PASSWORD'),
database: this.configService.get<string>('DATABASE_NAME'),
entities: [Task, Section, Account],
entities: [Task, Section, Account, Project, Contributor],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Contributor가 팀원인가용

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

팀원이라고 보시면 됩니다. 초대 관련 여러가지 정보를 담고 있어요

synchronize: true,
ssl: {
rejectUnauthorized: false,
Expand Down
6 changes: 4 additions & 2 deletions apps/server/src/account/dto/create-user.dto.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { IsEmail, IsString, Length } from 'class-validator';
import { IsNotEmpty, IsString, Length } from 'class-validator';

export class CreateUserDto {
@IsEmail()
@IsNotEmpty()
@Length(8, 15)
username: string;

@IsNotEmpty()
@IsString()
@Length(8, 15)
password: string;
Expand Down
3 changes: 2 additions & 1 deletion apps/server/src/account/entity/account.entity.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
import { EntityTimestamp } from '@/common/entity-timestamp.entity';

@Entity()
export class Account {
export class Account extends EntityTimestamp {
@PrimaryGeneratedColumn()
id: number;

Expand Down
2 changes: 1 addition & 1 deletion apps/server/src/account/service/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export class AuthService {
async signUp(username: string, password: string) {
const account = await this.accountService.findByUsername(username);
if (account) {
throw new BadRequestException('Already used email');
throw new BadRequestException('Already used username');
}
const hash = await bcrypt.hash(password, 10);
const user = await this.accountService.create(username, hash);
Expand Down
2 changes: 2 additions & 0 deletions apps/server/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { AppController } from './app.controller';
import { HttpLoggingInterceptor } from './common/httpLog.Interceptor';
import { AllExceptionsFilter } from './common/allException.filter';
import { AccountModule } from './account/account.module';
import { ProjectModule } from './project/project.module';

@Module({
imports: [
Expand All @@ -24,6 +25,7 @@ import { AccountModule } from './account/account.module';
}),
TaskModule,
AccountModule,
ProjectModule,
],
controllers: [AppController],
providers: [
Expand Down
9 changes: 9 additions & 0 deletions apps/server/src/common/entity-timestamp.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { CreateDateColumn, UpdateDateColumn } from 'typeorm';

export class EntityTimestamp {
@CreateDateColumn()
createdAt: Date;

@UpdateDateColumn()
updatedAt: Date;
}
46 changes: 46 additions & 0 deletions apps/server/src/project/controller/project.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Body, Controller, Get, Param, Patch, Post, UseGuards } from '@nestjs/common';
import { ProjectService } from '../service/project.service';
import { AccessTokenGuard } from '@/account/guard/accessToken.guard';
import { CreateProjectRequest } from '../dto/create-project-request.dto';
import { AuthUser } from '@/account/decorator/authUser.decorator';
import { Account } from '@/account/entity/account.entity';
import { InviteUserRequest } from '../dto/invite-user-request.dto';
import { UpdateContributorRequest } from '../dto/update-contributor-request.dts';

@UseGuards(AccessTokenGuard)
@Controller('projects')
export class ProjectController {
constructor(private projectService: ProjectService) {}

@Get()
getProjects(@AuthUser() user: Account) {
return this.projectService.getUserProjects(user.id);
}

@Get(':id/members')
getContributors(@AuthUser() user: Account, @Param('id') projectId: number) {
return this.projectService.getContributors(user.id, projectId);
}

@Get('invitation')
getInvitations(@AuthUser() user: Account) {
return this.projectService.getInvitations(user.id);
}

@Post()
create(@AuthUser() user: Account, @Body() body: CreateProjectRequest) {
return this.projectService.create(user.id, body.title);
}

@Post('invitation')
async invite(@AuthUser() user: Account, @Body() body: InviteUserRequest) {
await this.projectService.invite(user.id, body.projectId, body.username);
return { message: 'Successfully invite user', success: true };
}

@Patch('invitation')
async updateInvitation(@AuthUser() user: Account, @Body() body: UpdateContributorRequest) {
await this.projectService.updateInvitation(user.id, body.contributorId, body.status);
return { message: 'Successfully update invitation', success: true };
}
}
8 changes: 8 additions & 0 deletions apps/server/src/project/dto/create-project-request.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { IsNotEmpty, IsString, Length } from 'class-validator';

export class CreateProjectRequest {
@IsNotEmpty()
@IsString()
@Length(1, 20)
title: string;
}
12 changes: 12 additions & 0 deletions apps/server/src/project/dto/create-project-response.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Project } from '../entity/project.entity';

export class CreateProjectResponse {
id: number;

title: string;

constructor(project: Project) {
this.id = project.id;
this.title = project.title;
}
}
12 changes: 12 additions & 0 deletions apps/server/src/project/dto/invite-user-request.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { IsNotEmpty, IsNumber, IsPositive, Length } from 'class-validator';

export class InviteUserRequest {
@IsNotEmpty()
@Length(8, 15)
username: string;

@IsNotEmpty()
@IsNumber()
@IsPositive()
projectId: number;
}
15 changes: 15 additions & 0 deletions apps/server/src/project/dto/project-contributors-response-dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { ContributorStatus } from '../enum/contributor-status.enum';

export class ProjectContributorsResponse {
id: number;

username: string;

role: ContributorStatus;

constructor(id: number, username: string, role: ContributorStatus) {
this.id = id;
this.username = username;
this.role = role;
}
}
16 changes: 16 additions & 0 deletions apps/server/src/project/dto/update-contributor-request.dts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { IsEnum, IsIn, IsNotEmpty, IsNumber, IsPositive } from 'class-validator';
import { ContributorStatus } from '../enum/contributor-status.enum';

export class UpdateContributorRequest {
@IsNotEmpty()
@IsNumber()
@IsPositive()
contributorId: number;

@IsNotEmpty()
@IsEnum(ContributorStatus)
@IsIn([ContributorStatus.ACCEPTED, ContributorStatus.REJECTED], {
message: 'Required ACCEPTED or REJECTED',
})
status: ContributorStatus;
}
16 changes: 16 additions & 0 deletions apps/server/src/project/dto/user-invitation-response.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export class UserInvitationResponse {
contributorId: number;

projectId: number;

projectTitle: string;

inviter: string;

constructor(contributorId: number, projectId: number, projectTitle: string, inviter: string) {
this.contributorId = contributorId;
this.projectId = projectId;
this.projectTitle = projectTitle;
this.inviter = inviter;
}
}
12 changes: 12 additions & 0 deletions apps/server/src/project/dto/user-projects-response.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { ContributorStatus } from '../enum/contributor-status.enum';

export class UserProjectsResponse {
role: ContributorStatus;

project: { id: number; title: string; createdAt: Date };

constructor(role: ContributorStatus, project: { id: number; title: string; createdAt: Date }) {
this.role = role;
this.project = project;
}
}
25 changes: 25 additions & 0 deletions apps/server/src/project/entity/contributor.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
import { ContributorStatus } from '../enum/contributor-status.enum';
import { ProjectRole } from '../enum/project-role.enum';
import { EntityTimestamp } from '@/common/entity-timestamp.entity';

@Entity()
export class Contributor extends EntityTimestamp {
@PrimaryGeneratedColumn()
id: number;

@Column()
userId: number;

@Column()
inviterId: number;

@Column()
projectId: number;

@Column({ type: 'enum', enum: ContributorStatus })
status: ContributorStatus;

@Column({ type: 'enum', enum: ProjectRole })
role: ProjectRole;
}
11 changes: 11 additions & 0 deletions apps/server/src/project/entity/project.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
import { EntityTimestamp } from '@/common/entity-timestamp.entity';

@Entity()
export class Project extends EntityTimestamp {
@PrimaryGeneratedColumn()
id: number;

@Column()
title: string;
}
5 changes: 5 additions & 0 deletions apps/server/src/project/enum/contributor-status.enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export enum ContributorStatus {
PENDING = 'PENDING',
ACCEPTED = 'ACCEPTED',
REJECTED = 'REJECTED',
}
4 changes: 4 additions & 0 deletions apps/server/src/project/enum/project-role.enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum ProjectRole {
ADMIN = 'ADMIN',
GUEST = 'GUEST',
}
14 changes: 14 additions & 0 deletions apps/server/src/project/project.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ProjectService } from './service/project.service';
import { ProjectController } from './controller/project.controller';
import { Project } from './entity/project.entity';
import { Contributor } from './entity/contributor.entity';
import { Account } from '@/account/entity/account.entity';

@Module({
imports: [TypeOrmModule.forFeature([Project, Contributor, Account])],
controllers: [ProjectController],
providers: [ProjectService],
})
export class ProjectModule {}
Loading
Loading