import { Body, Controller, Get, HttpStatus, Post, Query, Req, UseGuards, } from '@nestjs/common'; import { ApiBearerAuth, ApiOperation, ApiResponse, ApiTags, } from '@nestjs/swagger'; import jwt from 'jsonwebtoken'; import { AccessToken } from '../../common/token'; import { ErrorResponse } from '../../common/error/types/types'; import { FilesService } from './files.service'; import { AudioDownloadLocationRequest, AudioDownloadLocationResponse, AudioUploadFinishedRequest, AudioUploadFinishedResponse, AudioUploadLocationRequest, AudioUploadLocationResponse, TemplateDownloadLocationRequest, TemplateDownloadLocationResponse, TemplateUploadFinishedReqponse, TemplateUploadFinishedRequest, TemplateUploadLocationResponse, } from './types/types'; import { AuthGuard } from '../../common/guards/auth/authguards'; import { RoleGuard } from '../../common/guards/role/roleguards'; import { ADMIN_ROLES, USER_ROLES } from '../../constants'; import { retrieveAuthorizationToken } from '../../common/http/helper'; import { Request } from 'express'; import { makeContext } from '../../common/log'; @ApiTags('files') @Controller('files') export class FilesController { constructor(private readonly filesService: FilesService) {} @ApiResponse({ status: HttpStatus.OK, type: AudioUploadFinishedResponse, description: '成功時のレスポンス', }) @ApiResponse({ status: HttpStatus.BAD_REQUEST, description: '不正なパラメータ', type: ErrorResponse, }) @ApiResponse({ status: HttpStatus.UNAUTHORIZED, description: '認証エラー', type: ErrorResponse, }) @ApiResponse({ status: HttpStatus.INTERNAL_SERVER_ERROR, description: '想定外のサーバーエラー', type: ErrorResponse, }) @ApiOperation({ operationId: 'uploadFinished', description: 'アップロードが完了した音声ファイルの情報を登録し、文字起こしタスクを生成します', }) @ApiBearerAuth() @UseGuards(AuthGuard) @UseGuards(RoleGuard.requireds({ roles: [USER_ROLES.AUTHOR] })) @Post('audio/upload-finished') async uploadFinished( @Req() req: Request, @Body() body: AudioUploadFinishedRequest, ): Promise { const token = retrieveAuthorizationToken(req); const accessToken = jwt.decode(token, { json: true }) as AccessToken; const context = makeContext(accessToken.userId); const { url, authorId, fileName, duration, createdDate, finishedDate, uploadedDate, fileSize, priority, audioFormat, comment, workType, optionItemList, isEncrypted, } = body; const res = await this.filesService.uploadFinished( context, accessToken.userId, url, authorId, fileName, duration, createdDate, finishedDate, uploadedDate, fileSize, priority, audioFormat, comment, workType, optionItemList, isEncrypted, ); return { jobNumber: res.jobNumber }; } @Get('audio/upload-location') @ApiResponse({ status: HttpStatus.OK, type: AudioUploadLocationResponse, description: '成功時のレスポンス', }) @ApiResponse({ status: HttpStatus.UNAUTHORIZED, description: '認証エラー', type: ErrorResponse, }) @ApiResponse({ status: HttpStatus.INTERNAL_SERVER_ERROR, description: '想定外のサーバーエラー', type: ErrorResponse, }) @ApiOperation({ operationId: 'uploadLocation', description: 'ログイン中ユーザー用のBlob Storage上の音声ファイルのアップロード先アクセスURLを取得します', }) @ApiBearerAuth() @UseGuards(AuthGuard) @UseGuards(RoleGuard.requireds({ roles: [USER_ROLES.AUTHOR] })) async uploadLocation( @Req() req: Request, // クエリパラメータ AudioUploadLocationRequest は空であるため内部で使用しない。 // 使用しないことを宣言するために先頭にプレフィックス_(アンダースコア)をつけている // eslint-disable-next-line @typescript-eslint/no-unused-vars @Query() _query: AudioUploadLocationRequest, ): Promise { const token = retrieveAuthorizationToken(req); const accessToken = jwt.decode(token, { json: true }) as AccessToken; const context = makeContext(accessToken.userId); const url = await this.filesService.publishUploadSas(context, accessToken); return { url }; } @Get('audio/download-location') @ApiResponse({ status: HttpStatus.OK, type: AudioDownloadLocationResponse, description: '成功時のレスポンス', }) @ApiResponse({ status: HttpStatus.BAD_REQUEST, description: '不正なパラメータ', type: ErrorResponse, }) @ApiResponse({ status: HttpStatus.UNAUTHORIZED, description: '認証エラー', type: ErrorResponse, }) @ApiResponse({ status: HttpStatus.INTERNAL_SERVER_ERROR, description: '想定外のサーバーエラー', type: ErrorResponse, }) @ApiOperation({ operationId: 'downloadLocation', description: '指定した音声ファイルのBlob Storage上のダウンロード先アクセスURLを取得します', }) @ApiBearerAuth() @UseGuards(AuthGuard) @UseGuards( RoleGuard.requireds({ roles: [USER_ROLES.AUTHOR, USER_ROLES.TYPIST] }), ) async downloadLocation( @Req() req: Request, @Query() body: AudioDownloadLocationRequest, ): Promise { const { audioFileId } = body; const token = retrieveAuthorizationToken(req); const accessToken = jwt.decode(token, { json: true }) as AccessToken; const context = makeContext(accessToken.userId); const url = await this.filesService.publishAudioFileDownloadSas( context, accessToken.userId, audioFileId, ); return { url }; } @Get('template/download-location') @ApiResponse({ status: HttpStatus.OK, type: TemplateDownloadLocationResponse, description: '成功時のレスポンス', }) @ApiResponse({ status: HttpStatus.BAD_REQUEST, description: '不正なパラメータ', type: ErrorResponse, }) @ApiResponse({ status: HttpStatus.UNAUTHORIZED, description: '認証エラー', type: ErrorResponse, }) @ApiResponse({ status: HttpStatus.INTERNAL_SERVER_ERROR, description: '想定外のサーバーエラー', type: ErrorResponse, }) @ApiOperation({ operationId: 'downloadTemplateLocation', description: '指定した音声ファイルに対応したテンプレートファイルのBlob Storage上のダウンロード先アクセスURLを取得します', }) @ApiBearerAuth() @UseGuards(AuthGuard) @UseGuards( RoleGuard.requireds({ roles: [USER_ROLES.AUTHOR, USER_ROLES.TYPIST] }), ) async downloadTemplateLocation( @Req() req: Request, @Query() body: TemplateDownloadLocationRequest, ): Promise { const { audioFileId } = body; const token = retrieveAuthorizationToken(req); const accessToken = jwt.decode(token, { json: true }) as AccessToken; const context = makeContext(accessToken.userId); const url = await this.filesService.publishTemplateFileDownloadSas( context, accessToken.userId, audioFileId, ); return { url }; } @Get('template/upload-location') @ApiResponse({ status: HttpStatus.OK, type: TemplateUploadLocationResponse, description: '成功時のレスポンス', }) @ApiResponse({ status: HttpStatus.UNAUTHORIZED, description: '認証エラー', type: ErrorResponse, }) @ApiResponse({ status: HttpStatus.INTERNAL_SERVER_ERROR, description: '想定外のサーバーエラー', type: ErrorResponse, }) @ApiOperation({ operationId: 'uploadTemplateLocation', description: 'ログイン中ユーザー用のBlob Storage上のテンプレートファイルのアップロード先アクセスURLを取得します', }) @ApiBearerAuth() @UseGuards(AuthGuard) @UseGuards(RoleGuard.requireds({ roles: [ADMIN_ROLES.ADMIN] })) async uploadTemplateLocation( @Req() req: Request, ): Promise { const token = retrieveAuthorizationToken(req); const { userId } = jwt.decode(token, { json: true }) as AccessToken; const context = makeContext(userId); const url = await this.filesService.publishTemplateFileUploadSas( context, userId, ); return { url }; } @ApiResponse({ status: HttpStatus.OK, type: TemplateUploadFinishedReqponse, description: '成功時のレスポンス', }) @ApiResponse({ status: HttpStatus.BAD_REQUEST, description: '不正なパラメータ', type: ErrorResponse, }) @ApiResponse({ status: HttpStatus.UNAUTHORIZED, description: '認証エラー', type: ErrorResponse, }) @ApiResponse({ status: HttpStatus.INTERNAL_SERVER_ERROR, description: '想定外のサーバーエラー', type: ErrorResponse, }) @ApiOperation({ operationId: 'uploadTemplateFinished', description: 'アップロードが完了したテンプレートファイルの情報を登録します', }) @ApiBearerAuth() @UseGuards(AuthGuard) @UseGuards(RoleGuard.requireds({ roles: [ADMIN_ROLES.ADMIN] })) @Post('template/upload-finished') async templateUploadFinished( @Req() req: Request, @Body() body: TemplateUploadFinishedRequest, ): Promise { const { name, url } = body; const token = retrieveAuthorizationToken(req); const { userId } = jwt.decode(token, { json: true }) as AccessToken; const context = makeContext(userId); await this.filesService.templateUploadFinished(context, userId, url, name); return {}; } }