From 58b91fb2d33703799fe4585b308e12eb2001d59f Mon Sep 17 00:00:00 2001 From: Purple Date: Sat, 17 Jan 2026 22:27:05 +0000 Subject: [PATCH] added hostname option --- webapp/api.php | 263 +++++++++++++++++++++++++++++++++++++++++++++++ webapp/index.php | 242 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 505 insertions(+) diff --git a/webapp/api.php b/webapp/api.php index d656361..214d7ea 100644 --- a/webapp/api.php +++ b/webapp/api.php @@ -142,6 +142,18 @@ try { handleLogout(); break; + case 'database_backup': + handleDatabaseBackup($db); + break; + + case 'database_import': + handleDatabaseImport($db); + break; + + case 'system_info': + handleSystemInfo($db); + break; + default: jsonResponse(['error' => 'Invalid action'], 400); } @@ -1429,3 +1441,254 @@ function handleLogout() { logoutUser(); jsonResponse(['success' => true, 'redirect' => 'login.php']); } + +/** + * Export full database backup as JSON + */ +function handleDatabaseBackup($db) { + if ($_SERVER['REQUEST_METHOD'] !== 'GET') { + jsonResponse(['error' => 'Method not allowed'], 405); + } + + try { + // Get all geofeed entries + $entries = $db->query("SELECT * FROM geofeed_entries ORDER BY id")->fetchAll(); + + // Get all settings + $settings = $db->query("SELECT * FROM settings ORDER BY setting_key")->fetchAll(); + + // Get audit log (last 1000 entries) + $auditLog = $db->query("SELECT * FROM geofeed_audit_log ORDER BY id DESC LIMIT 1000")->fetchAll(); + + // Get client logos + $logos = $db->query("SELECT * FROM client_logos ORDER BY short_name")->fetchAll(); + + $backup = [ + 'backup_info' => [ + 'created_at' => date('c'), + 'app_version' => APP_VERSION, + 'app_name' => APP_NAME, + 'entry_count' => count($entries), + 'settings_count' => count($settings), + 'audit_log_count' => count($auditLog), + 'logos_count' => count($logos) + ], + 'geofeed_entries' => $entries, + 'settings' => $settings, + 'audit_log' => $auditLog, + 'client_logos' => $logos + ]; + + // Set headers for file download + header('Content-Type: application/json'); + header('Content-Disposition: attachment; filename="geofeed_backup_' . date('Y-m-d_His') . '.json"'); + header('Cache-Control: no-cache, must-revalidate'); + + echo json_encode($backup, JSON_PRETTY_PRINT); + exit; + + } catch (Exception $e) { + jsonResponse(['error' => 'Backup failed: ' . $e->getMessage()], 500); + } +} + +/** + * Import database from JSON backup + */ +function handleDatabaseImport($db) { + if ($_SERVER['REQUEST_METHOD'] !== 'POST') { + jsonResponse(['error' => 'Method not allowed'], 405); + } + + $input = json_decode(file_get_contents('php://input'), true); + + // Validate CSRF + if (!validateCSRFToken($input['csrf_token'] ?? '')) { + jsonResponse(['error' => 'Invalid CSRF token'], 403); + } + + if (empty($input['backup_data'])) { + jsonResponse(['error' => 'No backup data provided'], 400); + } + + $backup = $input['backup_data']; + + // Validate backup structure + if (!isset($backup['backup_info']) || !isset($backup['geofeed_entries'])) { + jsonResponse(['error' => 'Invalid backup file format'], 400); + } + + try { + $db->beginTransaction(); + + $importedEntries = 0; + $importedSettings = 0; + $importedLogos = 0; + + // Clear existing entries if backup contains entries + if (!empty($backup['geofeed_entries'])) { + $db->exec("DELETE FROM geofeed_entries"); + + // Re-insert entries + $stmt = $db->prepare(" + INSERT INTO geofeed_entries + (ip_prefix, country_code, region_code, city, postal_code, client_short_name, notes, sort_order, + ipr_enriched_at, ipr_hostname, ipr_isp, ipr_org, ipr_asn, ipr_asn_name, ipr_connection_type, + ipr_country_name, ipr_region_name, ipr_timezone, ipr_latitude, ipr_longitude, + flag_abuser, flag_attacker, flag_bogon, flag_cloud_provider, flag_proxy, + flag_relay, flag_tor, flag_tor_exit, flag_vpn, flag_anonymous, flag_threat, + created_at, updated_at) + VALUES + (:ip_prefix, :country_code, :region_code, :city, :postal_code, :client_short_name, :notes, :sort_order, + :ipr_enriched_at, :ipr_hostname, :ipr_isp, :ipr_org, :ipr_asn, :ipr_asn_name, :ipr_connection_type, + :ipr_country_name, :ipr_region_name, :ipr_timezone, :ipr_latitude, :ipr_longitude, + :flag_abuser, :flag_attacker, :flag_bogon, :flag_cloud_provider, :flag_proxy, + :flag_relay, :flag_tor, :flag_tor_exit, :flag_vpn, :flag_anonymous, :flag_threat, + :created_at, :updated_at) + "); + + foreach ($backup['geofeed_entries'] as $entry) { + $stmt->execute([ + ':ip_prefix' => $entry['ip_prefix'] ?? null, + ':country_code' => $entry['country_code'] ?? null, + ':region_code' => $entry['region_code'] ?? null, + ':city' => $entry['city'] ?? null, + ':postal_code' => $entry['postal_code'] ?? null, + ':client_short_name' => $entry['client_short_name'] ?? null, + ':notes' => $entry['notes'] ?? null, + ':sort_order' => $entry['sort_order'] ?? 0, + ':ipr_enriched_at' => $entry['ipr_enriched_at'] ?? null, + ':ipr_hostname' => $entry['ipr_hostname'] ?? null, + ':ipr_isp' => $entry['ipr_isp'] ?? null, + ':ipr_org' => $entry['ipr_org'] ?? null, + ':ipr_asn' => $entry['ipr_asn'] ?? null, + ':ipr_asn_name' => $entry['ipr_asn_name'] ?? null, + ':ipr_connection_type' => $entry['ipr_connection_type'] ?? null, + ':ipr_country_name' => $entry['ipr_country_name'] ?? null, + ':ipr_region_name' => $entry['ipr_region_name'] ?? null, + ':ipr_timezone' => $entry['ipr_timezone'] ?? null, + ':ipr_latitude' => $entry['ipr_latitude'] ?? null, + ':ipr_longitude' => $entry['ipr_longitude'] ?? null, + ':flag_abuser' => $entry['flag_abuser'] ?? 0, + ':flag_attacker' => $entry['flag_attacker'] ?? 0, + ':flag_bogon' => $entry['flag_bogon'] ?? 0, + ':flag_cloud_provider' => $entry['flag_cloud_provider'] ?? 0, + ':flag_proxy' => $entry['flag_proxy'] ?? 0, + ':flag_relay' => $entry['flag_relay'] ?? 0, + ':flag_tor' => $entry['flag_tor'] ?? 0, + ':flag_tor_exit' => $entry['flag_tor_exit'] ?? 0, + ':flag_vpn' => $entry['flag_vpn'] ?? 0, + ':flag_anonymous' => $entry['flag_anonymous'] ?? 0, + ':flag_threat' => $entry['flag_threat'] ?? 0, + ':created_at' => $entry['created_at'] ?? date('Y-m-d H:i:s'), + ':updated_at' => $entry['updated_at'] ?? date('Y-m-d H:i:s') + ]); + $importedEntries++; + } + } + + // Import settings + if (!empty($backup['settings'])) { + foreach ($backup['settings'] as $setting) { + saveSetting($db, $setting['setting_key'], $setting['setting_value']); + $importedSettings++; + } + } + + // Import client logos + if (!empty($backup['client_logos'])) { + $db->exec("DELETE FROM client_logos"); + + $stmt = $db->prepare(" + INSERT INTO client_logos (short_name, logo_data, mime_type, created_at, updated_at) + VALUES (:short_name, :logo_data, :mime_type, :created_at, :updated_at) + "); + + foreach ($backup['client_logos'] as $logo) { + $stmt->execute([ + ':short_name' => $logo['short_name'], + ':logo_data' => $logo['logo_data'], + ':mime_type' => $logo['mime_type'] ?? 'image/png', + ':created_at' => $logo['created_at'] ?? date('Y-m-d H:i:s'), + ':updated_at' => $logo['updated_at'] ?? date('Y-m-d H:i:s') + ]); + $importedLogos++; + } + } + + // Log the import action + logAudit($db, null, 'database_import', null, [ + 'entries_imported' => $importedEntries, + 'settings_imported' => $importedSettings, + 'logos_imported' => $importedLogos, + 'backup_date' => $backup['backup_info']['created_at'] ?? 'unknown' + ]); + + $db->commit(); + + jsonResponse([ + 'success' => true, + 'message' => 'Database restored successfully', + 'imported' => [ + 'entries' => $importedEntries, + 'settings' => $importedSettings, + 'logos' => $importedLogos + ] + ]); + + } catch (Exception $e) { + $db->rollBack(); + jsonResponse(['error' => 'Import failed: ' . $e->getMessage()], 500); + } +} + +/** + * Get system information for developer tab + */ +function handleSystemInfo($db) { + if ($_SERVER['REQUEST_METHOD'] !== 'GET') { + jsonResponse(['error' => 'Method not allowed'], 405); + } + + try { + // Get database stats + $entryCount = $db->query("SELECT COUNT(*) FROM geofeed_entries")->fetchColumn(); + $enrichedCount = $db->query("SELECT COUNT(*) FROM geofeed_entries WHERE ipr_enriched_at IS NOT NULL")->fetchColumn(); + $settingsCount = $db->query("SELECT COUNT(*) FROM settings")->fetchColumn(); + $auditCount = $db->query("SELECT COUNT(*) FROM geofeed_audit_log")->fetchColumn(); + $logosCount = $db->query("SELECT COUNT(*) FROM client_logos")->fetchColumn(); + + // Get database size + $dbSize = $db->query(" + SELECT ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) as size_mb + FROM information_schema.tables + WHERE table_schema = '" . DB_NAME . "' + ")->fetchColumn(); + + jsonResponse([ + 'success' => true, + 'data' => [ + 'app_version' => APP_VERSION, + 'php_version' => PHP_VERSION, + 'database' => [ + 'name' => DB_NAME, + 'host' => DB_HOST, + 'size_mb' => $dbSize ?: 0, + 'entries' => (int)$entryCount, + 'enriched_entries' => (int)$enrichedCount, + 'settings' => (int)$settingsCount, + 'audit_log_entries' => (int)$auditCount, + 'client_logos' => (int)$logosCount + ], + 'server' => [ + 'software' => $_SERVER['SERVER_SOFTWARE'] ?? 'Unknown', + 'time' => date('c'), + 'timezone' => date_default_timezone_get() + ] + ] + ]); + + } catch (Exception $e) { + jsonResponse(['error' => 'Failed to get system info: ' . $e->getMessage()], 500); + } +} diff --git a/webapp/index.php b/webapp/index.php index c946d87..fe19a0d 100644 --- a/webapp/index.php +++ b/webapp/index.php @@ -1556,6 +1556,13 @@ if (function_exists('requireAuth')) { Advanced + @@ -1900,6 +1907,89 @@ if (function_exists('requireAuth')) { + + +
+ +
+

+ + + + + + Database Backup +

+

Export a full backup of the database including all entries, settings, and audit logs as a JSON file.

+ +
+ +
+
+ + +
+

+ + + + + + Database Import +

+

Restore the database from a previously exported backup file. This will replace all existing data.

+ +
+
+ + +
+
+ +
+
+ +
+ + + + + + Warning: Importing a backup will permanently replace all existing data. Make sure to download a backup first. +
+
+ + +
+

+ + + + + + System Information +

+

Current system and database information for debugging purposes.

+ +
+
+
+
+