diff --git a/dictation_client/codegen.sh b/dictation_client/codegen.sh new file mode 100644 index 0000000..5d09ae3 --- /dev/null +++ b/dictation_client/codegen.sh @@ -0,0 +1,2 @@ +npx openapi-generator-cli version-manager set latest +npx openapi-generator-cli generate -g typescript-axios -i /app/dictation_server/src/api/odms/openapi.json -o /app/dictation_client/src/api/ diff --git a/dictation_client/openapitools.json b/dictation_client/openapitools.json index c871d87..0436938 100644 --- a/dictation_client/openapitools.json +++ b/dictation_client/openapitools.json @@ -2,6 +2,6 @@ "$schema": "./node_modules/@openapitools/openapi-generator-cli/config.schema.json", "spaces": 2, "generator-cli": { - "version": "6.2.1" + "version": "6.4.0" } } diff --git a/dictation_client/src/pages/SamplePage/index.tsx b/dictation_client/src/pages/SamplePage/index.tsx index b588b41..0721e10 100644 --- a/dictation_client/src/pages/SamplePage/index.tsx +++ b/dictation_client/src/pages/SamplePage/index.tsx @@ -1,7 +1,11 @@ import React from "react"; -const SamplePage: React.FC = () => { - return (
hello whorld!
Dictation App Service Site
) -}; +const SamplePage: React.FC = () => ( +
+ hello whorld! +
+ Dictation App Service Site +
+); export default SamplePage; diff --git a/dictation_server/src/api/generate.ts b/dictation_server/src/api/generate.ts index 3ab6410..676000c 100644 --- a/dictation_server/src/api/generate.ts +++ b/dictation_server/src/api/generate.ts @@ -21,6 +21,12 @@ async function bootstrap(): Promise { const options = new DocumentBuilder() .setTitle('ODMSOpenAPI') .setVersion('1.0.0') + .addBearerAuth({ + type: 'http', + scheme: 'bearer', + bearerFormat: 'JWT', + in: 'header', + }) .build(); const document = SwaggerModule.createDocument(app, options); await fs.writeFile( diff --git a/dictation_server/src/api/odms/openapi.json b/dictation_server/src/api/odms/openapi.json index d5c8e55..ce787bb 100644 --- a/dictation_server/src/api/odms/openapi.json +++ b/dictation_server/src/api/odms/openapi.json @@ -12,10 +12,109 @@ } } } + }, + "/auth/token": { + "post": { + "operationId": "token", + "summary": "", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TokenRequest" + } + } + } + }, + "responses": { + "200": { + "description": "成功時のレスポンス", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TokenResponse" + } + } + } + }, + "401": { + "description": "認証エラー", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "想定外のサーバーエラー", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "tags": [ + "auth" + ] + } + }, + "/auth/accessToken": { + "post": { + "operationId": "accessToken", + "summary": "", + "parameters": [], + "responses": { + "200": { + "description": "成功時のレスポンス", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccessTokenResponse" + } + } + } + }, + "401": { + "description": "認証エラー", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "想定外のサーバーエラー", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "tags": [ + "auth" + ], + "security": [ + { + "bearer": [] + } + ] + } } }, "info": { - "title": "OMDSOpenAPI", + "title": "ODMSOpenAPI", "description": "", "version": "1.0.0", "contact": {} @@ -23,6 +122,72 @@ "tags": [], "servers": [], "components": { - "schemas": {} + "securitySchemes": { + "bearer": { + "scheme": "bearer", + "bearerFormat": "JWT", + "type": "http", + "in": "header" + } + }, + "schemas": { + "TokenRequest": { + "type": "object", + "properties": { + "idToken": { + "type": "string" + }, + "type": { + "type": "string", + "description": "web or mobile or desktop" + } + }, + "required": [ + "idToken", + "type" + ] + }, + "TokenResponse": { + "type": "object", + "properties": { + "refreshToken": { + "type": "string" + }, + "accessToken": { + "type": "string" + } + }, + "required": [ + "refreshToken", + "accessToken" + ] + }, + "ErrorResponse": { + "type": "object", + "properties": { + "message": { + "type": "string" + }, + "code": { + "type": "string" + } + }, + "required": [ + "message", + "code" + ] + }, + "AccessTokenResponse": { + "type": "object", + "properties": { + "accessToken": { + "type": "string" + } + }, + "required": [ + "accessToken" + ] + } + } } } \ No newline at end of file diff --git a/dictation_server/src/app.module.ts b/dictation_server/src/app.module.ts index 4fea7f6..0106524 100644 --- a/dictation_server/src/app.module.ts +++ b/dictation_server/src/app.module.ts @@ -1,11 +1,11 @@ import { MiddlewareConsumer, Module } from '@nestjs/common'; import { HealthController } from './health.controller'; import { ServeStaticModule } from '@nestjs/serve-static'; -import { ConfigModule, ConfigService } from '@nestjs/config'; +import { ConfigModule } from '@nestjs/config'; import { join } from 'path'; -import { TypeOrmModule } from '@nestjs/typeorm'; import { LoggerMiddleware } from './common/loggerMiddleware'; import { AuthModule } from './features/auth/auth.module'; +import { AuthController } from './features/auth/auth.controller'; @Module({ imports: [ @@ -32,7 +32,7 @@ import { AuthModule } from './features/auth/auth.module'; // inject: [ConfigService], // }), ], - controllers: [HealthController], + controllers: [HealthController, AuthController], providers: [], }) export class AppModule { diff --git a/dictation_server/src/common/error/code.ts b/dictation_server/src/common/error/code.ts new file mode 100644 index 0000000..75bd4d4 --- /dev/null +++ b/dictation_server/src/common/error/code.ts @@ -0,0 +1,4 @@ +//TODO 仮のエラーコード作成 +export const ErrorCodes = [ + 'E009999', // 汎用エラー +] as const; diff --git a/dictation_server/src/common/error/makeErrorResponse.ts b/dictation_server/src/common/error/makeErrorResponse.ts new file mode 100644 index 0000000..0a677b4 --- /dev/null +++ b/dictation_server/src/common/error/makeErrorResponse.ts @@ -0,0 +1,10 @@ +import { errors } from './message'; +import { ErrorCodeType, ErrorResponse } from './types/types'; + +export const makeErrorResponse = (errorcode: ErrorCodeType): ErrorResponse => { + const msg = errors[errorcode]; + return { + code: errorcode, + message: msg, + }; +}; diff --git a/dictation_server/src/common/error/message.ts b/dictation_server/src/common/error/message.ts new file mode 100644 index 0000000..e2cbc14 --- /dev/null +++ b/dictation_server/src/common/error/message.ts @@ -0,0 +1,6 @@ +import { Errors } from './types/types'; + +// エラーコードとメッセージ対応表 +export const errors: Errors = { + E009999: 'Internal Server Error', +}; diff --git a/dictation_server/src/common/error/types/types.ts b/dictation_server/src/common/error/types/types.ts new file mode 100644 index 0000000..8746924 --- /dev/null +++ b/dictation_server/src/common/error/types/types.ts @@ -0,0 +1,15 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { ErrorCodes } from '../code'; + +export class ErrorResponse { + @ApiProperty() + message: string; + @ApiProperty() + code: string; +} + +export type ErrorCodeType = (typeof ErrorCodes)[number]; + +export type Errors = { + [P in ErrorCodeType]: string; +}; diff --git a/dictation_server/src/features/auth/auth.controller.ts b/dictation_server/src/features/auth/auth.controller.ts index 268eeb2..bbc1f5a 100644 --- a/dictation_server/src/features/auth/auth.controller.ts +++ b/dictation_server/src/features/auth/auth.controller.ts @@ -1,4 +1,66 @@ -import { Controller } from '@nestjs/common'; - +import { Body, Controller, Headers, HttpStatus, Post } from '@nestjs/common'; +import { + ApiResponse, + ApiOperation, + ApiBearerAuth, + ApiTags, +} from '@nestjs/swagger'; +import { ErrorResponse } from '../../common/error/types/types'; +import { + AccessTokenResponse, + TokenRequest, + TokenResponse, +} from './types/types'; +@ApiTags('auth') @Controller('auth') -export class AuthController {} +export class AuthController { + @Post('token') + @ApiResponse({ + status: HttpStatus.OK, + type: TokenResponse, + description: '成功時のレスポンス', + }) + @ApiResponse({ + status: HttpStatus.UNAUTHORIZED, + description: '認証エラー', + type: ErrorResponse, + }) + @ApiResponse({ + status: HttpStatus.INTERNAL_SERVER_ERROR, + description: '想定外のサーバーエラー', + type: ErrorResponse, + }) + @ApiOperation({ operationId: 'token' }) + async token(@Body() body: TokenRequest): Promise { + console.log(body); + + return { + accessToken: '', + refreshToken: '', + }; + } + + @Post('accessToken') + @ApiBearerAuth() + @ApiResponse({ + status: HttpStatus.OK, + type: AccessTokenResponse, + description: '成功時のレスポンス', + }) + @ApiResponse({ + status: HttpStatus.UNAUTHORIZED, + description: '認証エラー', + type: ErrorResponse, + }) + @ApiResponse({ + status: HttpStatus.INTERNAL_SERVER_ERROR, + description: '想定外のサーバーエラー', + type: ErrorResponse, + }) + @ApiOperation({ operationId: 'accessToken' }) + async accessToken(@Headers() headers): Promise { + console.log(headers['authorization']); + + return { accessToken: '' }; + } +} diff --git a/dictation_server/src/features/auth/types/types.ts b/dictation_server/src/features/auth/types/types.ts new file mode 100644 index 0000000..95f41a3 --- /dev/null +++ b/dictation_server/src/features/auth/types/types.ts @@ -0,0 +1,19 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class TokenRequest { + @ApiProperty() + idToken: string; + @ApiProperty({ description: 'web or mobile or desktop' }) + type: string; +} +export class TokenResponse { + @ApiProperty() + refreshToken: string; + @ApiProperty() + accessToken: string; +} +export class AccessTokenResponse { + @ApiProperty() + accessToken: string; +} +export class AccessTokenRequest {} diff --git a/dictation_server/src/main.ts b/dictation_server/src/main.ts index 9a2faeb..c9e3be2 100644 --- a/dictation_server/src/main.ts +++ b/dictation_server/src/main.ts @@ -18,6 +18,12 @@ async function bootstrap() { const options = new DocumentBuilder() .setTitle('ODMSOpenAPI') .setVersion('1.0.0') + .addBearerAuth({ + type: 'http', + scheme: 'bearer', + bearerFormat: 'JWT', + in: 'header', + }) .build(); const document = SwaggerModule.createDocument(app, options); SwaggerModule.setup('api', app, document);