883bab87-6894-46db-972a-fb5bc303c0eb

This commit is contained in:
Z User committed 2026-10-29 22:50:51 +00:00
1 parent 7166c51680
commit e2c17aea18
3 files changed
+46 -53

No files matched your search

+22 -34
View File
@@ -214,7 +214,14 @@ export async function PATCH(req: Request, { params }: { params: Promise<{ id: st
// DELETE /api/aos/instances/[id] — tear down instance (idempotent, resumable)
// Removes: Dokploy domain → database → application + volumes → DNS record.
// Never deletes the Instance DB row (keeps audit record) — sets status=deleted.
//
// HARD DELETE: the Instance DB row is deleted entirely (cascade removes
// InstanceEvent + ProvisioningJob). Instances are infrastructure, not
// accounting records — they have NO GoBD retention requirement. Only the
// accounting module (muhasebe/buchhaltung: invoices, expenses, kassenbuch,
// belege, buchungssätze, licenses) is exempt from hard-deletion for GoBD.
// The cross-cutting AuditLog entry (separate table, string entityId) is the
// persistent record that this action happened.
export async function DELETE(_req: Request, { params }: { params: Promise<{ id: string }> }) {
try {
const { id } = await params
@@ -222,9 +229,6 @@ export async function DELETE(_req: Request, { params }: { params: Promise<{ id:
if (!instance) {
return NextResponse.json({ error: 'instance not found' }, { status: 404 })
}
if (instance.status === 'deleted') {
return NextResponse.json({ ok: true, message: 'already deleted', steps: [] })
}
await db.instance.update({ where: { id }, data: { status: 'deleting' } })
await db.instanceEvent.create({
@@ -285,8 +289,8 @@ export async function DELETE(_req: Request, { params }: { params: Promise<{ id:
}
// 3. Decide final state.
// - Both succeeded → 'deleted' (clear all IDs + secrets)
// - Either failed → 'failed' (keep IDs for reconciler retry)
// - Both succeeded → hard-delete the instance row (no GoBD on infra)
// - Either failed → keep row as 'failed' (retain IDs for reconciler retry)
const now = new Date()
if (dokployFailed || dnsFailed) {
await db.instance.update({
@@ -319,44 +323,28 @@ export async function DELETE(_req: Request, { params }: { params: Promise<{ id:
}, { status: 500 })
}
// 4. Both succeeded → mark deleted (keep row for audit — GoBD)
await db.instance.update({
where: { id },
data: {
status: 'deleted',
deletedAt: now,
dokployAppId: null,
dokployDbId: null,
dnsRecordId: null,
dokployDbPassword: null, // clear secrets
adminLoginToken: null,
},
})
// 5. Unlink from license (keep license, just remove instance ref)
if (instance.licenseId) {
await db.license.update({
where: { id: instance.licenseId },
data: { instance: { disconnect: true } },
})
}
await db.instanceEvent.create({
data: { instanceId: id, type: 'delete.ok', step: 'done', result: 'ok', details: JSON.stringify({ steps, deletedAt: now }) },
})
// 4. Both succeeded → hard-delete the instance row.
// Instances are infrastructure (not accounting), so there is no GoBD
// retention requirement on the row itself. Cascade deletes the
// InstanceEvent + ProvisioningJob rows. The AuditLog entry (separate
// table) is the durable record of this action.
await db.auditLog.create({
data: {
actor: 'admin:ui',
action: 'instance.delete',
entity: 'Instance',
entityId: id,
after: JSON.stringify({ subdomain: instance.subdomain, steps }),
before: JSON.stringify({ subdomain: instance.subdomain, status: instance.status, product: instance.product, companyId: instance.companyId }),
after: JSON.stringify({ subdomain: instance.subdomain, steps, deletedAt: now }),
hash: 'sha256:' + Math.random().toString(36).slice(2),
},
})
return NextResponse.json({ ok: true, status: 'deleted', steps, deletedAt: now })
// Hard-delete: License 1:1 reverse relation is automatically cleared
// (the FK lives on the Instance row). Cascade removes events + jobs.
await db.instance.delete({ where: { id } })
return NextResponse.json({ ok: true, deleted: true, steps, deletedAt: now })
} catch (err) {
const msg = err instanceof Error ? err.message : 'internal server error'
return NextResponse.json({ error: msg }, { status: 500 })