| import { NextResponse } from 'next/server'; |
| import { z } from 'zod'; |
| import { authenticateRequest } from '@/lib/auth-middleware'; |
| import { getUserSettings, upsertUserSettings } from '@/lib/user-settings'; |
| import { auditLog } from '@/lib/audit'; |
| import { getClientIp } from '@/lib/rate-limit'; |
| import { redact } from '@/lib/crypto'; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| export const runtime = 'nodejs'; |
|
|
| export async function GET(req: Request) { |
| const user = authenticateRequest(req); |
| if (!user) { |
| return NextResponse.json({ error: 'Not authenticated' }, { status: 401 }); |
| } |
|
|
| const s = getUserSettings(user.id); |
|
|
| return NextResponse.json({ |
| settings: { |
| language: s.language ?? null, |
| country: s.country ?? null, |
| units: s.units ?? null, |
| defaultModel: s.defaultModel ?? null, |
| theme: s.theme ?? null, |
| ehr: s.ehr ?? {}, |
| hfTokenRedacted: s.hfToken ? redact(s.hfToken) : null, |
| hasHfToken: !!s.hfToken, |
| }, |
| }); |
| } |
|
|
| const PutSchema = z.object({ |
| language: z.string().min(2).max(8).optional(), |
| country: z.string().min(2).max(4).optional(), |
| units: z.enum(['metric', 'imperial']).optional(), |
| defaultModel: z.string().max(100).optional(), |
| theme: z.enum(['light', 'dark', 'auto']).optional(), |
| |
| ehr: z.record(z.any()).optional(), |
| |
| hfToken: z.string().max(200).optional(), |
| }); |
|
|
| export async function PUT(req: Request) { |
| const user = authenticateRequest(req); |
| if (!user) { |
| return NextResponse.json({ error: 'Not authenticated' }, { status: 401 }); |
| } |
|
|
| let parsed; |
| try { |
| const body = await req.json(); |
| parsed = PutSchema.parse(body); |
| } catch (error: any) { |
| if (error instanceof z.ZodError) { |
| return NextResponse.json( |
| { error: 'Invalid input', details: error.errors }, |
| { status: 400 }, |
| ); |
| } |
| return NextResponse.json({ error: 'Invalid request body' }, { status: 400 }); |
| } |
|
|
| |
| if (parsed.ehr && JSON.stringify(parsed.ehr).length > 32_000) { |
| return NextResponse.json( |
| { error: 'EHR payload too large (max 32 KB).' }, |
| { status: 413 }, |
| ); |
| } |
|
|
| try { |
| upsertUserSettings(user.id, parsed); |
| } catch (error: any) { |
| console.error('[User Settings PUT]', error?.message); |
| return NextResponse.json({ error: 'Save failed' }, { status: 500 }); |
| } |
|
|
| auditLog({ |
| userId: user.id, |
| action: 'settings_update', |
| ip: getClientIp(req), |
| meta: { |
| fields: Object.keys(parsed), |
| tokenRotated: parsed.hfToken !== undefined, |
| ehrFieldsChanged: parsed.ehr ? Object.keys(parsed.ehr) : [], |
| }, |
| }); |
|
|
| |
| const s = getUserSettings(user.id); |
| return NextResponse.json({ |
| success: true, |
| settings: { |
| language: s.language ?? null, |
| country: s.country ?? null, |
| units: s.units ?? null, |
| defaultModel: s.defaultModel ?? null, |
| theme: s.theme ?? null, |
| ehr: s.ehr ?? {}, |
| hfTokenRedacted: s.hfToken ? redact(s.hfToken) : null, |
| hasHfToken: !!s.hfToken, |
| }, |
| }); |
| } |
|
|