fix: code-review findings — closed-incident embedding 503, hardening, dedup

- /api/incidents/[id]/similar: embedding backfill on a closed incident hit
  the closure-lock trigger and turned the whole request into a 503; now
  skips persistence for closed incidents (vector still used for the query)
- addenda: cap body at 5000 chars; include body text in audit_log entry
- admin users PATCH: 404 when target user does not exist (was silent ok)
- extract shared requireAdmin to lib/auth/require-admin.ts (was duplicated
  in admin users + sites routes)
- extract escapeCsv/rowsToCsv to lib/csv.ts (was duplicated in dashboard
  export route and lib/reports/jkkp8.ts)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CPf5Rc8QPx6V8KLEEgfKEQ
This commit is contained in:
2026-07-12 12:45:01 +08:00
co-authored by Claude Fable 5
parent e2559d1f59
commit 4242af029c
8 changed files with 50 additions and 52 deletions
+10 -5
View File
@@ -22,12 +22,12 @@ export async function GET(
const { data: incident } = await supabase
.from('incidents')
.select('id, description, embedding')
.select('id, description, embedding, status')
.eq('id', id)
.single()
if (!incident) return NextResponse.json({ error: 'Not found' }, { status: 404 })
const inc = incident as { id: string; description: string; embedding: string | null }
const inc = incident as { id: string; description: string; embedding: string | null; status: string }
let embeddingVec: number[]
try {
@@ -35,9 +35,14 @@ export async function GET(
embeddingVec = JSON.parse(inc.embedding) as number[]
} else {
embeddingVec = await embedText(inc.description, voyageKey)
await supabase.from('incidents').update({
embedding: `[${embeddingVec.join(',')}]` as unknown as string,
}).eq('id', id)
// Closed incidents are locked at the DB level — the trigger would reject
// this backfill. The vector still serves the similarity query below.
if (inc.status !== 'closed') {
const { error: persistError } = await supabase.from('incidents').update({
embedding: `[${embeddingVec.join(',')}]` as unknown as string,
}).eq('id', id)
if (persistError) console.error('embedding backfill error:', persistError)
}
}
} catch {
return NextResponse.json({ error: 'Embedding service unavailable' }, { status: 503 })