feat(settings): LLM config API with AES-GCM sealed key, masked reads

- Add GET/PUT /api/settings for encrypted LLM configuration
- SettingsResponse with masked key display
- SettingsUpdate validation with optional fields
- Defaults: llmBaseUrl "https://openrouter.ai/api/v1", llmModel "anthropic/claude-sonnet-4.5"
- Encrypted storage with AES-256-GCM for llm_key
- getSetting() export for future route groups
- Comprehensive test coverage: defaults, updates, masking, encryption at rest, validation

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
marcuspaico
2026-08-17 14:14:53 -07:00
parent 0b8e897e31
commit f33d395b71
4 changed files with 129 additions and 2 deletions

View File

@@ -6,3 +6,17 @@ export type HealthResponse = z.infer<typeof HealthResponse>;
export const PasswordBody = z.object({ password: z.string().min(8).max(200) });
export const MeResponse = z.object({ needsSetup: z.boolean(), authenticated: z.boolean() });
export type MeResponse = z.infer<typeof MeResponse>;
export const SettingsResponse = z.object({
llmBaseUrl: z.string(),
llmModel: z.string(),
llmKeyMasked: z.string().nullable(),
});
export type SettingsResponse = z.infer<typeof SettingsResponse>;
export const SettingsUpdate = z.object({
llmBaseUrl: z.string().url().optional(),
llmModel: z.string().min(1).optional(),
llmKey: z.string().min(1).optional(),
});
export type SettingsUpdate = z.infer<typeof SettingsUpdate>;