fix: OpenRouter API key save fails with network error
All checks were successful
Deploy to VPS / deploy (push) Successful in 40s
All checks were successful
Deploy to VPS / deploy (push) Successful in 40s
The 0004_add_openrouter_provider.sql migration existed but was never registered in _journal.json, so the 'openrouter' value was missing from the api_key_provider PostgreSQL enum. Inserting an OpenRouter key threw a DB error that was unhandled, causing Next.js to return an HTML 500; the frontend's res.json() then threw, showing "Netzwerkfehler". Fixes: - Add 0004_add_openrouter_provider to _journal.json (idx 7) so the migration runs on next deploy and registers 'openrouter' in the enum - Fix null-label duplicate check: use isNull() instead of passing undefined to and(), which incorrectly matched all provider keys - Wrap DB insert in try/catch to return a proper JSON error instead of crashing with an unhandled exception Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -50,6 +50,13 @@
|
||||
"when": 1776451200000,
|
||||
"tag": "0006_seed_system_skills_fix",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 7,
|
||||
"version": "7",
|
||||
"when": 1776537600000,
|
||||
"tag": "0004_add_openrouter_provider",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
import { db } from '@/lib/db';
|
||||
import { tenantApiKeys } from '@/lib/db/schema';
|
||||
import { eq, and } from 'drizzle-orm';
|
||||
import { eq, and, isNull } from 'drizzle-orm';
|
||||
import { requirePermission } from '@/lib/auth/rbac';
|
||||
import { encrypt, keyHint } from '@/lib/crypto';
|
||||
import { logAuditEvent } from '@/lib/auth/audit';
|
||||
@@ -53,7 +53,7 @@ export async function POST(request: Request) {
|
||||
return Response.json({ error: 'API-Schlüssel ist erforderlich (mindestens 8 Zeichen).' }, { status: 400 });
|
||||
}
|
||||
|
||||
// Check for duplicate provider+label
|
||||
// Check for duplicate provider+label (null labels must use IS NULL, not equality)
|
||||
const existing = await db
|
||||
.select({ id: tenantApiKeys.id })
|
||||
.from(tenantApiKeys)
|
||||
@@ -61,7 +61,7 @@ export async function POST(request: Request) {
|
||||
and(
|
||||
eq(tenantApiKeys.tenantId, ctx.tenantId),
|
||||
eq(tenantApiKeys.provider, provider as AIProvider),
|
||||
label ? eq(tenantApiKeys.label, label) : undefined,
|
||||
label ? eq(tenantApiKeys.label, label) : isNull(tenantApiKeys.label),
|
||||
),
|
||||
)
|
||||
.limit(1);
|
||||
@@ -84,24 +84,30 @@ export async function POST(request: Request) {
|
||||
}
|
||||
const hint = keyHint(apiKey);
|
||||
|
||||
const [created] = await db
|
||||
.insert(tenantApiKeys)
|
||||
.values({
|
||||
tenantId: ctx.tenantId,
|
||||
provider: provider as AIProvider,
|
||||
encryptedKey,
|
||||
keyHint: hint,
|
||||
label: label || null,
|
||||
createdByUserId: ctx.userId,
|
||||
})
|
||||
.returning({
|
||||
id: tenantApiKeys.id,
|
||||
provider: tenantApiKeys.provider,
|
||||
keyHint: tenantApiKeys.keyHint,
|
||||
label: tenantApiKeys.label,
|
||||
isActive: tenantApiKeys.isActive,
|
||||
createdAt: tenantApiKeys.createdAt,
|
||||
});
|
||||
let created: { id: string; provider: string; keyHint: string; label: string | null; isActive: boolean; createdAt: Date };
|
||||
try {
|
||||
[created] = await db
|
||||
.insert(tenantApiKeys)
|
||||
.values({
|
||||
tenantId: ctx.tenantId,
|
||||
provider: provider as AIProvider,
|
||||
encryptedKey,
|
||||
keyHint: hint,
|
||||
label: label || null,
|
||||
createdByUserId: ctx.userId,
|
||||
})
|
||||
.returning({
|
||||
id: tenantApiKeys.id,
|
||||
provider: tenantApiKeys.provider,
|
||||
keyHint: tenantApiKeys.keyHint,
|
||||
label: tenantApiKeys.label,
|
||||
isActive: tenantApiKeys.isActive,
|
||||
createdAt: tenantApiKeys.createdAt,
|
||||
});
|
||||
} catch (err) {
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
return Response.json({ error: `Datenbankfehler beim Speichern: ${msg}` }, { status: 500 });
|
||||
}
|
||||
|
||||
const ip = request.headers.get('x-forwarded-for')?.split(',')[0]?.trim();
|
||||
await logAuditEvent(ctx, 'create', 'tenant_api_key', created.id, { provider, label: label || null }, ip);
|
||||
|
||||
Reference in New Issue
Block a user