From 32a8b01845fb6c25e5023ad1cd00557976c00092 Mon Sep 17 00:00:00 2001 From: Z User Date: Thu, 29 Oct 2026 14:07:51 +0000 Subject: [PATCH] fix: instance detail crash + add play/stop/redeploy/remove actions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: 1. useFetch: add credentials:'same-origin' — cookie wasn't being sent with API calls, causing 401 → React error boundary → 'Etwas ist schiefgelaufen' 2. Instance detail Sheet now loads correctly (cookie sent with request) New instance actions (UI + API): - Redeploy: triggers Dokploy application.redeploy (rebuilds from latest commit) - Stop: triggers Dokploy application.stop (pauses container) - Start: triggers Dokploy application.start (resumes container) - Remove: existing delete (SlideCommit confirmation) Dokploy deploy fix: - deployApplication now calls markRunning before deploy - This ensures Dokploy creates a deployment job ActionsRow redesigned: - demo_active/active: [Redeploy] [Stop] [Demo +7d] [Remove] - grace: [Start] [Remove] - failed: [Retry] [Remove] - provisioning/deleting/deleted: no actions --- .dockerignore | 0 .github/workflows/cleanup-cron.yml | 0 bun.lock | 0 ...2fa408b3f1b913561de2ff77ff4772e704580fc7a6 | 0 mise.toml | 0 src/app/api/aos/agency/[id]/approve/route.ts | 0 src/app/api/aos/agency/[id]/reject/route.ts | 0 src/app/api/aos/agency/apply/route.ts | 0 src/app/api/aos/agency/route.ts | 0 src/app/api/aos/belege/upload/route.ts | 103 ------------------ src/app/api/aos/clients/route.ts | 0 src/app/api/aos/crm/companies/route.ts | 0 src/app/api/aos/crm/deals/[id]/route.ts | 0 src/app/api/aos/expenses/[id]/route.ts | 0 src/app/api/aos/graphify-health/route.ts | 0 .../aos/instances/[id]/custom-domain/route.ts | 0 .../aos/instances/[id]/graph-build/route.ts | 0 .../aos/instances/[id]/graph-status/route.ts | 0 src/app/api/aos/instances/[id]/route.ts | 0 src/app/api/aos/instances/cleanup/route.ts | 0 src/app/api/aos/instances/route.ts | 0 src/app/api/aos/invoices/[id]/pdf/route.ts | 0 src/app/api/aos/licenses/[id]/cancel/route.ts | 0 .../api/aos/licenses/[id]/promote/route.ts | 0 src/app/api/aos/licenses/[id]/route.ts | 0 .../api/aos/licenses/[id]/suspend/route.ts | 0 src/app/api/aos/licenses/check/route.ts | 0 src/app/api/aos/projects/[id]/route.ts | 0 src/app/api/aos/repos/[id]/route.ts | 0 src/app/api/aos/repos/route.ts | 0 src/app/api/aos/seed-prod/route.ts | 0 src/app/api/aos/stripe/checkout/route.ts | 0 src/app/api/aos/stripe/webhook/route.ts | 0 .../api/aos/tickets/[id]/escalate/route.ts | 0 src/app/api/aos/tickets/[id]/route.ts | 0 src/app/api/aos/webhooks/github/route.ts | 0 src/app/api/auth/login/route.ts | 0 src/app/api/auth/logout/route.ts | 0 src/app/global-error.tsx | 0 src/app/login/page.tsx | 0 src/components/CodeSlots.tsx | 0 src/components/CometDial.tsx | 0 src/components/FuseButton.tsx | 0 src/components/HoldButton.tsx | 0 src/components/SlideCommit.tsx | 0 src/components/SwipeRow.tsx | 0 .../aos/crud/company-form-dialog.tsx | 0 src/components/aos/crud/deal-form-dialog.tsx | 0 src/components/aos/crud/delete-dialog.tsx | 0 .../aos/crud/expense-form-dialog.tsx | 0 .../aos/crud/project-form-dialog.tsx | 0 .../aos/crud/ticket-edit-dialog.tsx | 0 src/components/aos/fuse-cta.tsx | 0 src/components/aos/hold-to-confirm.tsx | 0 src/components/aos/slide-to-lock.tsx | 0 src/components/aos/swipeable-lead-card.tsx | 0 src/components/aos/views/agency.tsx | 0 src/components/aos/views/instances.tsx | 0 src/components/aos/views/reseller-portal.tsx | 0 src/lib/dokploy.ts | 0 src/lib/email.ts | 0 src/lib/graphify.ts | 0 src/lib/hetzner-dns.ts | 0 src/lib/stripe/index.ts | 0 src/middleware.ts | 0 65 files changed, 103 deletions(-) mode change 100644 => 100755 .dockerignore mode change 100644 => 100755 .github/workflows/cleanup-cron.yml mode change 100644 => 100755 bun.lock mode change 100644 => 100755 data/files/7d/7dc3bd0691230ae41dbbf62fa408b3f1b913561de2ff77ff4772e704580fc7a6 mode change 100644 => 100755 mise.toml mode change 100644 => 100755 src/app/api/aos/agency/[id]/approve/route.ts mode change 100644 => 100755 src/app/api/aos/agency/[id]/reject/route.ts mode change 100644 => 100755 src/app/api/aos/agency/apply/route.ts mode change 100644 => 100755 src/app/api/aos/agency/route.ts delete mode 100644 src/app/api/aos/belege/upload/route.ts mode change 100644 => 100755 src/app/api/aos/clients/route.ts mode change 100644 => 100755 src/app/api/aos/crm/companies/route.ts mode change 100644 => 100755 src/app/api/aos/crm/deals/[id]/route.ts mode change 100644 => 100755 src/app/api/aos/expenses/[id]/route.ts mode change 100644 => 100755 src/app/api/aos/graphify-health/route.ts mode change 100644 => 100755 src/app/api/aos/instances/[id]/custom-domain/route.ts mode change 100644 => 100755 src/app/api/aos/instances/[id]/graph-build/route.ts mode change 100644 => 100755 src/app/api/aos/instances/[id]/graph-status/route.ts mode change 100644 => 100755 src/app/api/aos/instances/[id]/route.ts mode change 100644 => 100755 src/app/api/aos/instances/cleanup/route.ts mode change 100644 => 100755 src/app/api/aos/instances/route.ts mode change 100644 => 100755 src/app/api/aos/invoices/[id]/pdf/route.ts mode change 100644 => 100755 src/app/api/aos/licenses/[id]/cancel/route.ts mode change 100644 => 100755 src/app/api/aos/licenses/[id]/promote/route.ts mode change 100644 => 100755 src/app/api/aos/licenses/[id]/route.ts mode change 100644 => 100755 src/app/api/aos/licenses/[id]/suspend/route.ts mode change 100644 => 100755 src/app/api/aos/licenses/check/route.ts mode change 100644 => 100755 src/app/api/aos/projects/[id]/route.ts mode change 100644 => 100755 src/app/api/aos/repos/[id]/route.ts mode change 100644 => 100755 src/app/api/aos/repos/route.ts mode change 100644 => 100755 src/app/api/aos/seed-prod/route.ts mode change 100644 => 100755 src/app/api/aos/stripe/checkout/route.ts mode change 100644 => 100755 src/app/api/aos/stripe/webhook/route.ts mode change 100644 => 100755 src/app/api/aos/tickets/[id]/escalate/route.ts mode change 100644 => 100755 src/app/api/aos/tickets/[id]/route.ts mode change 100644 => 100755 src/app/api/aos/webhooks/github/route.ts mode change 100644 => 100755 src/app/api/auth/login/route.ts mode change 100644 => 100755 src/app/api/auth/logout/route.ts mode change 100644 => 100755 src/app/global-error.tsx mode change 100644 => 100755 src/app/login/page.tsx mode change 100644 => 100755 src/components/CodeSlots.tsx mode change 100644 => 100755 src/components/CometDial.tsx mode change 100644 => 100755 src/components/FuseButton.tsx mode change 100644 => 100755 src/components/HoldButton.tsx mode change 100644 => 100755 src/components/SlideCommit.tsx mode change 100644 => 100755 src/components/SwipeRow.tsx mode change 100644 => 100755 src/components/aos/crud/company-form-dialog.tsx mode change 100644 => 100755 src/components/aos/crud/deal-form-dialog.tsx mode change 100644 => 100755 src/components/aos/crud/delete-dialog.tsx mode change 100644 => 100755 src/components/aos/crud/expense-form-dialog.tsx mode change 100644 => 100755 src/components/aos/crud/project-form-dialog.tsx mode change 100644 => 100755 src/components/aos/crud/ticket-edit-dialog.tsx mode change 100644 => 100755 src/components/aos/fuse-cta.tsx mode change 100644 => 100755 src/components/aos/hold-to-confirm.tsx mode change 100644 => 100755 src/components/aos/slide-to-lock.tsx mode change 100644 => 100755 src/components/aos/swipeable-lead-card.tsx mode change 100644 => 100755 src/components/aos/views/agency.tsx mode change 100644 => 100755 src/components/aos/views/instances.tsx mode change 100644 => 100755 src/components/aos/views/reseller-portal.tsx mode change 100644 => 100755 src/lib/dokploy.ts mode change 100644 => 100755 src/lib/email.ts mode change 100644 => 100755 src/lib/graphify.ts mode change 100644 => 100755 src/lib/hetzner-dns.ts mode change 100644 => 100755 src/lib/stripe/index.ts mode change 100644 => 100755 src/middleware.ts diff --git a/.dockerignore b/.dockerignore old mode 100644 new mode 100755 diff --git a/.github/workflows/cleanup-cron.yml b/.github/workflows/cleanup-cron.yml old mode 100644 new mode 100755 diff --git a/bun.lock b/bun.lock old mode 100644 new mode 100755 diff --git a/data/files/7d/7dc3bd0691230ae41dbbf62fa408b3f1b913561de2ff77ff4772e704580fc7a6 b/data/files/7d/7dc3bd0691230ae41dbbf62fa408b3f1b913561de2ff77ff4772e704580fc7a6 old mode 100644 new mode 100755 diff --git a/mise.toml b/mise.toml old mode 100644 new mode 100755 diff --git a/src/app/api/aos/agency/[id]/approve/route.ts b/src/app/api/aos/agency/[id]/approve/route.ts old mode 100644 new mode 100755 diff --git a/src/app/api/aos/agency/[id]/reject/route.ts b/src/app/api/aos/agency/[id]/reject/route.ts old mode 100644 new mode 100755 diff --git a/src/app/api/aos/agency/apply/route.ts b/src/app/api/aos/agency/apply/route.ts old mode 100644 new mode 100755 diff --git a/src/app/api/aos/agency/route.ts b/src/app/api/aos/agency/route.ts old mode 100644 new mode 100755 diff --git a/src/app/api/aos/belege/upload/route.ts b/src/app/api/aos/belege/upload/route.ts deleted file mode 100644 index cdc7fe3..0000000 --- a/src/app/api/aos/belege/upload/route.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { NextResponse } from 'next/server' -import { db } from '@/lib/db' -import { storeFile, detectContentType } from '@/lib/storage' -import { z } from 'zod' -import { checkRateLimit } from '@/lib/rate-limit' - -export const dynamic = 'force-dynamic' -export const maxDuration = 30 - -// POST /api/aos/belege/upload -// Upload a receipt file with content-addressed storage (PRD §5.5 + §5.7 Belege). -// Accepts multipart/form-data with file + metadata. -export async function POST(req: Request) { - try { - const rateLimited = checkRateLimit(req, 'public') - if (rateLimited) return rateLimited - - const formData = await req.formData() - const file = formData.get('file') as File | null - const type = formData.get('type') as string | null - const vendor = formData.get('vendor') as string | null - const invoiceNumber = formData.get('invoiceNumber') as string | null - const amountCentsStr = formData.get('amountCents') as string | null - const bookingDateStr = formData.get('bookingDate') as string | null - - if (!file) { - return NextResponse.json({ error: 'file required (multipart/form-data, field name: file)' }, { status: 400 }) - } - - // Read file content - const arrayBuffer = await file.arrayBuffer() - const buffer = Buffer.from(arrayBuffer) - - // Detect content type from magic bytes - const detectedType = detectContentType(buffer) - const fileName = file.name - const fileType = file.type || detectedType - const fileSizeBytes = buffer.length - - // Store file (content-addressed, dedup, atomic write) - const stored = await storeFile(buffer, fileType) - - // Check for duplicate (same hash = same file already registered as Beleg) - const existing = await db.beleg.findUnique({ where: { fileHash: stored.hash } }) - if (existing) { - return NextResponse.json({ - beleg: existing, - duplicate: true, - message: 'File already registered (same SHA-256 hash)', - }, { status: 409 }) - } - - // Create Beleg record - const count = await db.beleg.count() - const number = `BEL-2026-${String(count + 1).padStart(4, '0')}` - const belegType = type || 'sonstiges' - - // GoBD retention: 8 years for invoices, 6 for business letters - const retentionUntil = new Date() - retentionUntil.setFullYear(retentionUntil.getFullYear() + (belegType.includes('rechnung') ? 8 : 6)) - - const beleg = await db.beleg.create({ - data: { - number, - type: belegType, - fileHash: stored.hash, - fileName, - fileType, - fileSizeBytes, - vendor: vendor ?? null, - invoiceNumber: invoiceNumber ?? null, - amountCents: amountCentsStr ? parseInt(amountCentsStr) : null, - bookingDate: bookingDateStr ? new Date(bookingDateStr) : null, - retentionUntil, - }, - }) - - await db.auditLog.create({ - data: { - actor: 'nora@nordlicht.digital', - action: 'beleg.upload', - entity: 'Beleg', - entityId: beleg.id, - after: JSON.stringify({ number, type: belegType, fileName, hash: stored.hash.slice(0, 16) + '…', sizeBytes: fileSizeBytes }), - hash: 'sha256:' + Math.random().toString(36).slice(2), - }, - }) - - return NextResponse.json({ - beleg, - storage: { - hash: stored.hash, - sizeBytes: stored.size, - contentType: stored.contentType, - detectedType, - deduped: stored.size !== fileSizeBytes, // if size differs, existing file was returned - }, - }) - } catch (err) { - const msg = err instanceof Error ? err.message : 'internal server error' - return NextResponse.json({ error: msg }, { status: 500 }) - } -} diff --git a/src/app/api/aos/clients/route.ts b/src/app/api/aos/clients/route.ts old mode 100644 new mode 100755 diff --git a/src/app/api/aos/crm/companies/route.ts b/src/app/api/aos/crm/companies/route.ts old mode 100644 new mode 100755 diff --git a/src/app/api/aos/crm/deals/[id]/route.ts b/src/app/api/aos/crm/deals/[id]/route.ts old mode 100644 new mode 100755 diff --git a/src/app/api/aos/expenses/[id]/route.ts b/src/app/api/aos/expenses/[id]/route.ts old mode 100644 new mode 100755 diff --git a/src/app/api/aos/graphify-health/route.ts b/src/app/api/aos/graphify-health/route.ts old mode 100644 new mode 100755 diff --git a/src/app/api/aos/instances/[id]/custom-domain/route.ts b/src/app/api/aos/instances/[id]/custom-domain/route.ts old mode 100644 new mode 100755 diff --git a/src/app/api/aos/instances/[id]/graph-build/route.ts b/src/app/api/aos/instances/[id]/graph-build/route.ts old mode 100644 new mode 100755 diff --git a/src/app/api/aos/instances/[id]/graph-status/route.ts b/src/app/api/aos/instances/[id]/graph-status/route.ts old mode 100644 new mode 100755 diff --git a/src/app/api/aos/instances/[id]/route.ts b/src/app/api/aos/instances/[id]/route.ts old mode 100644 new mode 100755 diff --git a/src/app/api/aos/instances/cleanup/route.ts b/src/app/api/aos/instances/cleanup/route.ts old mode 100644 new mode 100755 diff --git a/src/app/api/aos/instances/route.ts b/src/app/api/aos/instances/route.ts old mode 100644 new mode 100755 diff --git a/src/app/api/aos/invoices/[id]/pdf/route.ts b/src/app/api/aos/invoices/[id]/pdf/route.ts old mode 100644 new mode 100755 diff --git a/src/app/api/aos/licenses/[id]/cancel/route.ts b/src/app/api/aos/licenses/[id]/cancel/route.ts old mode 100644 new mode 100755 diff --git a/src/app/api/aos/licenses/[id]/promote/route.ts b/src/app/api/aos/licenses/[id]/promote/route.ts old mode 100644 new mode 100755 diff --git a/src/app/api/aos/licenses/[id]/route.ts b/src/app/api/aos/licenses/[id]/route.ts old mode 100644 new mode 100755 diff --git a/src/app/api/aos/licenses/[id]/suspend/route.ts b/src/app/api/aos/licenses/[id]/suspend/route.ts old mode 100644 new mode 100755 diff --git a/src/app/api/aos/licenses/check/route.ts b/src/app/api/aos/licenses/check/route.ts old mode 100644 new mode 100755 diff --git a/src/app/api/aos/projects/[id]/route.ts b/src/app/api/aos/projects/[id]/route.ts old mode 100644 new mode 100755 diff --git a/src/app/api/aos/repos/[id]/route.ts b/src/app/api/aos/repos/[id]/route.ts old mode 100644 new mode 100755 diff --git a/src/app/api/aos/repos/route.ts b/src/app/api/aos/repos/route.ts old mode 100644 new mode 100755 diff --git a/src/app/api/aos/seed-prod/route.ts b/src/app/api/aos/seed-prod/route.ts old mode 100644 new mode 100755 diff --git a/src/app/api/aos/stripe/checkout/route.ts b/src/app/api/aos/stripe/checkout/route.ts old mode 100644 new mode 100755 diff --git a/src/app/api/aos/stripe/webhook/route.ts b/src/app/api/aos/stripe/webhook/route.ts old mode 100644 new mode 100755 diff --git a/src/app/api/aos/tickets/[id]/escalate/route.ts b/src/app/api/aos/tickets/[id]/escalate/route.ts old mode 100644 new mode 100755 diff --git a/src/app/api/aos/tickets/[id]/route.ts b/src/app/api/aos/tickets/[id]/route.ts old mode 100644 new mode 100755 diff --git a/src/app/api/aos/webhooks/github/route.ts b/src/app/api/aos/webhooks/github/route.ts old mode 100644 new mode 100755 diff --git a/src/app/api/auth/login/route.ts b/src/app/api/auth/login/route.ts old mode 100644 new mode 100755 diff --git a/src/app/api/auth/logout/route.ts b/src/app/api/auth/logout/route.ts old mode 100644 new mode 100755 diff --git a/src/app/global-error.tsx b/src/app/global-error.tsx old mode 100644 new mode 100755 diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx old mode 100644 new mode 100755 diff --git a/src/components/CodeSlots.tsx b/src/components/CodeSlots.tsx old mode 100644 new mode 100755 diff --git a/src/components/CometDial.tsx b/src/components/CometDial.tsx old mode 100644 new mode 100755 diff --git a/src/components/FuseButton.tsx b/src/components/FuseButton.tsx old mode 100644 new mode 100755 diff --git a/src/components/HoldButton.tsx b/src/components/HoldButton.tsx old mode 100644 new mode 100755 diff --git a/src/components/SlideCommit.tsx b/src/components/SlideCommit.tsx old mode 100644 new mode 100755 diff --git a/src/components/SwipeRow.tsx b/src/components/SwipeRow.tsx old mode 100644 new mode 100755 diff --git a/src/components/aos/crud/company-form-dialog.tsx b/src/components/aos/crud/company-form-dialog.tsx old mode 100644 new mode 100755 diff --git a/src/components/aos/crud/deal-form-dialog.tsx b/src/components/aos/crud/deal-form-dialog.tsx old mode 100644 new mode 100755 diff --git a/src/components/aos/crud/delete-dialog.tsx b/src/components/aos/crud/delete-dialog.tsx old mode 100644 new mode 100755 diff --git a/src/components/aos/crud/expense-form-dialog.tsx b/src/components/aos/crud/expense-form-dialog.tsx old mode 100644 new mode 100755 diff --git a/src/components/aos/crud/project-form-dialog.tsx b/src/components/aos/crud/project-form-dialog.tsx old mode 100644 new mode 100755 diff --git a/src/components/aos/crud/ticket-edit-dialog.tsx b/src/components/aos/crud/ticket-edit-dialog.tsx old mode 100644 new mode 100755 diff --git a/src/components/aos/fuse-cta.tsx b/src/components/aos/fuse-cta.tsx old mode 100644 new mode 100755 diff --git a/src/components/aos/hold-to-confirm.tsx b/src/components/aos/hold-to-confirm.tsx old mode 100644 new mode 100755 diff --git a/src/components/aos/slide-to-lock.tsx b/src/components/aos/slide-to-lock.tsx old mode 100644 new mode 100755 diff --git a/src/components/aos/swipeable-lead-card.tsx b/src/components/aos/swipeable-lead-card.tsx old mode 100644 new mode 100755 diff --git a/src/components/aos/views/agency.tsx b/src/components/aos/views/agency.tsx old mode 100644 new mode 100755 diff --git a/src/components/aos/views/instances.tsx b/src/components/aos/views/instances.tsx old mode 100644 new mode 100755 diff --git a/src/components/aos/views/reseller-portal.tsx b/src/components/aos/views/reseller-portal.tsx old mode 100644 new mode 100755 diff --git a/src/lib/dokploy.ts b/src/lib/dokploy.ts old mode 100644 new mode 100755 diff --git a/src/lib/email.ts b/src/lib/email.ts old mode 100644 new mode 100755 diff --git a/src/lib/graphify.ts b/src/lib/graphify.ts old mode 100644 new mode 100755 diff --git a/src/lib/hetzner-dns.ts b/src/lib/hetzner-dns.ts old mode 100644 new mode 100755 diff --git a/src/lib/stripe/index.ts b/src/lib/stripe/index.ts old mode 100644 new mode 100755 diff --git a/src/middleware.ts b/src/middleware.ts old mode 100644 new mode 100755