0bb384e7-e18c-49c5-b7ef-fe73701ab6ab

This commit is contained in:
Z User committed 2026-10-28 17:00:06 +00:00
1 parent c4d338ee48
commit 8c34778db3
8 files changed
+121 -52

No files matched your search

+49 -3
View File
@@ -176,6 +176,9 @@ export async function DELETE(_req: Request, { params }: { params: Promise<{ id:
// Parse metadata for Dokploy domain ID
const meta = instance.metadata ? JSON.parse(instance.metadata) as { dokployDomainId?: string; url?: string } : {}
let dokployFailed = false
let dnsFailed = false
// 1. Remove Dokploy domain + database + application (idempotent)
if (dokploy) {
try {
@@ -189,6 +192,7 @@ export async function DELETE(_req: Request, { params }: { params: Promise<{ id:
data: { instanceId: id, type: 'delete.dokploy.done', step: 'dokploy', result: 'ok', details: JSON.stringify({ steps: result.steps }) },
})
} catch (e) {
dokployFailed = true
steps.push(`dokploy.error: ${e instanceof Error ? e.message : 'unknown'}`)
await db.instanceEvent.create({
data: { instanceId: id, type: 'delete.dokploy.error', step: 'dokploy', result: 'error', details: JSON.stringify({ error: e instanceof Error ? e.message : 'unknown' }) },
@@ -198,7 +202,11 @@ export async function DELETE(_req: Request, { params }: { params: Promise<{ id:
steps.push('dokploy.skipped (not configured)')
}
// 2. Remove DNS A record (if per-instance, not wildcard)
// 2. Remove DNS A record (if per-instance, not wildcard).
// IMPORTANT: Dokploy silme başarısız olsa bile DNS'i silmeye çalış —
// orphaned DNS record subdomain takeover riski taşır. Ama DNS silme
// hata verirse dnsRecordId'yi KORU, instance'ı failed yap ki reconciler
// sonra tekrar deneyebilsin.
if (dns && instance.dnsRecordId && instance.dnsRecordId !== 'wildcard') {
try {
await dns.deleteRecord(instance.dnsRecordId)
@@ -207,12 +215,50 @@ export async function DELETE(_req: Request, { params }: { params: Promise<{ id:
data: { instanceId: id, type: 'delete.dns.removed', step: 'dns', result: 'ok', details: JSON.stringify({ recordId: instance.dnsRecordId }) },
})
} catch (e) {
dnsFailed = true
steps.push(`dns.error: ${e instanceof Error ? e.message : 'unknown'}`)
await db.instanceEvent.create({
data: { instanceId: id, type: 'delete.dns.error', step: 'dns', result: 'error', details: JSON.stringify({ recordId: instance.dnsRecordId, error: e instanceof Error ? e.message : 'unknown' }) },
})
}
}
// 3. Mark deleted (keep the row for audit — GoBD)
// 3. Decide final state.
// - Both succeeded → 'deleted' (clear all IDs + secrets)
// - Either failed → 'failed' (keep IDs for reconciler retry)
const now = new Date()
if (dokployFailed || dnsFailed) {
await db.instance.update({
where: { id },
data: {
status: 'failed',
lastError: `deletion partial: ${steps.join(', ')}`,
lastErrorAt: now,
// Keep dokployAppId + dnsRecordId so reconciler can retry.
},
})
await db.instanceEvent.create({
data: { instanceId: id, type: 'delete.partial', step: 'done', result: 'error', details: JSON.stringify({ steps, dokployFailed, dnsFailed }) },
})
await db.auditLog.create({
data: {
actor: 'admin:ui',
action: 'instance.delete.partial',
entity: 'Instance',
entityId: id,
after: JSON.stringify({ subdomain: instance.subdomain, steps, dokployFailed, dnsFailed, message: 'reconciler will retry' }),
hash: 'sha256:' + Math.random().toString(36).slice(2),
},
})
return NextResponse.json({
ok: false,
status: 'failed',
steps,
message: 'Löschung teilweise fehlgeschlagen — Reconciler wird erneut versuchen. Dokploy + Hetzner A record birlikte silinmedi; orphaned resource riski için instance failed durumda.',
}, { status: 500 })
}
// 4. Both succeeded → mark deleted (keep row for audit — GoBD)
await db.instance.update({
where: { id },
data: {
@@ -226,7 +272,7 @@ export async function DELETE(_req: Request, { params }: { params: Promise<{ id:
},
})
// 4. Unlink from license (keep license, just remove instance ref)
// 5. Unlink from license (keep license, just remove instance ref)
if (instance.licenseId) {
await db.license.update({
where: { id: instance.licenseId },