Unifi-Voucher-Tool/cron_sync.php
Claude 0b1d4ec3b8
Webhook-Events & Health-Endpoint
- Notifier: controllerUnreachable, loginNewIp, updateAvailable
- Login von neuer IP -> Webhook (Abgleich gegen audit_log)
- cron_sync meldet nicht erreichbare Controller; cron_cleanup prüft täglich
  optional auf Updates und meldet Verfügbarkeit
- health.php: Status-Endpoint (DB; optional Controller per ?deep=1&token=)
2026-06-05 20:49:06 +00:00

234 lines
7.2 KiB
PHP

<?php
/**
* Cron-Script für automatische Voucher-Synchronisation
*
* Aufruf via URL: https://domain.de/cron_sync.php?token=DEIN_TOKEN
* Aufruf via CLI: php cron_sync.php DEIN_TOKEN
*
* Empfohlenes Cron-Intervall: Alle 30 Minuten
*/
// Fehlerbehandlung GANZ am Anfang
error_reporting(E_ALL);
ini_set('display_errors', 0);
// CLI oder Web?
$isCli = php_sapi_name() === 'cli';
// JSON-Header setzen bevor irgendwas anderes passiert
if (!$isCli) {
header('Content-Type: application/json');
}
// ============================================
// FUNKTIONEN ZUERST DEFINIEREN
// ============================================
function logMessage($message, $isCli) {
$timestamp = date('Y-m-d H:i:s');
$logLine = "[$timestamp] $message";
if ($isCli) {
echo $logLine . PHP_EOL;
}
}
function outputResponse($response, $isCli) {
if ($isCli) {
if ($response['success']) {
echo "SUCCESS: " . ($response['message'] ?? 'OK') . PHP_EOL;
if (isset($response['results'])) {
foreach ($response['results'] as $result) {
echo " - {$result['site_name']}: ";
if ($result['error']) {
echo "FEHLER - {$result['error']}" . PHP_EOL;
} else {
$s = $result['stats'];
echo "Gesamt: {$s['total']}, Neu: {$s['new']}, Aktualisiert: {$s['updated']}, Gültig: {$s['valid']}" . PHP_EOL;
}
}
}
} else {
echo "ERROR: " . $response['message'] . PHP_EOL;
}
} else {
echo json_encode($response, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
}
}
// ============================================
// ERROR HANDLER
// ============================================
register_shutdown_function(function() use ($isCli) {
$error = error_get_last();
if ($error && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) {
$response = [
'success' => false,
'message' => 'Fatal Error: ' . $error['message'],
'file' => $error['file'],
'line' => $error['line']
];
outputResponse($response, $isCli);
}
});
set_exception_handler(function($e) use ($isCli) {
$response = [
'success' => false,
'message' => 'Exception: ' . $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine()
];
outputResponse($response, $isCli);
exit;
});
// ============================================
// INCLUDES LADEN
// ============================================
require_once __DIR__ . '/config.php';
require_once __DIR__ . '/includes/Database.php';
require_once __DIR__ . '/includes/UniFiController.php';
require_once __DIR__ . '/includes/Notifier.php';
$db = Database::getInstance();
// ============================================
// TOKEN VALIDIERUNG
// ============================================
$cronToken = $db->getSetting('cron_token', '');
// Token aus GET oder CLI-Argument holen
$providedToken = '';
if ($isCli && isset($argv[1])) {
$providedToken = $argv[1];
} else {
$providedToken = $_GET['token'] ?? '';
}
// Prüfen ob Token konfiguriert und korrekt ist
if (empty($cronToken)) {
outputResponse([
'success' => false,
'message' => 'Kein Cron-Token konfiguriert. Bitte im Admin-Bereich unter Einstellungen generieren.'
], $isCli);
exit;
}
if ($providedToken !== $cronToken) {
outputResponse([
'success' => false,
'message' => 'Ungültiger Token'
], $isCli);
exit;
}
// ============================================
// SYNCHRONISATION STARTEN
// ============================================
logMessage("Starte Voucher-Synchronisation...", $isCli);
$startTime = microtime(true);
$results = [];
$totalStats = [
'sites_processed' => 0,
'sites_failed' => 0,
'vouchers_total' => 0,
'vouchers_new' => 0,
'vouchers_updated' => 0,
'vouchers_valid' => 0,
'vouchers_used' => 0,
'vouchers_expired' => 0
];
try {
// Alle aktiven Sites abrufen
$sites = $db->fetchAll("SELECT * FROM sites WHERE is_active = 1");
if (empty($sites)) {
outputResponse([
'success' => true,
'message' => 'Keine aktiven Sites gefunden',
'results' => []
], $isCli);
exit;
}
logMessage("Gefunden: " . count($sites) . " aktive Site(s)", $isCli);
foreach ($sites as $site) {
logMessage("Synchronisiere Site: {$site['name']} ({$site['site_id']})...", $isCli);
$siteResult = [
'site_id' => $site['id'],
'site_name' => $site['name'],
'stats' => null,
'error' => null
];
try {
$controller = new UniFiController(
$site['unifi_controller_url'],
$site['unifi_username'],
Crypto::decrypt($site['unifi_password']),
$site['site_id']
);
$stats = $controller->syncVouchersToDatabase($db, $site['id']);
$siteResult['stats'] = $stats;
// Gesamtstatistiken aktualisieren
$totalStats['sites_processed']++;
$totalStats['vouchers_total'] += $stats['total'];
$totalStats['vouchers_new'] += $stats['new'];
$totalStats['vouchers_updated'] += $stats['updated'];
$totalStats['vouchers_valid'] += $stats['valid'];
$totalStats['vouchers_used'] += $stats['used'];
$totalStats['vouchers_expired'] += $stats['expired'];
logMessage(" OK - {$stats['total']} Voucher(s), {$stats['new']} neu, {$stats['updated']} aktualisiert", $isCli);
} catch (Exception $e) {
$siteResult['error'] = $e->getMessage();
$totalStats['sites_failed']++;
logMessage(" FEHLER - " . $e->getMessage(), $isCli);
if (class_exists('Notifier')) {
Notifier::controllerUnreachable($site['name'], $e->getMessage());
}
}
$results[] = $siteResult;
}
// Letzte Sync-Zeit speichern
$db->execute(
"INSERT INTO settings (setting_key, setting_value) VALUES ('last_cron_sync', NOW())
ON DUPLICATE KEY UPDATE setting_value = NOW()"
);
$duration = round(microtime(true) - $startTime, 2);
$response = [
'success' => true,
'message' => "Synchronisation abgeschlossen in {$duration}s",
'duration' => $duration,
'total' => $totalStats,
'results' => $results,
'timestamp' => date('Y-m-d H:i:s')
];
logMessage("Synchronisation abgeschlossen in {$duration}s", $isCli);
logMessage("Gesamt: {$totalStats['vouchers_total']} Voucher(s), {$totalStats['vouchers_new']} neu, {$totalStats['sites_failed']} Fehler", $isCli);
} catch (Exception $e) {
$response = [
'success' => false,
'message' => 'Kritischer Fehler: ' . $e->getMessage()
];
logMessage("KRITISCHER FEHLER: " . $e->getMessage(), $isCli);
}
outputResponse($response, $isCli);