← back to Norma
app/api/user-preferences/route.ts
78 lines
/**
* GET/PUT /api/user-preferences?key=<prefKey>
* Per-user free-form JSONB preferences (e.g. panel layout, column count).
* Keyed by the authenticated session username.
*/
import { NextRequest, NextResponse } from 'next/server';
import { requireRole } from '@/lib/require-role';
import { query } from '@/lib/db';
export async function GET(request: NextRequest) {
const auth = requireRole(request, 'admin', 'staff', 'pulse');
if (auth instanceof NextResponse) return auth;
const key = request.nextUrl.searchParams.get('key');
if (!key) {
return NextResponse.json({ error: 'key query param required' }, { status: 400 });
}
try {
const { rows } = await query(
`SELECT pref_value, updated_at
FROM user_preferences
WHERE username = $1 AND pref_key = $2`,
[auth.username, key],
);
if (rows.length === 0) {
return NextResponse.json({ value: null, updatedAt: null });
}
return NextResponse.json({
value: rows[0].pref_value,
updatedAt: rows[0].updated_at,
});
} catch (err) {
console.error('[user-preferences] GET error:', (err as Error).message);
return NextResponse.json({ error: 'Failed to load preference' }, { status: 500 });
}
}
async function writePreference(request: NextRequest) {
const auth = requireRole(request, 'admin', 'staff', 'pulse');
if (auth instanceof NextResponse) return auth;
try {
const body = await request.json();
const { key, value } = body as { key?: string; value?: unknown };
if (!key || typeof key !== 'string') {
return NextResponse.json({ error: 'key required' }, { status: 400 });
}
if (value === undefined) {
return NextResponse.json({ error: 'value required' }, { status: 400 });
}
await query(
`INSERT INTO user_preferences (username, pref_key, pref_value)
VALUES ($1, $2, $3::jsonb)
ON CONFLICT (username, pref_key)
DO UPDATE SET pref_value = EXCLUDED.pref_value,
updated_at = now()`,
[auth.username, key, JSON.stringify(value)],
);
return NextResponse.json({ ok: true });
} catch (err) {
console.error('[user-preferences] write error:', (err as Error).message);
return NextResponse.json({ error: 'Failed to save preference' }, { status: 500 });
}
}
export async function PUT(request: NextRequest) {
return writePreference(request);
}
// POST accepted so navigator.sendBeacon (which only POSTs) can flush pending
// saves on page unload, avoiding lost drag-drop changes.
export async function POST(request: NextRequest) {
return writePreference(request);
}