883bab87-6894-46db-972a-fb5bc303c0eb
This commit is contained in:
1 parent
7166c51680
commit
e2c17aea18
3 files changed
+46
-53
No files matched your search
@@ -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 })
|
||||
|
||||
Reference in new issue
Block a user