managarten/maerchenzauber/apps/backend/src/settings/settings.controller.ts
Till-JS e7f5f942f3 chore: initial commit - consolidate 4 projects into monorepo
Projects included:
- maerchenzauber (NestJS backend + Expo mobile + SvelteKit web + Astro landing)
- manacore (Expo mobile + SvelteKit web + Astro landing)
- manadeck (NestJS backend + Expo mobile + SvelteKit web)
- memoro (Expo mobile + SvelteKit web + Astro landing)

This commit preserves the current state before monorepo restructuring.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-22 23:38:24 +01:00

278 lines
6.7 KiB
TypeScript

import {
Controller,
Get,
Put,
Body,
UseGuards,
UsePipes,
ValidationPipe,
Param,
} from '@nestjs/common';
import { AuthGuard, CurrentUser } from '@mana-core/nestjs-integration';
import { UserToken } from '../decorators/user.decorator';
import { SettingsClientService, UserSettings } from './settings-client.service';
import { SettingsService } from '../core/services/settings.service';
import { JwtPayload } from '../types/jwt-payload.interface';
import { UpdateUserSettingsDto } from './dto/settings.dto';
import { ImageModelId } from '../core/models/image-models';
@Controller('settings')
@UseGuards(AuthGuard)
export class SettingsController {
constructor(
private readonly settingsClientService: SettingsClientService,
private readonly settingsService: SettingsService,
) {}
/**
* Get user-specific settings
*/
@Get('user')
async getUserSettings(
@CurrentUser() user: JwtPayload,
@UserToken() token: string,
): Promise<{ userId: string; settings: UserSettings }> {
const settings = await this.settingsClientService.getUserSettings(token);
return {
userId: user.sub,
settings: settings || {
// Default settings if none exist
preferences: {
language: 'en',
timezone: 'UTC',
dateFormat: 'MM/DD/YYYY',
},
maerchenzauber: {
language: 'de',
storyLength: 'medium',
ageGroup: '4-6',
},
},
};
}
/**
* Get available image generation models
*/
@Get('image-models')
async getAvailableImageModels() {
const models = this.settingsService.getAvailableImageModels();
return {
models,
default: 'imagen-4-fast',
categories: {
budget: models.filter((m) => m.category === 'budget'),
standard: models.filter((m) => m.category === 'standard'),
premium: models.filter((m) => m.category === 'premium'),
specialty: models.filter((m) => m.category === 'specialty'),
},
};
}
/**
* Get user's selected image model
*/
@Get('user/image-model')
async getUserImageModel(@CurrentUser() user: JwtPayload) {
const result = await this.settingsService.getUserImageModel(user.sub);
if (result.error) {
return {
model: 'imagen-4-fast',
error: result.error.message,
};
}
return {
model: result.data,
modelInfo: this.settingsService.getImageModelInfo(result.data!),
};
}
/**
* Update user's selected image model
*/
@Put('user/image-model')
async updateUserImageModel(
@CurrentUser() user: JwtPayload,
@Body('model') modelId: ImageModelId,
) {
const result = await this.settingsService.setUserImageModel(
user.sub,
modelId,
);
if (result.error) {
return {
success: false,
error: result.error.message,
};
}
return {
success: true,
model: modelId,
modelInfo: this.settingsService.getImageModelInfo(modelId),
};
}
/**
* Update user-specific settings
*/
@Put('user')
@UsePipes(ValidationPipe)
async updateUserSettings(
@CurrentUser() user: JwtPayload,
@UserToken() token: string,
@Body() settings: UpdateUserSettingsDto,
) {
await this.settingsClientService.updateServiceSettings(
settings as any,
token,
);
return {
success: true,
userId: user.sub,
settings,
};
}
/**
* Get app-wide settings (public)
*/
@Get('app')
async getAppSettings() {
const settings = this.settingsService.getAllSettings();
return {
app: 'storyteller',
version: '1.0.0',
settings: {
features: {
aiStoryGeneration: true,
characterCreation: true,
imageGeneration: true,
collaborativeStories: false, // Coming soon
voiceNarration: false, // Coming soon
},
limits: {
maxCharactersPerUser: 50,
maxStoriesPerUser: 100,
maxPagesPerStory: 20,
maxImageGenerationsPerDay: 10,
},
costs: {
characterCreation: 5,
storyGeneration: 10,
imageGeneration: 3,
voiceNarration: 15,
},
...settings,
},
};
}
/**
* Get feature flags
*/
@Get('features')
async getFeatureFlags(@CurrentUser() user: JwtPayload) {
// Check user-specific feature flags
const userRole = user.role || 'user';
return {
userId: user.sub,
role: userRole,
features: {
// Core features
createCharacter: true,
createStory: true,
generateImages: true,
// Premium features (role-based)
unlimitedStories: userRole === 'premium' || userRole === 'admin',
voiceNarration: userRole === 'premium' || userRole === 'admin',
collaborativeMode: false, // Not implemented yet
// Beta features
betaFeatures: userRole === 'admin',
debugMode: userRole === 'admin',
},
};
}
/**
* Get creators list (for story creation)
*/
@Get('creators')
async getCreators() {
const creators = this.settingsService.getCreators();
const authors = creators.filter((c) => c.type === 'author');
const illustrators = creators.filter((c) => c.type === 'illustrator');
return {
authors,
illustrators,
total: creators.length,
};
}
/**
* Get creators by type
*/
@Get('creators/:type')
async getCreatorsByType(@Param('type') type: 'author' | 'illustrator') {
if (type !== 'author' && type !== 'illustrator') {
return {
error: 'Invalid creator type. Must be "author" or "illustrator"',
};
}
const creators = this.settingsService.getCreatorsByType(type);
return {
creators,
type,
count: creators.length,
};
}
/**
* Get supported languages
*/
@Get('languages')
async getSupportedLanguages() {
return {
languages: [
{ code: 'en', name: 'English', flag: '🇺🇸' },
{ code: 'de', name: 'Deutsch', flag: '🇩🇪' },
{ code: 'es', name: 'Español', flag: '🇪🇸' },
{ code: 'fr', name: 'Français', flag: '🇫🇷' },
{ code: 'it', name: 'Italiano', flag: '🇮🇹' },
],
default: 'en',
};
}
/**
* Get theme options
*/
@Get('themes')
async getThemeOptions() {
return {
themes: [
{
id: 'light',
name: 'Light Mode',
description: 'Default light theme',
},
{
id: 'dark',
name: 'Dark Mode',
description: 'Easy on the eyes',
},
{
id: 'kids',
name: 'Kids Mode',
description: 'Colorful and playful',
},
],
default: 'light',
};
}
}