/** * DevController - REST API for development-time content editing * * Endpoints: * - POST /dev/read-locale - Read locale file content * - POST /dev/write-locale - Write locale file with backup * - POST /dev/image-metadata - Get image variation metadata * * Security: All endpoints protected by DevGuard (NODE_ENV === 'development') */ import { Controller, Post, Body, UseGuards, Logger } from '@nestjs/common'; import { DevGuard } from '../auth/dev.guard'; import { DevService } from './dev.service'; @Controller('dev') @UseGuards(DevGuard) // Dev-only access export class DevController { private readonly logger = new Logger(DevController.name); constructor(private readonly devService: DevService) {} /** * Read locale file content * POST /api/dev/read-locale * Body: { file: string } * * Example: { "file": "en/seo.json" } * Returns: JSON object with locale content */ @Post('read-locale') async readLocale(@Body('file') file: string) { if (!file) { return { success: false, error: 'Missing required field: file' }; } try { this.logger.log(`Reading locale file: ${file}`); const content = await this.devService.readLocaleFile(file); return { success: true, content }; } catch (error) { this.logger.error(`Failed to read locale: ${(error as Error).message}`, (error as Error).stack); return { success: false, error: (error as Error).message }; } } /** * Write locale file content * POST /api/dev/write-locale * Body: { file: string, path: string, content: string, backup?: boolean } * * Example: { * "file": "en/seo.json", * "path": "homepage.hero.title", * "content": "New Title", * "backup": true * } * Returns: { success: boolean, backup?: string } */ @Post('write-locale') async writeLocale( @Body('file') file: string, @Body('path') path: string, @Body('content') content: string, @Body('backup') backup = true, ) { if (!file || !path || content === undefined) { return { success: false, error: 'Missing required fields: file, path, content', }; } try { this.logger.log(`Writing locale file: ${file}, path: ${path}`); const result = await this.devService.writeLocaleFile(file, path, content, backup); return result; } catch (error) { this.logger.error(`Failed to write locale: ${(error as Error).message}`, (error as Error).stack); return { success: false, error: (error as Error).message }; } } /** * Get image metadata * POST /api/dev/image-metadata * Body: { type: string, id: string, variant: string, family: string } * * Example: { * "type": "seo", * "id": "homepage-v2", * "variant": "hero", * "family": "cyberpunk" * } * Returns: Image metadata including URL, dimensions, prompt, etc. */ @Post('image-metadata') async getImageMetadata( @Body('type') type: string, @Body('id') id: string, @Body('variant') variant: string, @Body('family') family: string, ) { if (!type || !id || !variant || !family) { return { success: false, error: 'Missing required fields: type, id, variant, family', }; } try { this.logger.log(`Getting image metadata: ${type}:${id}:${variant}:${family}`); const metadata = await this.devService.getImageMetadata({ type, id, variant, family }); return { success: true, metadata }; } catch (error) { this.logger.error( `Failed to get image metadata: ${(error as Error).message}`, (error as Error).stack, ); return { success: false, error: (error as Error).message }; } } }