ccp: surface slug-collision as 409, not raw Prisma error
agents.routes.ts approve handler: wrap prisma.instance.create in try/catch. When a PrismaClientKnownRequestError P2002 with target including 'slug' is thrown, convert to a 409 AppError with a clear message directing the admin at DELETE /api/instances/:id or the new scripts/ccp-deregister.sh on the target host. Before this, re-registering a previously-registered host (after the operator tore down the underlying stack without cleaning CCP's DB row) returned 500 with a raw Prisma error string — the operator had to read a stack trace to understand the cause. Now the error is self-describing and points at the fix. Matches the pattern other CCP error paths use. Bunker Admin
This commit is contained in:
parent
ce8c5aaf1f
commit
6504598752
@ -150,23 +150,41 @@ router.post('/registrations/:id/approve', authenticate, requireRole('SUPER_ADMIN
|
||||
throw new AppError(400, `Registration is ${registration.status}, not PENDING`);
|
||||
}
|
||||
|
||||
// Create the Instance record
|
||||
const instance = await prisma.instance.create({
|
||||
data: {
|
||||
slug: registration.slug,
|
||||
name: registration.name,
|
||||
domain: registration.domain,
|
||||
status: InstanceStatus.STOPPED,
|
||||
statusMessage: 'Remote instance registered — agent connecting',
|
||||
basePath: registration.basePath,
|
||||
composeProject: registration.composeProject,
|
||||
portConfig: (registration.metadata as Record<string, unknown>)?.portConfig || { api: 4000, admin: 3000, postgres: 5432, nginx: 80 },
|
||||
isRegistered: true,
|
||||
isRemote: true,
|
||||
agentUrl: registration.agentUrl,
|
||||
adminEmail: (registration.metadata as Record<string, unknown>)?.adminEmail as string || 'admin@example.com',
|
||||
},
|
||||
});
|
||||
// Create the Instance record.
|
||||
// The slug is unique. If a stale Instance with the same slug exists (e.g. the
|
||||
// operator tore down the underlying stack without running ccp-deregister.sh)
|
||||
// surface a clean 409 instead of leaking the raw Prisma error.
|
||||
let instance;
|
||||
try {
|
||||
instance = await prisma.instance.create({
|
||||
data: {
|
||||
slug: registration.slug,
|
||||
name: registration.name,
|
||||
domain: registration.domain,
|
||||
status: InstanceStatus.STOPPED,
|
||||
statusMessage: 'Remote instance registered — agent connecting',
|
||||
basePath: registration.basePath,
|
||||
composeProject: registration.composeProject,
|
||||
portConfig: (registration.metadata as Record<string, unknown>)?.portConfig || { api: 4000, admin: 3000, postgres: 5432, nginx: 80 },
|
||||
isRegistered: true,
|
||||
isRemote: true,
|
||||
agentUrl: registration.agentUrl,
|
||||
adminEmail: (registration.metadata as Record<string, unknown>)?.adminEmail as string || 'admin@example.com',
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
if (err instanceof Prisma.PrismaClientKnownRequestError && err.code === 'P2002') {
|
||||
const target = (err.meta?.target as string[] | undefined) ?? [];
|
||||
if (target.includes('slug')) {
|
||||
throw new AppError(
|
||||
409,
|
||||
`Slug '${registration.slug}' is already in use by another Instance. Delete the stale instance first (DELETE /api/instances/:id) or run scripts/ccp-deregister.sh from the target host.`,
|
||||
'SLUG_CONFLICT'
|
||||
);
|
||||
}
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
|
||||
// Issue mTLS certificates
|
||||
const certMaterials = await issueAgentCert(instance.id, registration.slug, registration.agentUrl);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user