Skip to content

Commit

Permalink
Merge pull request #9 from siwonpada/master
Browse files Browse the repository at this point in the history
add reactions and upload documents
  • Loading branch information
siwonpada authored Jan 13, 2024
2 parents 3a37000 + bca4328 commit d3c933c
Show file tree
Hide file tree
Showing 11 changed files with 351 additions and 11 deletions.
2 changes: 2 additions & 0 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { TagModule } from './tag/tag.module';
import { AppController } from './app.controller';
import { ImageModule } from './image/image.module';
import { ScheduleModule } from '@nestjs/schedule';
import { DocumentModule } from './document/document.module';

@Module({
imports: [
Expand All @@ -19,6 +20,7 @@ import { ScheduleModule } from '@nestjs/schedule';
TagModule,
ImageModule,
ScheduleModule.forRoot(),
DocumentModule,
],
controllers: [AppController],
})
Expand Down
41 changes: 41 additions & 0 deletions src/document/document.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import {
Controller,
Post,
UploadedFiles,
UseInterceptors,
} from '@nestjs/common';
import { ApiBody, ApiConsumes, ApiResponse, ApiTags } from '@nestjs/swagger';
import { DocumentService } from './document.service';
import { FilesInterceptor } from '@nestjs/platform-express';

@ApiTags('document')
@Controller('document')
export class DocumentController {
constructor(private readonly documentService: DocumentService) {}

@ApiBody({
required: true,
type: 'multipart/form-data',
schema: {
type: 'object',
properties: {
documents: {
type: 'array',
items: {
type: 'string',
format: 'binary',
},
},
},
},
})
@ApiConsumes('multipart/form-data')
@ApiResponse({ type: [String], status: 201 })
@Post('upload')
@UseInterceptors(FilesInterceptor('documents'))
async uploadDocument(
@UploadedFiles() files: Express.Multer.File[],
): Promise<string[]> {
return this.documentService.uploadDocuments(files);
}
}
10 changes: 10 additions & 0 deletions src/document/document.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Module } from '@nestjs/common';
import { DocumentController } from './document.controller';
import { DocumentService } from './document.service';

@Module({
controllers: [DocumentController],
providers: [DocumentService],
exports: [DocumentService],
})
export class DocumentModule {}
121 changes: 121 additions & 0 deletions src/document/document.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import {
DeleteObjectCommand,
PutObjectCommand,
PutObjectTaggingCommand,
S3Client,
} from '@aws-sdk/client-s3';
import {
BadRequestException,
Injectable,
InternalServerErrorException,
Logger,
} from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import path from 'path';

@Injectable()
export class DocumentService {
private readonly logger = new Logger(DocumentService.name);
constructor(private readonly configService: ConfigService) {}

async uploadDocuments(files: Express.Multer.File[]): Promise<string[]> {
if (!files) {
throw new BadRequestException('No documents sent');
}
return Promise.all(files.map((file) => this.uploadDocument(file)));
}

async validateDocuments(documentKeys: string[]): Promise<void> {
if (!documentKeys) {
throw new BadRequestException('No documents sent');
}

await Promise.all(
documentKeys.map((documentKey) =>
this.validateUploadedDocument(documentKey),
),
);
}

async deleteDocuments(documentKeys: string[]): Promise<void> {
if (!documentKeys) {
return;
}

await Promise.all(
documentKeys.map((documentKey) => this.deleteDocument(documentKey)),
);
}

private async uploadDocument(file: Express.Multer.File): Promise<string> {
const s3 = new S3Client({
region: this.configService.get<string>('AWS_S3_REGION'),
});
const key = `${new Date().toISOString()}-${Math.random()
.toString(36)
.substring(2)}.${path.extname(file.originalname)}`;

const command = new PutObjectCommand({
Bucket: this.configService.get<string>('AWS_S3_BUCKET_NAME'),
Key: key,
Body: file.buffer,
Tagging: 'expiration=true',
Metadata: {
originalName: file.originalname,
},
});

try {
await s3.send(command);
return key;
} catch (error) {
this.logger.error('error uploading document');
this.logger.debug(error);
throw new InternalServerErrorException("Couldn't uploading document");
}
}

private async validateUploadedDocument(documentKey: string): Promise<void> {
const s3 = new S3Client({
region: this.configService.get<string>('AWS_S3_REGION'),
});
const command = new PutObjectTaggingCommand({
Bucket: this.configService.get<string>('AWS_S3_BUCKET_NAME'),
Key: documentKey,
Tagging: {
TagSet: [
{
Key: 'expiration',
Value: 'false',
},
],
},
});

try {
await s3.send(command);
} catch (error) {
this.logger.error('error validating document');
this.logger.debug(error);
throw new BadRequestException('Invalid document');
}
}

private async deleteDocument(documentKey: string): Promise<void> {
const s3 = new S3Client({
region: this.configService.get<string>('AWS_S3_REGION'),
});
const command = new DeleteObjectCommand({
Bucket: this.configService.get<string>('AWS_S3_BUCKET_NAME'),
Key: documentKey,
});

try {
await s3.send(command);
} catch (error) {
this.logger.error('error deleting document');
this.logger.debug(error);
throw new InternalServerErrorException("Couldn't delete document");
}
}
}
9 changes: 9 additions & 0 deletions src/notice/dto/createNotice.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,13 @@ export class CreateNoticeDto {
@IsString({ each: true })
@IsOptional()
images: string[] = [];

@ApiProperty({
example: 'wow.docx',
description: '파일 이름',
required: false,
})
@IsString({ each: true })
@IsOptional()
documents: string[] = [];
}
12 changes: 12 additions & 0 deletions src/notice/dto/reaction.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsString } from 'class-validator';

export class ReactionDto {
@ApiProperty({
example: '👍',
description: '반응할 이모지',
required: true,
})
@IsString()
emoji: string;
}
22 changes: 22 additions & 0 deletions src/notice/notice.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { ForeignContentDto } from './dto/foreignContent.dto';
import { ApiTags } from '@nestjs/swagger';
import { GetNoticeDto } from './dto/getNotice.dto';
import { UpdateNoticeDto } from './dto/updateNotice.dto';
import { ReactionDto } from './dto/reaction.dto';

@ApiTags('notice')
@Controller('notice')
Expand Down Expand Up @@ -107,6 +108,17 @@ export class NoticeController {
return this.noticeService.addNoticeReminder(id, user?.uuid);
}

/* notice reaction 추가 */
@Post(':id/reaction')
@UseGuards(IdPGuard)
async addNoticeReaction(
@GetUser() user: User,
@Param('id') id: number,
@Body() body: ReactionDto,
) {
return this.noticeService.addNoticeReaction(id, body, user?.uuid);
}

/* notice 수정은 작성자만 가능, 15분 이내에만 가능 */
@Patch(':id')
@UseGuards(IdPGuard)
Expand All @@ -124,6 +136,16 @@ export class NoticeController {
return this.noticeService.removeNoticeReminder(id, user?.uuid);
}

@Delete(':id/reaction')
@UseGuards(IdPGuard)
async deleteNoticeReaction(
@GetUser() user: User,
@Param('id') id: number,
@Body() body: ReactionDto,
) {
return this.noticeService.removeNoticeReaction(id, body, user?.uuid);
}

/* notice 삭제는 작성자만 가능 */
@Delete(':id')
@UseGuards(IdPGuard)
Expand Down
2 changes: 2 additions & 0 deletions src/notice/notice.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ import { UserModule } from 'src/user/user.module';
import { NoticeController } from './notice.controller';
import { NoticeRepository } from './notice.repository';
import { NoticeService } from './notice.service';
import { DocumentModule } from 'src/document/document.module';

@Module({
imports: [
ConfigModule,
UserModule,
ImageModule,
DocumentModule,
FcmModule,
PrismaModule,
HttpModule,
Expand Down
Loading

0 comments on commit d3c933c

Please sign in to comment.