← back to Norma
app/api/settings/route.ts
91 lines
import { NextRequest, NextResponse } from 'next/server';
import { query } from '@/lib/db';
import { requireRole } from '@/lib/require-role';
import { getOrgId } from '@/lib/orgId';
/**
* GET /api/settings
* Return all app settings as a merged object.
*/
export async function GET(request: NextRequest) {
const auth = requireRole(request, 'admin');
if (auth instanceof NextResponse) return auth;
try {
const result = await query(`SELECT key, value FROM app_settings ORDER BY key`);
const settings: Record<string, unknown> = {};
for (const row of result.rows) {
settings[row.key] = row.value;
}
// Also get data stats (scoped by org where possible)
const orgId = auth.role === 'admin' ? getOrgId(request) : auth.orgId;
const statsQueries = await Promise.all([
query(`SELECT COUNT(*)::int AS count FROM drafts WHERE ($1::uuid IS NULL OR org_id = $1::uuid)`, [orgId]).catch(() => ({ rows: [{ count: 0 }] })),
query(`SELECT COUNT(*)::int AS count FROM news_items WHERE ($1::uuid IS NULL OR org_id = $1::uuid)`, [orgId]).catch(() => ({ rows: [{ count: 0 }] })),
query(`SELECT COUNT(*)::int AS count FROM collaborations WHERE ($1::uuid IS NULL OR org_id = $1::uuid)`, [orgId]).catch(() => ({ rows: [{ count: 0 }] })),
query(`SELECT COUNT(*)::int AS count FROM partners WHERE ($1::uuid IS NULL OR org_id = $1::uuid)`, [orgId]).catch(() => ({ rows: [{ count: 0 }] })),
query(`SELECT COUNT(*)::int AS count FROM old_leads WHERE ($1::uuid IS NULL OR org_id = $1::uuid)`, [orgId]).catch(() => ({ rows: [{ count: 0 }] })),
]);
settings.data_stats = {
total_drafts: statsQueries[0].rows[0]?.count || 0,
total_news: statsQueries[1].rows[0]?.count || 0,
total_collaborations: statsQueries[2].rows[0]?.count || 0,
total_partners: statsQueries[3].rows[0]?.count || 0,
total_leads: statsQueries[4].rows[0]?.count || 0,
last_backup: new Date().toISOString(),
};
return NextResponse.json({ settings });
} catch (err) {
console.error('[api/settings] GET error:', (err as Error).message);
return NextResponse.json({ error: 'Failed to fetch settings' }, { status: 500 });
}
}
/**
* PATCH /api/settings
* Update one or more settings sections.
* Body: { org_profile?: {...}, email_config?: {...}, ai_config?: {...}, integrations?: {...} }
*/
export async function PATCH(request: NextRequest) {
const auth = requireRole(request, 'admin');
if (auth instanceof NextResponse) return auth;
try {
const body = await request.json();
const allowedKeys = ['org_profile', 'email_config', 'ai_config', 'integrations', 'alert_preferences'];
let updated = 0;
for (const key of allowedKeys) {
if (key in body && body[key] !== undefined) {
await query(
`INSERT INTO app_settings (key, value, updated_at)
VALUES ($1, $2, now())
ON CONFLICT (key) DO UPDATE SET value = $2, updated_at = now()`,
[key, JSON.stringify(body[key])]
);
updated++;
}
}
if (updated === 0) {
return NextResponse.json({ error: 'No valid settings to update' }, { status: 400 });
}
// Re-fetch all settings
const result = await query(`SELECT key, value FROM app_settings ORDER BY key`);
const settings: Record<string, unknown> = {};
for (const row of result.rows) {
settings[row.key] = row.value;
}
return NextResponse.json({ settings, updated });
} catch (err) {
console.error('[api/settings] PATCH error:', (err as Error).message);
return NextResponse.json({ error: 'Failed to update settings' }, { status: 500 });
}
}