Upload via GUI (340 Dateien)
This commit is contained in:
@@ -1542,6 +1542,255 @@ function run_migrations(PDO $pdo, bool $verbose = false): array
|
||||
}
|
||||
}
|
||||
|
||||
// Migration 0055: Status-Komponenten lassen sich für Anzeige und Prüfungen aktivieren/deaktivieren.
|
||||
if (!isset($done['php_0055_status_component_enabled'])) {
|
||||
try {
|
||||
$drv = $pdo->getAttribute(PDO::ATTR_DRIVER_NAME);
|
||||
$cols = $pdo->query($drv === 'sqlite' ? 'PRAGMA table_info(status_components)' : 'SHOW COLUMNS FROM status_components')->fetchAll();
|
||||
$hasEnabled = false;
|
||||
foreach ($cols as $col) if (($col['name'] ?? $col['Field'] ?? '') === 'is_enabled') $hasEnabled = true;
|
||||
if (!$hasEnabled) $pdo->exec('ALTER TABLE status_components ADD COLUMN is_enabled TINYINT NOT NULL DEFAULT 1');
|
||||
$categoryStmt = $pdo->prepare('SELECT id FROM status_categories WHERE LOWER(name) = LOWER(?) LIMIT 1');
|
||||
$categoryStmt->execute(['Apps']);
|
||||
$gameCategoryId = $categoryStmt->fetchColumn();
|
||||
if (!$gameCategoryId) {
|
||||
$categoryOrder = (int) $pdo->query('SELECT COALESCE(MAX(sort_order), 0) FROM status_categories')->fetchColumn() + 10;
|
||||
$pdo->prepare('INSERT INTO status_categories (name, sort_order) VALUES (?,?)')->execute(['Apps', $categoryOrder]);
|
||||
$gameCategoryId = (int) $pdo->lastInsertId();
|
||||
}
|
||||
$minecraftStmt = $pdo->query("SELECT id, category_id, category FROM status_components WHERE LOWER(name) LIKE '%minecraft%' ORDER BY id LIMIT 1");
|
||||
$minecraft = $minecraftStmt->fetch(PDO::FETCH_ASSOC);
|
||||
if (!$minecraft) {
|
||||
$sortOrder = (int) $pdo->query('SELECT COALESCE(MAX(sort_order), 0) FROM status_components')->fetchColumn() + 10;
|
||||
$insert = $pdo->prepare('INSERT INTO status_components (name, description, status, sort_order, created_at, category_id, monitor_type, is_enabled) VALUES (?,?,?,?,?,?,?,0)');
|
||||
$insert->execute(['Minecraft-Server', 'Minecraft Java – IP-Adresse oder Domain und Port eintragen.', 'operational', $sortOrder, db_now(), (int) $gameCategoryId, 'tcp']);
|
||||
} elseif (empty($minecraft['category_id']) && trim((string) ($minecraft['category'] ?? '')) === '') {
|
||||
$pdo->prepare('UPDATE status_components SET category_id = ? WHERE id = ?')->execute([(int) $gameCategoryId, (int) $minecraft['id']]);
|
||||
}
|
||||
$pdo->prepare('INSERT INTO schema_migrations (migration) VALUES (?)')->execute(['php_0055_status_component_enabled']);
|
||||
$result['applied']++;
|
||||
if ($verbose) $result['log'][] = ' [OK] php_0055_status_component_enabled';
|
||||
} catch (Throwable $e) {
|
||||
$result['errors'][] = 'php_0055_status_component_enabled: ' . $e->getMessage();
|
||||
if ($verbose) $result['log'][] = ' [FAIL] php_0055_status_component_enabled - ' . $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
// Migration 0056: Minecraft-Statusdienst der Kategorie „Apps“ zuordnen.
|
||||
if (!isset($done['php_0056_minecraft_status_category'])) {
|
||||
try {
|
||||
$categoryStmt = $pdo->prepare('SELECT id FROM status_categories WHERE LOWER(name) = LOWER(?) LIMIT 1');
|
||||
$categoryStmt->execute(['Apps']);
|
||||
$gameCategoryId = $categoryStmt->fetchColumn();
|
||||
if (!$gameCategoryId) {
|
||||
$categoryOrder = (int) $pdo->query('SELECT COALESCE(MAX(sort_order), 0) FROM status_categories')->fetchColumn() + 10;
|
||||
$pdo->prepare('INSERT INTO status_categories (name, sort_order) VALUES (?,?)')->execute(['Apps', $categoryOrder]);
|
||||
$gameCategoryId = (int) $pdo->lastInsertId();
|
||||
}
|
||||
$pdo->prepare("UPDATE status_components SET category_id = ? WHERE LOWER(name) LIKE '%minecraft%' AND category_id IS NULL AND (category IS NULL OR TRIM(category) = '')")
|
||||
->execute([(int) $gameCategoryId]);
|
||||
$pdo->prepare('INSERT INTO schema_migrations (migration) VALUES (?)')->execute(['php_0056_minecraft_status_category']);
|
||||
$result['applied']++;
|
||||
if ($verbose) $result['log'][] = ' [OK] php_0056_minecraft_status_category';
|
||||
} catch (Throwable $e) {
|
||||
$result['errors'][] = 'php_0056_minecraft_status_category: ' . $e->getMessage();
|
||||
if ($verbose) $result['log'][] = ' [FAIL] php_0056_minecraft_status_category - ' . $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
// Migration 0057: Minecraft-Dienst unter „Apps“ einsortieren.
|
||||
if (!isset($done['php_0057_minecraft_status_apps_category'])) {
|
||||
try {
|
||||
$categoryStmt = $pdo->prepare('SELECT id FROM status_categories WHERE LOWER(name) = LOWER(?) LIMIT 1');
|
||||
$categoryStmt->execute(['Apps']);
|
||||
$appCategoryId = $categoryStmt->fetchColumn();
|
||||
if (!$appCategoryId) {
|
||||
$categoryOrder = (int) $pdo->query('SELECT COALESCE(MAX(sort_order), 0) FROM status_categories')->fetchColumn() + 10;
|
||||
$pdo->prepare('INSERT INTO status_categories (name, sort_order) VALUES (?,?)')->execute(['Apps', $categoryOrder]);
|
||||
$appCategoryId = (int) $pdo->lastInsertId();
|
||||
}
|
||||
$pdo->prepare("UPDATE status_components SET category_id = ?, category = NULL WHERE LOWER(name) LIKE '%minecraft%'")
|
||||
->execute([(int) $appCategoryId]);
|
||||
$pdo->prepare('INSERT INTO schema_migrations (migration) VALUES (?)')->execute(['php_0057_minecraft_status_apps_category']);
|
||||
$result['applied']++;
|
||||
if ($verbose) $result['log'][] = ' [OK] php_0057_minecraft_status_apps_category';
|
||||
} catch (Throwable $e) {
|
||||
$result['errors'][] = 'php_0057_minecraft_status_apps_category: ' . $e->getMessage();
|
||||
if ($verbose) $result['log'][] = ' [FAIL] php_0057_minecraft_status_apps_category - ' . $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
// Migration 0059: Optionalen Backup-Server für die Statusseite anlegen.
|
||||
if (!isset($done['php_0059_backup_server_status_component'])) {
|
||||
try {
|
||||
$categoryStmt = $pdo->prepare('SELECT id FROM status_categories WHERE LOWER(name) = LOWER(?) LIMIT 1');
|
||||
$categoryStmt->execute(['Server & Datenbank']);
|
||||
$categoryId = $categoryStmt->fetchColumn();
|
||||
if (!$categoryId) {
|
||||
$categoryOrder = (int) $pdo->query('SELECT COALESCE(MAX(sort_order), 0) FROM status_categories')->fetchColumn() + 10;
|
||||
$pdo->prepare('INSERT INTO status_categories (name, sort_order) VALUES (?,?)')->execute(['Server & Datenbank', $categoryOrder]);
|
||||
$categoryId = (int) $pdo->lastInsertId();
|
||||
}
|
||||
$existing = $pdo->query("SELECT id FROM status_components WHERE LOWER(name) IN ('backup-server', 'backup server', 'ftp-backup-server') LIMIT 1")->fetchColumn();
|
||||
if (!$existing) {
|
||||
$sortOrder = (int) $pdo->query('SELECT COALESCE(MAX(sort_order), 0) FROM status_components')->fetchColumn() + 10;
|
||||
$insert = $pdo->prepare('INSERT INTO status_components (name, description, status, sort_order, created_at, category_id, monitor_type, is_enabled) VALUES (?,?,?,?,?,?,?,0)');
|
||||
$insert->execute(['Backup-Server', 'Optionaler externer Backup-Server; Prüfung mit Host und Port.', 'operational', $sortOrder, db_now(), (int) $categoryId, 'tcp']);
|
||||
}
|
||||
$pdo->prepare('INSERT INTO schema_migrations (migration) VALUES (?)')->execute(['php_0059_backup_server_status_component']);
|
||||
$result['applied']++;
|
||||
if ($verbose) $result['log'][] = ' [OK] php_0059_backup_server_status_component';
|
||||
} catch (Throwable $e) {
|
||||
$result['errors'][] = 'php_0059_backup_server_status_component: ' . $e->getMessage();
|
||||
if ($verbose) $result['log'][] = ' [FAIL] php_0059_backup_server_status_component - ' . $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
// Migration 0060: Geplante Wartungsfenster der Statusseite.
|
||||
if (!isset($done['php_0060_status_maintenance_windows'])) {
|
||||
try {
|
||||
$drv = $pdo->getAttribute(PDO::ATTR_DRIVER_NAME);
|
||||
$ai = $drv === 'sqlite' ? 'INTEGER PRIMARY KEY AUTOINCREMENT' : 'INT AUTO_INCREMENT PRIMARY KEY';
|
||||
$eng = $drv === 'sqlite' ? '' : ' ENGINE=InnoDB DEFAULT CHARSET=utf8mb4';
|
||||
$pdo->exec("CREATE TABLE IF NOT EXISTS status_maintenance_windows (
|
||||
id $ai,
|
||||
title VARCHAR(200) NOT NULL,
|
||||
body TEXT NULL,
|
||||
component_id INT NULL,
|
||||
starts_at DATETIME NOT NULL,
|
||||
ends_at DATETIME NOT NULL,
|
||||
created_by INT NULL,
|
||||
created_at DATETIME NOT NULL,
|
||||
cancelled_at DATETIME NULL
|
||||
)$eng");
|
||||
$pdo->exec('CREATE INDEX idx_status_maintenance_window_time ON status_maintenance_windows (starts_at, ends_at)');
|
||||
$pdo->prepare('INSERT INTO schema_migrations (migration) VALUES (?)')->execute(['php_0060_status_maintenance_windows']);
|
||||
$result['applied']++;
|
||||
if ($verbose) $result['log'][] = ' [OK] php_0060_status_maintenance_windows';
|
||||
} catch (Throwable $e) {
|
||||
$result['errors'][] = 'php_0060_status_maintenance_windows: ' . $e->getMessage();
|
||||
if ($verbose) $result['log'][] = ' [FAIL] php_0060_status_maintenance_windows - ' . $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
// Migration 0061: Prüfintervall je Statusdienst konfigurierbar machen.
|
||||
if (!isset($done['php_0061_status_check_intervals'])) {
|
||||
try {
|
||||
$drv = $pdo->getAttribute(PDO::ATTR_DRIVER_NAME);
|
||||
$cols = $pdo->query($drv === 'sqlite' ? 'PRAGMA table_info(status_components)' : 'SHOW COLUMNS FROM status_components')->fetchAll();
|
||||
$hasInterval = false;
|
||||
foreach ($cols as $col) if (($col['name'] ?? $col['Field'] ?? '') === 'check_interval_minutes') $hasInterval = true;
|
||||
if (!$hasInterval) $pdo->exec('ALTER TABLE status_components ADD COLUMN check_interval_minutes INT NOT NULL DEFAULT 1');
|
||||
$pdo->prepare('INSERT INTO schema_migrations (migration) VALUES (?)')->execute(['php_0061_status_check_intervals']);
|
||||
$result['applied']++;
|
||||
if ($verbose) $result['log'][] = ' [OK] php_0061_status_check_intervals';
|
||||
} catch (Throwable $e) {
|
||||
$result['errors'][] = 'php_0061_status_check_intervals: ' . $e->getMessage();
|
||||
if ($verbose) $result['log'][] = ' [FAIL] php_0061_status_check_intervals - ' . $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
// Migration 0063: SLA-Wartepausen und Ereignisverlauf.
|
||||
if (!isset($done['php_0063_ticket_sla_pause_history'])) {
|
||||
try {
|
||||
$drv = $pdo->getAttribute(PDO::ATTR_DRIVER_NAME);
|
||||
$cols = $pdo->query($drv === 'sqlite' ? 'PRAGMA table_info(tickets)' : 'SHOW COLUMNS FROM tickets')->fetchAll();
|
||||
$hasPauseColumn = false;
|
||||
foreach ($cols as $col) if ((string) ($col['name'] ?? $col['Field'] ?? '') === 'sla_paused_at') $hasPauseColumn = true;
|
||||
if (!$hasPauseColumn) $pdo->exec('ALTER TABLE tickets ADD COLUMN sla_paused_at DATETIME NULL');
|
||||
$ai = $drv === 'sqlite' ? 'INTEGER PRIMARY KEY AUTOINCREMENT' : 'INT AUTO_INCREMENT PRIMARY KEY';
|
||||
$eng = $drv === 'sqlite' ? '' : ' ENGINE=InnoDB DEFAULT CHARSET=utf8mb4';
|
||||
$pdo->exec("CREATE TABLE IF NOT EXISTS ticket_sla_history (
|
||||
id $ai, ticket_id INT NOT NULL, event_type VARCHAR(24) NOT NULL,
|
||||
deadline_type VARCHAR(24) NULL, details TEXT NULL, staff_id INT NULL, created_at DATETIME NOT NULL
|
||||
)$eng");
|
||||
$indexes = $pdo->query($drv === 'sqlite' ? 'PRAGMA index_list(ticket_sla_history)' : 'SHOW INDEX FROM ticket_sla_history')->fetchAll();
|
||||
$hasIndex = false;
|
||||
foreach ($indexes as $index) if ((string) ($index['name'] ?? $index['Key_name'] ?? '') === 'idx_ticket_sla_history_ticket_time') $hasIndex = true;
|
||||
if (!$hasIndex) $pdo->exec('CREATE INDEX idx_ticket_sla_history_ticket_time ON ticket_sla_history (ticket_id, created_at)');
|
||||
foreach ([['first_response_due_at', 'first_response'], ['resolution_due_at', 'resolution']] as [$dueColumn, $type]) {
|
||||
$sql = "INSERT INTO ticket_sla_history (ticket_id, event_type, deadline_type, details, created_at)
|
||||
SELECT t.id, 'started', ?, ?, t.created_at FROM tickets t
|
||||
WHERE t.$dueColumn IS NOT NULL AND NOT EXISTS (
|
||||
SELECT 1 FROM ticket_sla_history h WHERE h.ticket_id = t.id AND h.event_type = 'started' AND h.deadline_type = ?
|
||||
)";
|
||||
$pdo->prepare($sql)->execute([$type, 'SLA gestartet; Fristfeld: ' . $dueColumn, $type]);
|
||||
}
|
||||
$now = db_now();
|
||||
$pdo->prepare("UPDATE tickets SET sla_paused_at = ? WHERE sla_paused_at IS NULL AND status IN ('pending','on_hold')")->execute([$now]);
|
||||
$pdo->prepare("INSERT INTO ticket_sla_history (ticket_id, event_type, details, created_at)
|
||||
SELECT id, 'paused', 'SLA-Pause bei Aktivierung uebernommen', ? FROM tickets
|
||||
WHERE status IN ('pending','on_hold') AND sla_paused_at = ?")->execute([$now, $now]);
|
||||
$pdo->exec("INSERT INTO ticket_sla_history (ticket_id,event_type,deadline_type,details,created_at)
|
||||
SELECT t.id,'overdue','resolution',NULL,t.resolution_due_at FROM tickets t
|
||||
WHERE t.is_overdue = 1 AND t.resolution_due_at IS NOT NULL AND NOT EXISTS (
|
||||
SELECT 1 FROM ticket_sla_history h WHERE h.ticket_id=t.id AND h.event_type='overdue' AND h.deadline_type='resolution'
|
||||
)");
|
||||
$backfillResponse = $pdo->prepare("INSERT INTO ticket_sla_history (ticket_id,event_type,deadline_type,details,created_at)
|
||||
SELECT t.id,'overdue','first_response',NULL,t.first_response_due_at FROM tickets t
|
||||
WHERE t.first_responded_at IS NULL AND t.first_response_due_at IS NOT NULL AND t.first_response_due_at < ?
|
||||
AND t.status NOT IN ('pending','on_hold','resolved','closed') AND NOT EXISTS (
|
||||
SELECT 1 FROM ticket_sla_history h WHERE h.ticket_id=t.id AND h.event_type='overdue' AND h.deadline_type='first_response'
|
||||
)");
|
||||
$backfillResponse->execute([$now]);
|
||||
$pdo->prepare('INSERT INTO schema_migrations (migration) VALUES (?)')->execute(['php_0063_ticket_sla_pause_history']);
|
||||
$result['applied']++;
|
||||
if ($verbose) $result['log'][] = ' [OK] php_0063_ticket_sla_pause_history';
|
||||
} catch (Throwable $e) {
|
||||
$result['errors'][] = 'php_0063_ticket_sla_pause_history: ' . $e->getMessage();
|
||||
if ($verbose) $result['log'][] = ' [FAIL] php_0063_ticket_sla_pause_history - ' . $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
// Migration 0062: Problemmanagement und dokumentierte Ticket-Abschlussgründe.
|
||||
if (!isset($done['php_0062_problems_and_resolution_codes'])) {
|
||||
try {
|
||||
$drv = $pdo->getAttribute(PDO::ATTR_DRIVER_NAME);
|
||||
$ai = $drv === 'sqlite' ? 'INTEGER PRIMARY KEY AUTOINCREMENT' : 'INT AUTO_INCREMENT PRIMARY KEY';
|
||||
$eng = $drv === 'sqlite' ? '' : ' ENGINE=InnoDB DEFAULT CHARSET=utf8mb4';
|
||||
$ticketCols = $pdo->query($drv === 'sqlite' ? 'PRAGMA table_info(tickets)' : 'SHOW COLUMNS FROM tickets')->fetchAll();
|
||||
$has = [];
|
||||
foreach ($ticketCols as $col) $has[] = (string) ($col['name'] ?? $col['Field'] ?? '');
|
||||
if (!in_array('resolution_code', $has, true)) $pdo->exec('ALTER TABLE tickets ADD COLUMN resolution_code VARCHAR(40) NULL');
|
||||
if (!in_array('resolution_note', $has, true)) $pdo->exec('ALTER TABLE tickets ADD COLUMN resolution_note TEXT NULL');
|
||||
$pdo->exec("CREATE TABLE IF NOT EXISTS problems (
|
||||
id $ai,
|
||||
title VARCHAR(200) NOT NULL,
|
||||
description TEXT NULL,
|
||||
root_cause TEXT NULL,
|
||||
workaround TEXT NULL,
|
||||
resolution TEXT NULL,
|
||||
status VARCHAR(24) NOT NULL DEFAULT 'investigating',
|
||||
created_by INT NULL,
|
||||
updated_by INT NULL,
|
||||
created_at DATETIME NOT NULL,
|
||||
updated_at DATETIME NOT NULL,
|
||||
resolved_at DATETIME NULL
|
||||
)$eng");
|
||||
$pdo->exec("CREATE TABLE IF NOT EXISTS problem_tickets (
|
||||
problem_id INT NOT NULL,
|
||||
ticket_id INT NOT NULL,
|
||||
linked_by INT NULL,
|
||||
linked_at DATETIME NOT NULL,
|
||||
PRIMARY KEY (problem_id, ticket_id)
|
||||
)$eng");
|
||||
$indexes = $pdo->query($drv === 'sqlite' ? 'PRAGMA index_list(problem_tickets)' : 'SHOW INDEX FROM problem_tickets')->fetchAll();
|
||||
$ticketIndexExists = false;
|
||||
foreach ($indexes as $index) if ((string) ($index['name'] ?? $index['Key_name'] ?? '') === 'idx_problem_tickets_ticket') $ticketIndexExists = true;
|
||||
if (!$ticketIndexExists) $pdo->exec('CREATE INDEX idx_problem_tickets_ticket ON problem_tickets (ticket_id)');
|
||||
$indexes = $pdo->query($drv === 'sqlite' ? 'PRAGMA index_list(problems)' : 'SHOW INDEX FROM problems')->fetchAll();
|
||||
$problemIndexExists = false;
|
||||
foreach ($indexes as $index) if ((string) ($index['name'] ?? $index['Key_name'] ?? '') === 'idx_problems_status_updated') $problemIndexExists = true;
|
||||
if (!$problemIndexExists) $pdo->exec('CREATE INDEX idx_problems_status_updated ON problems (status, updated_at)');
|
||||
$pdo->prepare('INSERT INTO schema_migrations (migration) VALUES (?)')->execute(['php_0062_problems_and_resolution_codes']);
|
||||
$result['applied']++;
|
||||
if ($verbose) $result['log'][] = ' [OK] php_0062_problems_and_resolution_codes';
|
||||
} catch (Throwable $e) {
|
||||
$result['errors'][] = 'php_0062_problems_and_resolution_codes: ' . $e->getMessage();
|
||||
if ($verbose) $result['log'][] = ' [FAIL] php_0062_problems_and_resolution_codes - ' . $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
// Migration 0049: gesammelte, noch nicht übersetzte Texte (Sammelmodus der Mehrsprachigkeit).
|
||||
if (!isset($done['php_0049_i18n_missing'])) {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user