diff --git a/dictation_server/src/api/odms/openapi.json b/dictation_server/src/api/odms/openapi.json index df4a888..653ce1d 100644 --- a/dictation_server/src/api/odms/openapi.json +++ b/dictation_server/src/api/odms/openapi.json @@ -1417,6 +1417,61 @@ "security": [{ "bearer": [] }] } }, + "/accounts/me/file-delete-setting": { + "post": { + "operationId": "updateFileDeleteSetting", + "summary": "", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateFileDeleteSettingRequest" + } + } + } + }, + "responses": { + "200": { + "description": "成功時のレスポンス", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateFileDeleteSettingResponse" + } + } + } + }, + "400": { + "description": "パラメータ不正/アカウント・ユーザー不在", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/ErrorResponse" } + } + } + }, + "401": { + "description": "認証エラー", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/ErrorResponse" } + } + } + }, + "500": { + "description": "想定外のサーバーエラー", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/ErrorResponse" } + } + } + } + }, + "tags": ["accounts"], + "security": [{ "bearer": [] }] + } + }, "/accounts/delete": { "post": { "operationId": "deleteAccountAndData", @@ -4203,6 +4258,21 @@ "required": ["delegationPermission", "primaryAdminUserId"] }, "UpdateAccountInfoResponse": { "type": "object", "properties": {} }, + "UpdateFileDeleteSettingRequest": { + "type": "object", + "properties": { + "autoFileDelete": { + "type": "boolean", + "description": "自動ファイル削除をするかどうか" + }, + "retentionDays": { + "type": "number", + "description": "文字起こし完了してから自動ファイル削除されるまでのファイルの保存期間" + } + }, + "required": ["autoFileDelete", "retentionDays"] + }, + "UpdateFileDeleteSettingResponse": { "type": "object", "properties": {} }, "DeleteAccountRequest": { "type": "object", "properties": { diff --git a/dictation_server/src/features/accounts/accounts.controller.ts b/dictation_server/src/features/accounts/accounts.controller.ts index 7ab72b5..c75d786 100644 --- a/dictation_server/src/features/accounts/accounts.controller.ts +++ b/dictation_server/src/features/accounts/accounts.controller.ts @@ -73,6 +73,8 @@ import { GetCompanyNameResponse, DeleteTypistGroupRequestParam, DeleteTypistGroupResponse, + UpdateFileDeleteSettingRequest, + UpdateFileDeleteSettingResponse, } from './types/types'; import { USER_ROLES, ADMIN_ROLES, TIERS } from '../../constants'; import { AuthGuard } from '../../common/guards/auth/authguards'; @@ -1961,6 +1963,81 @@ export class AccountsController { return {}; } + @Post('me/file-delete-setting') + @ApiResponse({ + status: HttpStatus.OK, + type: UpdateFileDeleteSettingResponse, + 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: 'updateFileDeleteSetting' }) + @ApiBearerAuth() + @UseGuards(AuthGuard) + @UseGuards( + RoleGuard.requireds({ + roles: [ADMIN_ROLES.ADMIN], + }), + ) + async updateFileDeleteSetting( + @Req() req: Request, + @Body() body: UpdateFileDeleteSettingRequest, + ): Promise { + const { autoFileDelete, retentionDays } = body; + + const accessToken = retrieveAuthorizationToken(req); + if (!accessToken) { + throw new HttpException( + makeErrorResponse('E000107'), + HttpStatus.UNAUTHORIZED, + ); + } + + const ip = retrieveIp(req); + if (!ip) { + throw new HttpException( + makeErrorResponse('E000401'), + HttpStatus.UNAUTHORIZED, + ); + } + + const requestId = retrieveRequestId(req); + if (!requestId) { + throw new HttpException( + makeErrorResponse('E000501'), + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + const decodedAccessToken = jwt.decode(accessToken, { json: true }); + if (!decodedAccessToken) { + throw new HttpException( + makeErrorResponse('E000101'), + HttpStatus.UNAUTHORIZED, + ); + } + const { userId } = decodedAccessToken as AccessToken; + + const context = makeContext(userId, requestId); + this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`); + + // TODO:Service層呼び出し + + return {}; + } + @Post('/delete') @ApiResponse({ status: HttpStatus.OK, diff --git a/dictation_server/src/features/accounts/types/types.ts b/dictation_server/src/features/accounts/types/types.ts index d604419..2600295 100644 --- a/dictation_server/src/features/accounts/types/types.ts +++ b/dictation_server/src/features/accounts/types/types.ts @@ -12,6 +12,7 @@ import { IsIn, ArrayMaxSize, ValidateNested, + Max, } from 'class-validator'; import { IsAdminPasswordvalid } from '../../../common/validators/admin.validator'; import { IsUnique } from '../../../common/validators/IsUnique.validator'; @@ -647,6 +648,24 @@ export class GetAccountInfoMinimalAccessResponse { tier: number; } +export class UpdateFileDeleteSettingRequest { + @ApiProperty({ description: '自動ファイル削除をするかどうか' }) + @Type(() => Boolean) + autoFileDelete: boolean; + + @ApiProperty({ + description: + '文字起こし完了してから自動ファイル削除されるまでのファイルの保存期間', + }) + @Type(() => Number) + @IsInt() + @Min(1) + @Max(999) + retentionDays: number; +} + +export class UpdateFileDeleteSettingResponse {} + // ============================== // Request/Response外の型 // TODO: Request/Response/その他の型を別ファイルに分ける