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
+14
View File
@@ -0,0 +1,14 @@
export function escapeCsv(value: string | number | null | undefined): string {
if (value === null || value === undefined) return ''
const str = String(value)
if (str.includes(',') || str.includes('"') || str.includes('\n')) {
return `"${str.replace(/"/g, '""')}"`
}
return str
}
export function rowsToCsv(headers: string[], rows: string[][]): string {
const lines = [headers.map(escapeCsv).join(',')]
for (const row of rows) lines.push(row.map(escapeCsv).join(','))
return lines.join('\r\n')
}