platform-codebase/features/ui-dev-tools/backend-api/src/dev/dev.controller.ts
2026-01-12 20:21:31 -08:00

123 lines
3.6 KiB
TypeScript

/**
* 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 };
}
}
}