prepare('SELECT l.*, p.slug as product_slug FROM license_licenses l JOIN dc_projects p ON l.product_id = p.id WHERE l.id = :id');
$stmt->execute([':id' => $licId]);
$lic = $stmt->fetch();
if ($lic) {
$payload = [
'type' => 'offline_license_file',
'issued_at' => time(),
'product' => $lic['product_slug'],
'license_key' => $lic['license_key'],
'customer' => $lic['customer_name'] ?? 'Universal',
'valid_until' => $lic['expires_at'] ? strtotime($lic['expires_at']) : strtotime('+1 year'),
'signature' => 'ED25519_SIG_' . base64_encode(hash('sha256', $lic['license_key'] . 'DC_OFFLINE_SECRET', true))
];
$jsonContent = json_encode($payload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
$filename = sprintf('%s_%s_offline.lic', $lic['product_slug'], substr($lic['license_key'], 0, 5));
header('Content-Type: application/json');
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Content-Length: ' . strlen($jsonContent));
echo $jsonContent;
exit;
}
}
// Download Handler: Watchdog Agent Installer Script (.ps1 or .sh)
if (isset($_GET['action']) && $_GET['action'] === 'download_agent') {
$source = trim($_GET['source'] ?? 'server-node');
$os = trim($_GET['os'] ?? 'windows');
$token = trim($_GET['token'] ?? 'wd_live_token_default');
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
$host = $_SERVER['HTTP_HOST'] ?? 'dc.mhdf.de';
$baseUrl = $protocol . '://' . $host;
if ($os === 'windows') {
$script = "# Deploymentcenter Watchdog Agent Installer (Windows PowerShell)\n"
. "\$WatchdogUrl = \"{$baseUrl}/api/watchdog/v1/ping\"\n"
. "\$Token = \"{$token}\"\n"
. "\$Source = \"{$source}\"\n"
. "Write-Host \"🚀 Initialisiere Watchdog Agent für \$Source...\" -ForegroundColor Cyan\n"
. "\$body = @{ source = \$Source; status = 'ok'; message = 'Heartbeat via PowerShell Task'; interval = 60 } | ConvertTo-Json\n"
. "Invoke-RestMethod -Uri \$WatchdogUrl -Method Post -Body \$body -ContentType 'application/json' -Headers @{ 'X-Agent-Token' = \$Token }\n"
. "Write-Host \"[✔] Heartbeat erfolgreich gesendet!\" -ForegroundColor Green\n";
header('Content-Type: application/octet-stream');
header("Content-Disposition: attachment; filename=\"watchdog-install-{$source}.ps1\"");
echo $script;
exit;
} else {
$script = "#!/usr/bin/env bash\n"
. "WATCHDOG_URL=\"{$baseUrl}/api/watchdog/v1/ping\"\n"
. "TOKEN=\"{$token}\"\n"
. "SOURCE=\"{$source}\"\n"
. "echo \"🚀 Initialisiere Watchdog Agent für \$SOURCE...\"\n"
. "curl -X POST \"\$WATCHDOG_URL\" -H \"Content-Type: application/json\" -H \"X-Agent-Token: \$TOKEN\" -d '{\"source\": \"'\"\$SOURCE\"'\", \"status\": \"ok\", \"message\": \"Heartbeat via Bash Cron\", \"interval\": 60}'\n"
. "echo \"[✔] Heartbeat gesendet!\"\n";
header('Content-Type: application/octet-stream');
header("Content-Disposition: attachment; filename=\"watchdog-install-{$source}.sh\"");
echo $script;
exit;
}
}
// Handle POST actions
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$action = $_POST['action'] ?? '';
// Create / Edit Project
if ($action === 'save_project') {
$id = (int)($_POST['project_id'] ?? 0);
$slug = trim($_POST['slug'] ?? '');
$name = trim($_POST['name'] ?? '');
$ttl = (int)($_POST['ttl'] ?? 168);
$notes = trim($_POST['notes'] ?? '');
if ($slug && $name) {
try {
if ($id > 0) {
$stmt = $pdo->prepare('UPDATE dc_projects SET slug = :s, name = :n, default_cache_ttl_hours = :t, notes = :notes WHERE id = :id');
$stmt->execute([':s' => $slug, ':n' => $name, ':t' => $ttl, ':notes' => $notes, ':id' => $id]);
$msg = "Projekt '{$name}' wurde aktualisiert.";
} else {
$stmt = $pdo->prepare('INSERT INTO dc_projects (slug, name, default_cache_ttl_hours, notes) VALUES (:s, :n, :t, :notes)');
$stmt->execute([':s' => $slug, ':n' => $name, ':t' => $ttl, ':notes' => $notes]);
$msg = "Neues Projekt '{$name}' angelegt.";
}
} catch (Throwable $e) {
$msg = "Fehler beim Speichern des Projekts: " . $e->getMessage();
$msgType = 'danger';
}
}
}
// Create License Key
if ($action === 'create_license') {
$productId = (int)($_POST['product_id'] ?? 0);
$customerName = trim($_POST['customer_name'] ?? '');
$customerEmail = trim($_POST['customer_email'] ?? '');
$maxActivations = (int)($_POST['max_activations'] ?? 2);
$expiresAt = !empty($_POST['expires_at']) ? $_POST['expires_at'] . ' 23:59:59' : null;
$notes = trim($_POST['notes'] ?? '');
if ($productId > 0) {
$licenseKey = KeyGen::generateKey();
try {
$stmt = $pdo->prepare('
INSERT INTO license_licenses (product_id, license_key, customer_name, customer_email, max_activations, expires_at, notes)
VALUES (:pid, :key, :cname, :cemail, :max, :exp, :notes)
');
$stmt->execute([
':pid' => $productId,
':key' => $licenseKey,
':cname' => $customerName,
':cemail' => $customerEmail,
':max' => $maxActivations,
':exp' => $expiresAt,
':notes' => $notes
]);
$pdo->prepare('INSERT INTO license_audit_log (actor, action, details) VALUES ("admin", "license.create", :d)')
->execute([':d' => json_encode(['key' => $licenseKey, 'customer' => $customerName])]);
$msg = "Lizenzschlüssel generiert: {$licenseKey} ";
} catch (Throwable $e) {
$msg = "Fehler bei Generierung: " . $e->getMessage();
$msgType = 'danger';
}
}
}
// Edit License
if ($action === 'edit_license') {
$id = (int)($_POST['license_id'] ?? 0);
$customerName = trim($_POST['customer_name'] ?? '');
$customerEmail = trim($_POST['customer_email'] ?? '');
$status = $_POST['status'] ?? 'active';
$maxActivations = (int)($_POST['max_activations'] ?? 2);
$expiresAt = !empty($_POST['expires_at']) ? $_POST['expires_at'] . ' 23:59:59' : null;
$notes = trim($_POST['notes'] ?? '');
if ($id > 0) {
$stmt = $pdo->prepare('
UPDATE license_licenses
SET customer_name = :cname, customer_email = :cemail, status = :status, max_activations = :max_act, expires_at = :exp, notes = :notes
WHERE id = :id
');
$stmt->execute([
':cname' => $customerName,
':cemail' => $customerEmail,
':status' => $status,
':max_act' => $maxActivations,
':exp' => $expiresAt,
':notes' => $notes,
':id' => $id
]);
$pdo->prepare('INSERT INTO license_audit_log (actor, action, details) VALUES ("admin", "license.update", :d)')
->execute([':d' => json_encode(['license_id' => $id, 'status' => $status])]);
$msg = "Lizenzdaten wurden erfolgreich aktualisiert.";
}
}
// Revoke / Re-activate License
if ($action === 'toggle_license_status') {
$licId = (int)($_POST['license_id'] ?? 0);
$newStatus = $_POST['new_status'] ?? 'revoked';
if ($licId > 0) {
$stmt = $pdo->prepare('UPDATE license_licenses SET status = :s WHERE id = :id');
$stmt->execute([':s' => $newStatus, ':id' => $licId]);
$msg = "Lizenz-Status geändert zu: " . strtoupper($newStatus);
}
}
// Toggle Hardware Activation Block
if ($action === 'toggle_block_activation') {
$actId = (int)($_POST['activation_id'] ?? 0);
$block = (int)($_POST['block_state'] ?? 0);
if ($actId > 0) {
$stmt = $pdo->prepare('UPDATE license_activations SET is_blocked = :b WHERE id = :id');
$stmt->execute([':b' => $block, ':id' => $actId]);
$msg = $block ? "Hardware-Aktivierung wurde gesperrt." : "Hardware-Aktivierung wurde entsperrt.";
}
}
// Add Watchdog Host Machine / Application
if ($action === 'add_watchdog_monitor') {
$source = trim($_POST['source'] ?? '');
$type = $_POST['type'] ?? 'heartbeat';
$group = trim($_POST['group'] ?? 'Default');
$os = trim($_POST['os'] ?? 'Linux');
$interval = (int)($_POST['interval'] ?? 60);
if ($source) {
$stmt = $pdo->prepare('
INSERT INTO watchdog_monitors (source, instance, type, state, expected_interval_sec, group_key, os, created_utc, updated_utc)
VALUES (:source, "default", :type, "stopped", :interval, :group, :os, NOW(), NOW())
ON DUPLICATE KEY UPDATE expected_interval_sec = VALUES(expected_interval_sec), group_key = VALUES(group_key)
');
$stmt->execute([':source' => $source, ':type' => $type, ':interval' => $interval, ':group' => $group, ':os' => $os]);
$tokMgr = new TokenManager($pdo);
$tok = $tokMgr->createToken($source, "Token for {$source}");
$msg = "Monitor '{$source}' angelegt. Agent Token: {$tok['raw_token']} ";
}
}
// Create Agent Token (Watchdog)
if ($action === 'create_agent_token') {
$source = trim($_POST['token_source'] ?? '');
$name = trim($_POST['token_name'] ?? '');
if ($name) {
$tokMgr = new TokenManager($pdo);
$res = $tokMgr->createToken($source, $name);
$msg = "Agent-Token für '{$name}' generiert: {$res['raw_token']} ";
}
}
// Revoke Agent Token
if ($action === 'revoke_agent_token') {
$tokId = trim($_POST['token_id'] ?? '');
if ($tokId) {
$stmt = $pdo->prepare('UPDATE watchdog_agent_tokens SET revoked = 1 WHERE token_id = :id');
$stmt->execute([':id' => $tokId]);
$msg = "Agent-Token wurde widerrufen.";
}
}
// Add Update Release
if ($action === 'add_release') {
$productSlug = trim($_POST['product_slug'] ?? '');
$version = trim($_POST['version'] ?? '');
$url = trim($_POST['download_url'] ?? '');
$hash = trim($_POST['sha256_hash'] ?? '');
$notes = trim($_POST['release_notes'] ?? '');
$critical = isset($_POST['is_critical']);
if ($productSlug && $version && $url) {
$updMgr = new UpdateManager($pdo);
if ($updMgr->addRelease($productSlug, $version, $notes, $url, $hash, $critical)) {
$msg = "Release v{$version} für Projekt '{$productSlug}' veröffentlicht.";
} else {
$msg = "Fehler beim Speichern des Releases.";
$msgType = 'danger';
}
}
}
}
// Fetch All Data
$projects = $pdo->query('SELECT * FROM dc_projects ORDER BY name ASC')->fetchAll();
$licenses = $pdo->query('
SELECT l.*, p.name as product_name, p.slug as product_slug,
(SELECT COUNT(*) FROM license_activations a WHERE a.license_id = l.id) as active_count
FROM license_licenses l
JOIN dc_projects p ON l.product_id = p.id
ORDER BY l.created_at DESC
')->fetchAll();
$activations = $pdo->query('
SELECT a.*, l.license_key, p.slug as product_slug, p.name as product_name
FROM license_activations a
JOIN license_licenses l ON a.license_id = l.id
JOIN dc_projects p ON l.product_id = p.id
ORDER BY a.last_seen DESC
')->fetchAll();
$auditLogs = $pdo->query('SELECT * FROM license_audit_log ORDER BY created_at DESC LIMIT 100')->fetchAll();
$monitorRepo = new MonitorRepo($pdo);
$monitors = $monitorRepo->getAllMonitors();
$monitorsUp = 0; $monitorsWarning = 0; $monitorsDown = 0;
foreach ($monitors as $m) {
if ($m['state'] === 'up') $monitorsUp++;
elseif ($m['state'] === 'warning') $monitorsWarning++;
else $monitorsDown++;
}
$eventLog = new EventLog($pdo);
$recentEvents = $eventLog->getRecentEvents(100);
$tokenManager = new TokenManager($pdo);
$agentTokens = $tokenManager->getAllTokens();
$updateMgr = new UpdateManager($pdo);
$releases = $updateMgr->getReleases();
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
$host = $_SERVER['HTTP_HOST'] ?? 'dc.mhdf.de';
$baseUrl = $protocol . '://' . $host;
$pubKeyB64 = 'Fehlt (Ed25519 Key Server Default)';
?>
Deploymentcenter - Unified Operations Center
= $msg ?>
= $monitorsUp ?> /
= $monitorsWarning ?> /
= $monitorsDown ?>
Source / Instanz
Typ
Gruppe
Status
Letzte Meldung
Zuletzt Gesehen
= htmlspecialchars($m['source']) ?> (= htmlspecialchars($m['instance']) ?>)
= htmlspecialchars($m['type']) ?>
= htmlspecialchars($m['group_key'] ?? 'Default') ?>
● = strtoupper($m['state']) ?>
= htmlspecialchars($m['last_message'] ?? '-') ?>
= htmlspecialchars($m['last_seen_utc'] ?? 'Nie') ?>
ID
Slug
Projekt Name
Cache TTL
Verknüpfte Lizenzen
Verknüpfte Releases
Erstellt am
$l['product_slug'] === $p['slug']));
$relCount = count(array_filter($releases, fn($r) => $r['product_slug'] === $p['slug']));
?>
= $p['id'] ?>
= htmlspecialchars($p['slug']) ?>
= htmlspecialchars($p['name']) ?>
= $p['default_cache_ttl_hours'] ?> h (= round($p['default_cache_ttl_hours']/24, 1) ?> Tage)
= $licCount ?> Lizenzen
= $relCount ?> Releases
= $p['created_at'] ?>
📊 Dashboard
🔑 Lizenzverwaltung
🔍 Lizenz-Details & Hardware
💾 Offline-Lizenzen (.lic)
📜 Audit-Log
💻 Integration & Prompts
⚙️ Einstellungen
= count(array_filter($licenses, fn($l) => $l['status'] === 'active')) ?>
Projekt
Lizenzschlüssel
Kunde
Aktivierungen
Status
Ablaufdatum
Aktionen
= htmlspecialchars($l['product_name']) ?>
= htmlspecialchars($l['license_key']) ?>
= htmlspecialchars($l['customer_name'] ?? '-') ?>
= $l['active_count'] ?> / = $l['max_activations'] ?>
= strtoupper($l['status']) ?>
= $l['expires_at'] ? htmlspecialchars($l['expires_at']) : 'Unbefristet' ?>
📥 .lic
Projekt
Lizenzschlüssel
Hardware-ID
Hostname
App-Version
Zuletzt gesehen
Status
Aktion
= htmlspecialchars($a['product_name']) ?>
= htmlspecialchars($a['license_key']) ?>
= htmlspecialchars($a['hardware_id']) ?>
= htmlspecialchars($a['hostname'] ?? '-') ?>
= htmlspecialchars($a['app_version'] ?? '-') ?>
= htmlspecialchars($a['last_seen']) ?>
GESPERRT
AKTIV
Erzeugt eine signierte .lic Offline-Lizenzdatei für Air-Gapped Kundensysteme.
Offline Payload Generieren
Generierte Offline .lic Payload (JSON)
Wählen Sie oben eine Lizenz aus und klicken Sie auf 'Offline Payload Generieren'.
ID
Zeitpunkt
Akteur
Aktion
Details
= $log['id'] ?>
= $log['created_at'] ?>
= htmlspecialchars($log['actor']) ?>
= htmlspecialchars($log['action']) ?>
= htmlspecialchars($log['details'] ?? '-') ?>
C# / .NET Project Prompt
Ich möchte mein C# / .NET Projekt mit dem Lizenzen-Modul des Deploymentcenters verbinden.
Server-Konfiguration:
- Base API URL: = $baseUrl ?>/api/license/v1
- Endpunkte: /validate (POST), /deactivate (POST)
- Public Key: MCowBQYDK2VwAyEA9f8J7K2mX4vQ8n1L6s5t4r3q2p1o0n9m8l7k6j5h4g3f
- Produkt-Slug: myapp
Anforderungen:
1. Erstelle eine C# Klasse `LicenseValidator.cs`.
2. Sende beim Anwendungsstart einen POST-Request an `= $baseUrl ?>/api/license/v1/validate` mit JSON:
{ "product": "myapp", "license_key": "XXXXX-XXXXX-XXXXX-XXXXX-XXXXX", "hardware_id": getHardwareId(), "nonce": Guid.NewGuid().ToString() }
3. Werte den Status aus ("valid", "revoked", "expired", "activation_limit").
4. Bei Server-Unerreichbarkeit: Erlaube Offline-Nutzung innerhalb des Cache-TTL Zeitraums.
📋 Prompt Kopieren (C#)
📊 Dashboard
➕ Monitor Hinzufügen
⏱️ Uptime Monitor
⚙️ Services
💻 Applications
🖥️ Machines
☁️ Hypervisors
📜 Event-Log
🔑 Agent-Tokens
💻 Integration & Prompts
= htmlspecialchars($m['source']) ?> (= htmlspecialchars($m['os'] ?? 'Host') ?>)
= strtoupper($m['state']) ?>
CPU Auslastung = $metrics['cpu'] ?? 0 ?>%
= $pct ? (($metrics['cpu']??0)>85?'filled-crit':(($metrics['cpu']??0)>70?'filled-warn':'filled-ok')) : ''; ?>
Monitor
Gruppe
Erwartetes Intervall
Status
Zuletzt Gesehen
Skript Download
= htmlspecialchars($m['source']) ?>
= htmlspecialchars($m['group_key'] ?? 'Default') ?>
Alle = $m['expected_interval_sec'] ?>s
= strtoupper($m['state']) ?>
= htmlspecialchars($m['last_seen_utc'] ?? 'Nie') ?>
📥 .ps1 (Windows)
📥 .sh (Linux)
Dienst-Name
Gruppe
Status
Letzte Meldung
$m['group_key'] === 'Services') as $m): ?>
= htmlspecialchars($m['source']) ?>
= htmlspecialchars($m['group_key']) ?>
= strtoupper($m['state']) ?>
= htmlspecialchars($m['last_message'] ?? '-') ?>
Anwendung
Umgebung / OS
Status
Letztes Signal
$m['group_key'] === 'Applications') as $m): ?>
= htmlspecialchars($m['source']) ?>
= htmlspecialchars($m['os'] ?? '.NET') ?>
ONLINE
= htmlspecialchars($m['last_seen_utc']) ?>
Host Name
Betriebssystem
Status
CPU / RAM Snapshot
$m['type'] === 'host') as $m): ?>
= htmlspecialchars($m['source']) ?>
= htmlspecialchars($m['os'] ?? 'Linux/Windows') ?>
ONLINE
CPU: = $metrics['cpu'] ?? 0 ?>% | RAM: = $metrics['ram'] ?? 0 ?>%
Node / Guest
Typ
Zugehöriger Node
Status
in_array($m['type'], ['hypervisor_node', 'guest'])) as $m): ?>
= htmlspecialchars($m['source']) ?>
= htmlspecialchars($m['type']) ?>
= htmlspecialchars($m['group_key'] ?? '-') ?>
= strtoupper($m['state']) ?>
Zeitpunkt (UTC)
Source
Kind
Severity
Nachricht
= htmlspecialchars($e['at_utc']) ?>
= htmlspecialchars($e['source']) ?>
= htmlspecialchars($e['kind']) ?>
= strtoupper($e['severity']) ?>
= htmlspecialchars($e['message'] ?? '-') ?>
Token ID
Bezeichnung
Token Value
Gebundene Source
Status
Aktion
= htmlspecialchars($tok['token_id']) ?>
= htmlspecialchars($tok['name']) ?>
= htmlspecialchars($masked) ?>
👁️
📋
= htmlspecialchars($tok['monitor_source'] ?? 'Alle Sources') ?>
= $tok['revoked'] ? 'WIDERUFEN' : 'AKTIV' ?>
C# Background Service Heartbeat Prompt
Ich möchte in meinem C# .NET Worker Service einen Watchdog-Heartbeat einbauen.
Ping-URL: = $baseUrl ?>/api/watchdog/v1/ping
HTTP Method: POST
JSON-Payload:
{
"source": "MyWorkerService",
"status": "ok",
"message": "Processing queue",
"expected_interval_sec": 60
}
Bitte schreibe einen IHostedService in C#, der alle 30 Sekunden automatisch diesen Heartbeat-Ping an das Deploymentcenter sendet.
📋 Prompt Kopieren (C# Worker)
📊 Releases Overview
➕ Release Veröffentlichen
💻 Integration & Prompts
Projekt
Version
Release Notes
Download URL
Datum
= htmlspecialchars($r['product_slug']) ?>
v= htmlspecialchars($r['version']) ?>
= htmlspecialchars($r['release_notes'] ?? '-') ?>
= htmlspecialchars($r['download_url']) ?>
= htmlspecialchars($r['created_at']) ?>
Ich möchte in meiner Client-Anwendung eine automatische Update-Prüfung einbauen.
Update-Check URL: = $baseUrl ?>/api/updateservice/v1/check?product=myapp&version=1.0.0
Bitte erstelle ein Modul, das beim Start prüft, ob `update_available` true ist, und dem Nutzer den Download-Link sowie die Release Notes anzeigt.
📋 Prompt Kopieren
⚙️ System-Status
🗄️ Daten-Migration & Repair