import { Body, Controller, Delete, Path, Post, Res, Route, Security, SuccessResponse, TsoaResponse, } from 'tsoa'; import { getCustomRepository } from 'typeorm'; import { User } from '@/entities'; import { ApiClientRepository } from '@/repositories'; import { TooManyAPIClientsError } from '@/repositories/ApiClientRepository/types'; import { HttpStatusCodes } from '@/routes/types'; import { UserRole } from '@/types'; import { ApiKeyPayload, GenerateApiKeyInput } from './types'; // @NOTE: We're inlining the request and response types here // because tsoa's automagic swagger generator only hides them // when we do so. @Security('basic', [UserRole.ADMIN]) @Route('api-keys') export class ApiClientController extends Controller { @SuccessResponse(HttpStatusCodes.CREATED) @Post() async generateApiKey( @Body() requestBody: GenerateApiKeyInput, @Res() notFoundResponse: TsoaResponse< HttpStatusCodes.NOT_FOUND, { reason: string } >, @Res() alreadyExistsResponse: TsoaResponse< HttpStatusCodes.CONFLICT, { reason: string } > ): Promise { const user = await User.findOne({ username: requestBody.username }); if (!user) { return notFoundResponse(HttpStatusCodes.NOT_FOUND, { reason: 'User not found.', }); } try { const apiKey = await getCustomRepository( ApiClientRepository ).generateApiKey({ user, }); this.setStatus(HttpStatusCodes.CREATED); return { apiKey }; } catch (error) { if (error instanceof TooManyAPIClientsError) { return alreadyExistsResponse(HttpStatusCodes.CONFLICT, { reason: error.message, }); } } } @SuccessResponse(HttpStatusCodes.NO_CONTENT) @Delete('{username}') async revokeApiKey( @Path() username: string, @Res() notFoundResponse: TsoaResponse< HttpStatusCodes.NOT_FOUND, { reason: string } >, @Res() conflictResponse: TsoaResponse ): Promise { const user = await User.findOne({ username }); if (!user) { return notFoundResponse(HttpStatusCodes.NOT_FOUND, { reason: 'User not found.', }); } try { await getCustomRepository(ApiClientRepository).revokeApiKey({ user }); } catch (error) { if (error instanceof TooManyAPIClientsError) { return conflictResponse(HttpStatusCodes.CONFLICT, { reason: error.message, }); } } this.setStatus(HttpStatusCodes.NO_CONTENT); } }