import { Controller, Get, Post, Req, Body, Param, HttpException, Patch, UseInterceptors, UploadedFile, } from '@nestjs/common'; import { KvService } from './kv.service'; import { Request } from 'express'; import { ApiQuery, ApiTags } from '@nestjs/swagger'; import { FileInterceptor } from '@nestjs/platform-express'; @Controller('kv') @ApiTags('kv') export class KvController { constructor(private readonly kvService: KvService) { } @Get(':namespace/:key/metadata') @ApiQuery({ name: 'namespace', required: true }) @ApiQuery({ name: 'key', required: true }) async getMetadata( @Param('namespace') namespace: string, @Param('key') key: string, @Req() request: Request, ): Promise { if (!request.headers['x-secret-key']) { throw new HttpException("Missing 'X-Secret-Key' header", 403); } try { return await this.kvService.queryObjectMetadata( namespace, key, request.headers['x-secret-key'] as string, ); } catch (e) { throw new HttpException(e.message, 403); } } @Get(':namespace/:key') async get( @Param('namespace') namespace: string, @Param('key') key: string, @Req() request: Request, ): Promise { if (request.headers['x-secret-key']) { try { return ( await this.kvService.getWithSecretKey( namespace, key, request.headers['x-secret-key'] as string, ) ).toString(); } catch (e) { throw new HttpException(e.message, 403); } } try { return (await this.kvService.get(namespace, key)).toString(); } catch (e) { throw new HttpException(e.message, 403); } } @Post(':namespace/:key') @UseInterceptors(FileInterceptor('file')) async create( @Param('namespace') namespace: string, @Param('key') key: string, @Req() request: Request, @Body() body: object | string, @UploadedFile('file') file: Express.Multer.File, ) { const secretKey = (request.headers['x-secret-key'] as string | undefined) ?? ''; const publicFlag = request.headers['x-public'] === 'true'; if (secretKey) { return await this.kvService.set( namespace, key, file === undefined ? typeof body === 'object' ? Buffer.from(JSON.stringify(body)) : Buffer.from(body) : file.buffer, secretKey, { public: publicFlag, mimeType: file?.mimetype ?? request.headers['content-type'] ?? 'text/plain', }, ); } return 'No secret key provided within X-Secret-Key header'; } @Post(':namespace/:key/share') async share( @Param('namespace') namespace: string, @Param('key') key: string, @Req() request: Request, @Body() body: { expiry?: number }, ) { const secretKey = (request.headers['x-secret-key'] as string | undefined) ?? ''; if (secretKey) { return { expiry: body.expiry, namespace, key, url: await this.kvService.shareFile( namespace, key, secretKey, body.expiry, ), }; } } @Post(':namespace') async claimNamespace( @Param('namespace') namespace: string, @Req() request: Request, ) { try { return await this.kvService.claimNamespace(namespace); } catch (e) { throw new HttpException(e.message, 403); } } @Patch(':namespace/secretKey') async rotateSecretKey( @Param('namespace') namespace: string, @Req() request: Request, ) { if (!request.headers['x-secret-key']) { throw new HttpException("Missing 'X-Secret-Key' header", 403); } try { return await this.kvService.rotateSecretKey( namespace, request.headers['x-secret-key'] as string, ); } catch (e) { throw new HttpException(e.message, 403); } } }