diff --git a/CHANGELOG.md b/CHANGELOG.md index eb4dc51..0e6a81b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,3 +50,4 @@ Alle Einträge verwenden ISO-8601-Timestamps in UTC. Mehrere Änderungen desselb - Administratoren erhalten bei fehlgeschlagenen Instanzaktionen einen bereinigten technischen Diagnosehinweis inklusive cURL-Fehlerdetails bei Verbindungsproblemen. - Löschaktionen verwenden jetzt den korrekten Docker-API-Endpunkt mit `force=true`. - Als gelöscht markierte Instanzen werden nicht mehr im Dashboard angezeigt; die Datenbankhistorie bleibt erhalten. +- Tarife zeigen Brutto-Monats- und Jahrespreise; die Jahrespreise werden mit Rabatt gespeichert. Tarif S umfasst jetzt 5 GB Speicher. diff --git a/dashboard.php b/dashboard.php index 936d241..9211972 100644 --- a/dashboard.php +++ b/dashboard.php @@ -34,12 +34,13 @@ $message = null; $error = null; $plans = [ - 's' => ['label' => 'S', 'memory' => 512, 'cpu' => 1.00, 'storage' => 1024], - 'm' => ['label' => 'M', 'memory' => 2048, 'cpu' => 2.00, 'storage' => 10240], - 'l' => ['label' => 'L', 'memory' => 4096, 'cpu' => 4.00, 'storage' => 25600], + 's' => ['label' => 'S', 'memory' => 512, 'cpu' => 1.00, 'storage' => 5120, 'monthly_cents' => 299, 'yearly_cents' => 2999], + 'm' => ['label' => 'M', 'memory' => 2048, 'cpu' => 2.00, 'storage' => 10240, 'monthly_cents' => 399, 'yearly_cents' => 3999], + 'l' => ['label' => 'L', 'memory' => 4096, 'cpu' => 4.00, 'storage' => 25600, 'monthly_cents' => 499, 'yearly_cents' => 4999], ]; function dashboard_escape(string $value): string { return htmlspecialchars($value, ENT_QUOTES, 'UTF-8'); } +function dashboard_money(int $cents): string { return $cents > 0 ? number_format($cents / 100, 2, ',', '.') . ' €' : '–'; } function dashboard_status(string $status): string { return match ($status) { 'pending_payment' => 'Zahlung ausstehend', @@ -67,11 +68,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { if ($action === 'request_instance') { $name = trim((string)($_POST['name'] ?? '')); $planKey = strtolower(trim((string)($_POST['plan'] ?? ''))); + $billingPeriod = (string)($_POST['billing_period'] ?? ''); $paymentMethod = (string)($_POST['payment_method'] ?? 'bank_transfer'); if ($name === '' || !preg_match('/^[A-Za-z0-9][A-Za-z0-9 _.-]{1,119}$/', $name)) { throw new RuntimeException('Bitte einen gültigen Instanznamen eingeben.'); } - if (!isset($plans[$planKey]) || !in_array($paymentMethod, ['paypal', 'bank_transfer'], true)) { + if (!isset($plans[$planKey]) || !in_array($billingPeriod, ['monthly', 'yearly'], true) || !in_array($paymentMethod, ['paypal', 'bank_transfer'], true)) { throw new RuntimeException('Tarif oder Zahlungsart ist ungültig.'); } $slug = strtolower(preg_replace('/[^a-z0-9]+/i', '-', $name) ?? 'foundry'); @@ -79,8 +81,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { $port = (int)$db->query('SELECT COALESCE(MAX(port), 29999) + 1 FROM instances')->fetchColumn(); if ($port > 60000) throw new RuntimeException('Es sind derzeit keine Ports verfügbar.'); $p = $plans[$planKey]; - $statement = $db->prepare('INSERT INTO instances (owner_user_id, name, plan, payment_status, payment_method, container_name, port, volume_size_mb, memory_limit_mb, cpu_limit, status) VALUES (:owner, :name, :plan, "pending", :method, :container, :port, :storage, :memory, :cpu, "pending_payment")'); - $statement->execute(['owner' => $userId, 'name' => $name, 'plan' => $planKey, 'method' => $paymentMethod, 'container' => $containerName, 'port' => $port, 'storage' => $p['storage'], 'memory' => $p['memory'], 'cpu' => $p['cpu']]); + $statement = $db->prepare('INSERT INTO instances (owner_user_id, name, plan, billing_period, price_gross_cents, payment_status, payment_method, container_name, port, volume_size_mb, memory_limit_mb, cpu_limit, status) VALUES (:owner, :name, :plan, :billing_period, :price_gross_cents, "pending", :method, :container, :port, :storage, :memory, :cpu, "pending_payment")'); + $statement->execute(['owner' => $userId, 'name' => $name, 'plan' => $planKey, 'billing_period' => $billingPeriod, 'price_gross_cents' => $p[$billingPeriod . '_cents'], 'method' => $paymentMethod, 'container' => $containerName, 'port' => $port, 'storage' => $p['storage'], 'memory' => $p['memory'], 'cpu' => $p['cpu']]); $message = 'Die Instanzanfrage wurde erfasst und wartet auf die Zahlungsfreigabe.'; } elseif ($action === 'approve_instance' && $isAdmin) { $instanceId = (int)($_POST['instance_id'] ?? 0); @@ -301,7 +303,7 @@ function dashboard_bytes(int $bytes): string {
Neue Buchung
Provisionierung
| Name | Benutzer | Tarif | Zahlung | Status | Adresse | Aktion |
|---|---|---|---|---|---|---|
| = dashboard_escape((string)$instance['name']) ?> | = dashboard_escape((string)$instance['username']) ?> | = strtoupper(dashboard_escape((string)$instance['plan'])) ?> | = dashboard_escape((string)$instance['payment_status']) ?> | = dashboard_escape(dashboard_status((string)$instance['status'])) ?> = dashboard_escape((string)$instance['error_message']) ?> | := dashboard_escape((string)$instance['port']) ?>– | – |
| Name | Benutzer | Tarif | Preis | Zahlung | Status | Adresse | Aktion |
|---|---|---|---|---|---|---|---|
| = dashboard_escape((string)$instance['name']) ?> | = dashboard_escape((string)$instance['username']) ?> | = strtoupper(dashboard_escape((string)$instance['plan'])) ?> | = dashboard_escape(dashboard_money((int)($instance['price_gross_cents'] ?? 0))) ?> (= (string)($instance['billing_period'] ?? 'monthly') === 'yearly' ? 'jährlich' : 'monatlich' ?>) | = dashboard_escape((string)$instance['payment_status']) ?> | = dashboard_escape(dashboard_status((string)$instance['status'])) ?> = dashboard_escape((string)$instance['error_message']) ?> | := dashboard_escape((string)$instance['port']) ?>– | – |