Das Wartungs-System des Upkeep AddOns bietet umfassende Funktionen zur Website-Wartung und -Verwaltung. Es ermöglicht die zentrale Steuerung von Frontend- und Backend-Wartungsmodi sowie die Verwaltung von Domain-spezifischen Einstellungen.
- Wartungsseite: Automatische Anzeige einer Wartungsseite für Besucher
- Mehrsprachigkeit: Unterstützung für mehrsprachige Wartungsseiten
- Passwort-Schutz: Optionaler Passwort-geschützter Zugang
- IP-Whitelist: Spezifische IP-Adressen vom Wartungsmodus ausschließen
- Bypass-Parameter: URL-Parameter für direkten Zugang
- Admin-Only Modus: Backend nur für Administratoren zugänglich
- Benutzer-Ausschluss: Nicht-Admin Benutzer werden ausgesperrt
- Sicherheitsmeldung: Informative Meldung für gesperrte Benutzer
- Notfallzugang: Administratoren behalten immer Zugang
- YRewrite Integration: Wartungsmodus pro Domain konfigurierbar
- Domain-spezifisch: Einzelne Domains unabhängig verwalten
- Bulk-Operationen: Alle Domains gleichzeitig sperren/entsperren
- Override-Funktion: Domain-Einstellungen überschreiben globale Einstellungen
Das Wartungs-System ist Teil des Upkeep AddOns und erfordert:
- REDAXO 5.18.1 oder höher
- PHP 8.2 oder höher
- Für Domain-Verwaltung: YRewrite AddOn
- AddOn aktivieren: Upkeep im REDAXO Backend aktivieren
- Berechtigungen setzen: Entsprechende Benutzerrechte vergeben
- Wartungsseite anpassen: Texte und Design konfigurieren
Backend: Upkeep → Wartung → Frontend
Programmatisch:
$addon = rex_addon::get('upkeep');
$addon->setConfig('frontend_active', true);Console Command:
php redaxo/bin/console upkeep:mode frontend on// Wartungsseite aktivieren
$addon->setConfig('frontend_active', true);
// Seitentitel
$addon->setConfig('maintenance_page_title', 'Website wird gewartet');
// Wartungsmeldung
$addon->setConfig('maintenance_page_message', 'Wir führen aktuell Wartungsarbeiten durch. Bitte versuchen Sie es in Kürze erneut.');
// HTTP Status Code
$addon->setConfig('http_status_code', 503);
// Retry-After Header (Sekunden)
$addon->setConfig('retry_after', 3600);Backend: Upkeep → Wartung → Frontend → Mehrsprachigkeit
JSON-Konfiguration:
{
"de": {
"title": "Website wird gewartet",
"message": "Wir führen aktuell Wartungsarbeiten durch."
},
"en": {
"title": "Site under maintenance",
"message": "We are currently performing maintenance work."
},
"fr": {
"title": "Site en maintenance",
"message": "Nous effectuons actuellement des travaux de maintenance."
}
}Programmatisch:
$multilangConfig = [
'enabled' => true,
'default_language' => 'de',
'texts' => [
'de' => [
'title' => 'Website wird gewartet',
'message' => 'Wartungsarbeiten in Bearbeitung...'
],
'en' => [
'title' => 'Site under maintenance',
'message' => 'Maintenance work in progress...'
]
]
];
$addon->setConfig('multilanguage_settings', $multilangConfig);Backend: Upkeep → Wartung → Frontend → Erlaubte IP-Adressen
Konfiguration:
// Einzelne IPs
$allowedIPs = [
'192.168.1.100', // Lokale IP
'203.0.113.0', // Büro IP
'2001:db8::1' // IPv6
];
// IP-Bereiche (CIDR)
$allowedIPRanges = [
'192.168.1.0/24', // Lokales Netzwerk
'203.0.113.0/24', // Büro-Netzwerk
'10.0.0.0/8' // Private Klasse A
];
$addon->setConfig('allowed_ips', implode(',', array_merge($allowedIPs, $allowedIPRanges)));// Frontend-Passwort setzen
$addon->setConfig('frontend_password', password_hash('mein-wartungs-passwort', PASSWORD_DEFAULT));
// Angemeldete Backend-Benutzer ausschließen
$addon->setConfig('bypass_logged_in', true);// Bypass-Parameter aktivieren
$addon->setConfig('allow_bypass_param', true);
// Parameter-Schlüssel definieren
$addon->setConfig('bypass_param_key', 'maintenance_bypass');
// Verwendung: https://ihre-domain.de/?maintenance_bypass=1// HTTP Status Codes
$httpCodes = [
503 => 'Service Unavailable (mit Cache-Control)',
503 => 'Service Unavailable (ohne Cache-Control)',
403 => 'Forbidden',
307 => 'Temporary Redirect'
];
$addon->setConfig('http_status_code', 503);
// Cache-Control Header
$addon->setConfig('cache_control_enabled', true);
// Retry-After Header (empfohlen: 3600 = 1 Stunde)
$addon->setConfig('retry_after', 3600);Backend: Upkeep → Wartung → Backend
Programmatisch:
$addon = rex_addon::get('upkeep');
$addon->setConfig('backend_active', true);Console Command:
php redaxo/bin/console upkeep:mode backend onWenn der Backend-Wartungsmodus aktiviert ist:
- Admin-Zugang: Nur Benutzer mit Admin-Rechten können sich anmelden
- Normale Benutzer: Werden ausgesperrt und sehen eine Wartungsmeldung
- Aktive Sessions: Nicht-Admin Benutzer werden automatisch abgemeldet
- API-Zugriff: APIs sind weiterhin für Administratoren verfügbar
// Backend-Wartungsmeldung
$message = 'Das REDAXO-Backend befindet sich im Wartungsmodus und ist vorübergehend nicht verfügbar. Bitte kontaktieren Sie den Administrator.';
$addon->setConfig('backend_maintenance_message', $message);
// Kontakt-Informationen
$addon->setConfig('backend_contact_info', 'admin@ihre-domain.de');- Notfallzugang: Administratoren behalten immer Zugang
- Database-Zugriff: Bei kritischen Problemen über Datenbank deaktivierbar
- File-Override: Über
.maintenanceDatei im Root-Verzeichnis steuerbar
Voraussetzung: YRewrite AddOn muss installiert und aktiviert sein.
Backend: Upkeep → Wartung → Domains
// Einzelne Domain in Wartung versetzen
$yrewrite = rex_yrewrite::getDomainByName('www.example.com');
if ($yrewrite) {
$domainConfig = [
'domain_id' => $yrewrite->getId(),
'maintenance_active' => true,
'override_global' => true
];
$addon->setConfig('domain_' . $yrewrite->getId(), $domainConfig);
}// Alle Domains sperren
$addon->setConfig('all_domains_locked', true);
// Ausnahme-Domains definieren
$exceptions = ['admin.example.com', 'api.example.com'];
$addon->setConfig('domain_exceptions', $exceptions);function getDomainMaintenanceStatus(): array {
$addon = rex_addon::get('upkeep');
$domains = rex_yrewrite::getDomains();
$status = [];
foreach ($domains as $domain) {
$domainConfig = $addon->getConfig('domain_' . $domain->getId(), []);
$status[$domain->getName()] = [
'id' => $domain->getId(),
'maintenance_active' => $domainConfig['maintenance_active'] ?? false,
'override_global' => $domainConfig['override_global'] ?? false
];
}
return $status;
}Das Wartungs-System bietet API-Endpoints für externe Steuerung:
# Aktivieren
curl -X POST \
"https://ihre-domain.de/api/upkeep/maintenance/frontend" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-d '{"active": true}'
# Status abfragen
curl -X GET \
"https://ihre-domain.de/api/upkeep/maintenance/status" \
-H "Authorization: Bearer YOUR_API_TOKEN"# Aktivieren (nur für Admins)
curl -X POST \
"https://ihre-domain.de/api/upkeep/maintenance/backend" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-d '{"active": true, "message": "Geplante Wartung bis 15:00 Uhr"}'// Webhook bei Wartungsmodus-Änderungen
rex_extension::register('UPKEEP_MAINTENANCE_CHANGED', function($ep) {
$type = $ep->getParam('type'); // 'frontend' oder 'backend'
$active = $ep->getParam('active');
$user = $ep->getParam('user');
// Webhook-URL benachrichtigen
$webhook_url = 'https://monitoring.example.com/webhook';
$payload = [
'event' => 'maintenance_changed',
'type' => $type,
'active' => $active,
'changed_by' => $user->getLogin(),
'timestamp' => date('c')
];
// HTTP POST Request
$context = stream_context_create([
'http' => [
'method' => 'POST',
'header' => 'Content-Type: application/json',
'content' => json_encode($payload)
]
]);
file_get_contents($webhook_url, false, $context);
});# Frontend-Wartung
php redaxo/bin/console upkeep:mode frontend on
php redaxo/bin/console upkeep:mode frontend off
php redaxo/bin/console upkeep:mode frontend status
# Backend-Wartung
php redaxo/bin/console upkeep:mode backend on
php redaxo/bin/console upkeep:mode backend off
php redaxo/bin/console upkeep:mode backend status
# Domain-Wartung
php redaxo/bin/console upkeep:domain lock example.com
php redaxo/bin/console upkeep:domain unlock example.com
php redaxo/bin/console upkeep:domain status# Automatische Wartung um 2:00 Uhr
0 2 * * * /usr/bin/php /path/to/redaxo/bin/console upkeep:mode frontend on
# Wartung nach 6 Stunden beenden
0 8 * * * /usr/bin/php /path/to/redaxo/bin/console upkeep:mode frontend offTemplate-Datei: templates/maintenance.php
<!DOCTYPE html>
<html lang="<?= $language ?>">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><?= htmlspecialchars($title) ?></title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
margin: 0;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.maintenance-container {
text-align: center;
max-width: 600px;
padding: 2rem;
background: rgba(255,255,255,0.1);
border-radius: 20px;
backdrop-filter: blur(10px);
}
.maintenance-icon {
font-size: 4rem;
margin-bottom: 1rem;
}
.maintenance-title {
font-size: 2.5rem;
margin-bottom: 1rem;
font-weight: 300;
}
.maintenance-message {
font-size: 1.2rem;
line-height: 1.6;
margin-bottom: 2rem;
opacity: 0.9;
}
.maintenance-footer {
font-size: 0.9rem;
opacity: 0.7;
}
.language-switcher {
position: absolute;
top: 20px;
right: 20px;
}
.language-switcher a {
color: white;
text-decoration: none;
margin: 0 5px;
padding: 5px 10px;
border-radius: 5px;
background: rgba(255,255,255,0.2);
}
</style>
</head>
<body>
<?php if ($multilanguage_enabled): ?>
<div class="language-switcher">
<?php foreach ($available_languages as $lang => $lang_title): ?>
<a href="?lang=<?= $lang ?>" <?= $lang === $current_language ? 'style="background: rgba(255,255,255,0.3)"' : '' ?>>
<?= strtoupper($lang) ?>
</a>
<?php endforeach; ?>
</div>
<?php endif; ?>
<div class="maintenance-container">
<div class="maintenance-icon">🔧</div>
<h1 class="maintenance-title"><?= htmlspecialchars($title) ?></h1>
<div class="maintenance-message"><?= nl2br(htmlspecialchars($message)) ?></div>
<?php if ($password_required): ?>
<form method="post" style="margin-top: 2rem;">
<input type="password" name="maintenance_password" placeholder="Passwort eingeben"
style="padding: 12px; border: none; border-radius: 5px; margin-right: 10px;">
<button type="submit" style="padding: 12px 24px; background: #4CAF50; color: white; border: none; border-radius: 5px; cursor: pointer;">
Zugang
</button>
</form>
<?php endif; ?>
<div class="maintenance-footer">
<?php if ($retry_after > 0): ?>
<p>Nächster Versuch empfohlen in: <?= gmdate('H:i:s', $retry_after) ?></p>
<?php endif; ?>
<p>© <?= date('Y') ?> <?= rex::getServerName() ?></p>
</div>
</div>
</body>
</html>// Eigene Template-Hooks
rex_extension::register('UPKEEP_MAINTENANCE_TEMPLATE', function($ep) {
$template_vars = $ep->getParam('template_vars');
// Eigene Variablen hinzufügen
$template_vars['company_name'] = 'Ihre Firma GmbH';
$template_vars['contact_email'] = 'support@ihre-domain.de';
$template_vars['estimated_completion'] = '15:00 Uhr';
$ep->setParam('template_vars', $template_vars);
});// Dashboard-Integration
function getMaintenanceStats(): array {
$addon = rex_addon::get('upkeep');
return [
'frontend_active' => $addon->getConfig('frontend_active', false),
'backend_active' => $addon->getConfig('backend_active', false),
'domains_locked' => $addon->getConfig('all_domains_locked', false),
'allowed_ips_count' => count(explode(',', $addon->getConfig('allowed_ips', ''))),
'last_changed' => $addon->getConfig('last_maintenance_change'),
'changed_by' => $addon->getConfig('last_maintenance_user')
];
}// Wartungsänderungen protokollieren
rex_extension::register('UPKEEP_MAINTENANCE_CHANGED', function($ep) {
$logEntry = [
'timestamp' => date('Y-m-d H:i:s'),
'type' => $ep->getParam('type'),
'action' => $ep->getParam('active') ? 'activated' : 'deactivated',
'user' => rex::getUser()?->getLogin() ?? 'system',
'ip' => rex_request::server('REMOTE_ADDR', 'string', ''),
'user_agent' => rex_request::server('HTTP_USER_AGENT', 'string', '')
];
rex_logger::factory()->info('Maintenance mode changed', $logEntry);
});// Health Check Endpoint
rex_extension::register('PACKAGES_INCLUDED', function() {
if (rex_request::get('health-check') === 'maintenance') {
$addon = rex_addon::get('upkeep');
$status = [
'maintenance_active' => [
'frontend' => $addon->getConfig('frontend_active', false),
'backend' => $addon->getConfig('backend_active', false)
],
'timestamp' => time(),
'version' => $addon->getVersion()
];
header('Content-Type: application/json');
echo json_encode($status);
exit;
}
});
// Verwendung: https://ihre-domain.de/?health-check=maintenance// Geplante Wartung mit Vorankündigung
function scheduleMaintenanceWindow(DateTime $start, DateTime $end, string $reason): void {
$addon = rex_addon::get('upkeep');
$maintenanceWindow = [
'start' => $start->format('Y-m-d H:i:s'),
'end' => $end->format('Y-m-d H:i:s'),
'reason' => $reason,
'scheduled_by' => rex::getUser()?->getLogin(),
'created_at' => date('Y-m-d H:i:s')
];
$addon->setConfig('scheduled_maintenance', $maintenanceWindow);
// E-Mail-Benachrichtigung an Administratoren
sendMaintenanceNotification($maintenanceWindow);
}// Wartungsseite testen
function testMaintenancePage(): bool {
$addon = rex_addon::get('upkeep');
// Temporär aktivieren
$originalState = $addon->getConfig('frontend_active', false);
$addon->setConfig('frontend_active', true);
try {
// HTTP Request zur eigenen Domain
$response = file_get_contents(rex::getServer());
$httpCode = $http_response_header[0] ?? '';
// Prüfungen
$tests = [
'http_503' => strpos($httpCode, '503') !== false,
'maintenance_text' => strpos($response, 'Wartung') !== false,
'html_valid' => strpos($response, '</html>') !== false
];
return !in_array(false, $tests, true);
} finally {
// Original-Zustand wiederherstellen
$addon->setConfig('frontend_active', $originalState);
}
}// Automatisches Backup vor Wartungsaktivierung
rex_extension::register('UPKEEP_MAINTENANCE_BEFORE_ACTIVATE', function($ep) {
if ($ep->getParam('type') === 'frontend') {
// Database Backup
$backup = rex_backup::factory();
$backup->create('pre_maintenance_' . date('Y-m-d_H-i-s'));
// File Backup für kritische Dateien
$criticalFiles = [
rex_path::core('config.yml'),
rex_path::addon('upkeep', 'config.yml')
];
foreach ($criticalFiles as $file) {
if (file_exists($file)) {
copy($file, $file . '.backup.' . time());
}
}
}
});// Emergency Rollback über .emergency Datei
function checkEmergencyRollback(): void {
$emergencyFile = rex_path::base('.emergency');
if (file_exists($emergencyFile)) {
$addon = rex_addon::get('upkeep');
// Alle Wartungsmodi deaktivieren
$addon->setConfig('frontend_active', false);
$addon->setConfig('backend_active', false);
$addon->setConfig('all_domains_locked', false);
// Emergency-Datei entfernen
unlink($emergencyFile);
// Log-Eintrag
rex_logger::factory()->emergency('Emergency rollback executed', [
'file' => $emergencyFile,
'timestamp' => date('Y-m-d H:i:s')
]);
}
}
// Bei jedem Request prüfen
rex_extension::register('PACKAGES_INCLUDED', 'checkEmergencyRollback');Lösung 1: Cache leeren
rex_delete_cache();Lösung 2: Template-Pfad prüfen
$templatePath = rex_path::addon('upkeep', 'templates/maintenance.php');
if (!file_exists($templatePath)) {
// Template-Datei fehlt
echo "Template nicht gefunden: " . $templatePath;
}Lösung: Database-Override
UPDATE rex_config
SET value = '0'
WHERE namespace = 'upkeep'
AND key = 'backend_active';Debugging:
$currentIP = rex_request::server('REMOTE_ADDR', 'string', '');
$allowedIPs = explode(',', rex_addon::get('upkeep')->getConfig('allowed_ips', ''));
echo "Aktuelle IP: " . $currentIP . "\n";
echo "Erlaubte IPs: " . implode(', ', $allowedIPs) . "\n";
foreach ($allowedIPs as $allowedIP) {
if (trim($allowedIP) === $currentIP) {
echo "IP gefunden!\n";
break;
}
}// Debug-Informationen aktivieren
$addon = rex_addon::get('upkeep');
$addon->setConfig('maintenance_debug', true);
// Debug-Log prüfen
$debugLog = rex_path::addonData('upkeep', 'maintenance_debug.log');
if (file_exists($debugLog)) {
echo file_get_contents($debugLog);
}// Starke API-Token generieren
function generateSecureApiToken(): string {
return base64_encode(random_bytes(32));
}
// Token regelmäßig rotieren
function rotateApiToken(): void {
$addon = rex_addon::get('upkeep');
$newToken = generateSecureApiToken();
$addon->setConfig('api_token', $newToken);
// Alte Token ungültig machen
rex_logger::factory()->info('API token rotated');
}// Rate Limiting für Wartungsseiten-Zugriffe
function checkMaintenanceRateLimit(string $ip): bool {
$attempts = rex_cache::get('maintenance_attempts_' . $ip, 0);
if ($attempts >= 5) {
// IP für 1 Stunde sperren
rex_cache::set('maintenance_blocked_' . $ip, true, 3600);
return false;
}
return true;
}// Sichere Standard-Konfiguration
$secureDefaults = [
'frontend_password' => null, // Kein Standard-Passwort
'bypass_param_key' => bin2hex(random_bytes(16)), // Zufälliger Parameter
'api_token' => null, // API standardmäßig deaktiviert
'allowed_ips' => '', // Keine Standard-IPs
'http_status_code' => 503, // Korrekte HTTP-Codes
'retry_after' => 3600 // Angemessene Retry-Zeit
];- ✅ Gruppierung in Wartung-Bereich
- ✅ Frontend-Wartung mit Mehrsprachigkeit
- ✅ Backend-Wartung für Admin-Only Modus
- ✅ Domain-spezifische Wartung
- ✅ Console Commands
- ✅ API Integration
- ✅ Template-System
- ✅ Monitoring & Logging
- 🔄 Geplante Wartungsfenster
- 🔄 Progressive Web App Support
- 🔄 Social Media Integration
- 🔄 Advanced Analytics
- 🔄 Mobile Benachrichtigungen
Für Support und weitere Informationen:
- GitHub: https://github.com/klxm/upkeep
- Issues: https://github.com/klxm/upkeep/issues
- Dokumentation: Backend → Upkeep → Wartung → Wartung Dokumentation
- REDAXO Forum: https://www.redaxo.org/forum/
Diese Dokumentation wird kontinuierlich aktualisiert. Letzte Aktualisierung: September 2025