update webapp
This commit is contained in:
@@ -12,13 +12,15 @@ CREATE TABLE IF NOT EXISTS geofeed_entries (
|
||||
region_code VARCHAR(10) DEFAULT NULL,
|
||||
city VARCHAR(255) DEFAULT NULL,
|
||||
postal_code VARCHAR(50) DEFAULT NULL,
|
||||
client_short_name VARCHAR(100) DEFAULT NULL,
|
||||
notes TEXT DEFAULT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY unique_prefix (ip_prefix),
|
||||
INDEX idx_country (country_code),
|
||||
INDEX idx_region (region_code),
|
||||
INDEX idx_city (city)
|
||||
INDEX idx_city (city),
|
||||
INDEX idx_client (client_short_name)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- Audit log for tracking changes
|
||||
@@ -42,6 +44,17 @@ CREATE TABLE IF NOT EXISTS geofeed_settings (
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- Client logos table for storing logo URLs per shortname
|
||||
CREATE TABLE IF NOT EXISTS client_logos (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
short_name VARCHAR(100) NOT NULL,
|
||||
logo_url VARCHAR(500) NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY unique_short_name (short_name),
|
||||
INDEX idx_short_name (short_name)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- Insert default settings
|
||||
INSERT INTO geofeed_settings (setting_key, setting_value) VALUES
|
||||
('bunny_cdn_storage_zone', ''),
|
||||
|
||||
@@ -116,4 +116,4 @@ volumes:
|
||||
|
||||
networks:
|
||||
geofeed-network:
|
||||
driver: bridge
|
||||
driver: bridge
|
||||
|
||||
192
webapp/api.php
192
webapp/api.php
@@ -65,7 +65,27 @@ try {
|
||||
case 'clear_all':
|
||||
handleClearAll($db);
|
||||
break;
|
||||
|
||||
|
||||
case 'audit_log':
|
||||
handleAuditLog($db);
|
||||
break;
|
||||
|
||||
case 'logos_list':
|
||||
handleLogosList($db);
|
||||
break;
|
||||
|
||||
case 'logo_save':
|
||||
handleLogoSave($db);
|
||||
break;
|
||||
|
||||
case 'logo_delete':
|
||||
handleLogoDelete($db);
|
||||
break;
|
||||
|
||||
case 'shortnames_list':
|
||||
handleShortnamesList($db);
|
||||
break;
|
||||
|
||||
default:
|
||||
jsonResponse(['error' => 'Invalid action'], 400);
|
||||
}
|
||||
@@ -90,12 +110,18 @@ function handleList($db) {
|
||||
$params[':country'] = strtoupper($_GET['country']);
|
||||
}
|
||||
|
||||
if (!empty($_GET['client'])) {
|
||||
$where[] = 'client_short_name = :client';
|
||||
$params[':client'] = $_GET['client'];
|
||||
}
|
||||
|
||||
if (!empty($_GET['search'])) {
|
||||
$where[] = '(ip_prefix LIKE :search OR city LIKE :search2 OR region_code LIKE :search3)';
|
||||
$where[] = '(ip_prefix LIKE :search OR city LIKE :search2 OR region_code LIKE :search3 OR client_short_name LIKE :search4)';
|
||||
$searchTerm = '%' . $_GET['search'] . '%';
|
||||
$params[':search'] = $searchTerm;
|
||||
$params[':search2'] = $searchTerm;
|
||||
$params[':search3'] = $searchTerm;
|
||||
$params[':search4'] = $searchTerm;
|
||||
}
|
||||
|
||||
$whereClause = implode(' AND ', $where);
|
||||
@@ -105,8 +131,13 @@ function handleList($db) {
|
||||
$countStmt->execute($params);
|
||||
$total = $countStmt->fetch()['total'];
|
||||
|
||||
// Get entries
|
||||
$sql = "SELECT * FROM geofeed_entries WHERE $whereClause ORDER BY created_at DESC LIMIT :limit OFFSET :offset";
|
||||
// Get entries - sorted by IP prefix using INET_ATON for proper IP sorting
|
||||
$sql = "SELECT * FROM geofeed_entries WHERE $whereClause
|
||||
ORDER BY
|
||||
CASE WHEN ip_prefix LIKE '%:%' THEN 1 ELSE 0 END,
|
||||
INET_ATON(SUBSTRING_INDEX(ip_prefix, '/', 1)),
|
||||
ip_prefix
|
||||
LIMIT :limit OFFSET :offset";
|
||||
$stmt = $db->prepare($sql);
|
||||
|
||||
foreach ($params as $key => $value) {
|
||||
@@ -195,8 +226,8 @@ function handleCreate($db) {
|
||||
|
||||
// Insert entry
|
||||
$stmt = $db->prepare("
|
||||
INSERT INTO geofeed_entries (ip_prefix, country_code, region_code, city, postal_code, notes)
|
||||
VALUES (:ip_prefix, :country_code, :region_code, :city, :postal_code, :notes)
|
||||
INSERT INTO geofeed_entries (ip_prefix, country_code, region_code, city, postal_code, client_short_name, notes)
|
||||
VALUES (:ip_prefix, :country_code, :region_code, :city, :postal_code, :client_short_name, :notes)
|
||||
");
|
||||
|
||||
$stmt->execute([
|
||||
@@ -205,6 +236,7 @@ function handleCreate($db) {
|
||||
':region_code' => $regionCode ?: null,
|
||||
':city' => trim($input['city'] ?? '') ?: null,
|
||||
':postal_code' => trim($input['postal_code'] ?? '') ?: null,
|
||||
':client_short_name' => trim($input['client_short_name'] ?? '') ?: null,
|
||||
':notes' => trim($input['notes'] ?? '') ?: null
|
||||
]);
|
||||
|
||||
@@ -279,6 +311,7 @@ function handleUpdate($db) {
|
||||
region_code = :region_code,
|
||||
city = :city,
|
||||
postal_code = :postal_code,
|
||||
client_short_name = :client_short_name,
|
||||
notes = :notes
|
||||
WHERE id = :id
|
||||
");
|
||||
@@ -290,6 +323,7 @@ function handleUpdate($db) {
|
||||
':region_code' => $regionCode ?: null,
|
||||
':city' => trim($input['city'] ?? '') ?: null,
|
||||
':postal_code' => trim($input['postal_code'] ?? '') ?: null,
|
||||
':client_short_name' => trim($input['client_short_name'] ?? '') ?: null,
|
||||
':notes' => trim($input['notes'] ?? '') ?: null
|
||||
]);
|
||||
|
||||
@@ -747,7 +781,7 @@ function logAction($db, $entryId, $action, $oldValues, $newValues) {
|
||||
INSERT INTO geofeed_audit_log (entry_id, action, old_values, new_values, changed_by)
|
||||
VALUES (:entry_id, :action, :old_values, :new_values, :changed_by)
|
||||
");
|
||||
|
||||
|
||||
$stmt->execute([
|
||||
':entry_id' => $entryId,
|
||||
':action' => $action,
|
||||
@@ -756,3 +790,147 @@ function logAction($db, $entryId, $action, $oldValues, $newValues) {
|
||||
':changed_by' => $_SERVER['REMOTE_ADDR'] ?? 'system'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get audit log entries with pagination
|
||||
*/
|
||||
function handleAuditLog($db) {
|
||||
$page = max(1, intval($_GET['page'] ?? 1));
|
||||
$limit = min(100, max(10, intval($_GET['limit'] ?? 25)));
|
||||
$offset = ($page - 1) * $limit;
|
||||
|
||||
// Get total count
|
||||
$countStmt = $db->query("SELECT COUNT(*) as total FROM geofeed_audit_log");
|
||||
$total = $countStmt->fetch()['total'];
|
||||
|
||||
// Get audit log entries with entry details
|
||||
$sql = "SELECT
|
||||
a.id,
|
||||
a.entry_id,
|
||||
a.action,
|
||||
a.old_values,
|
||||
a.new_values,
|
||||
a.changed_at,
|
||||
a.changed_by,
|
||||
e.ip_prefix
|
||||
FROM geofeed_audit_log a
|
||||
LEFT JOIN geofeed_entries e ON a.entry_id = e.id
|
||||
ORDER BY a.changed_at DESC
|
||||
LIMIT :limit OFFSET :offset";
|
||||
|
||||
$stmt = $db->prepare($sql);
|
||||
$stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
|
||||
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
|
||||
$stmt->execute();
|
||||
|
||||
$entries = $stmt->fetchAll();
|
||||
|
||||
// Parse JSON fields
|
||||
foreach ($entries as &$entry) {
|
||||
$entry['old_values'] = $entry['old_values'] ? json_decode($entry['old_values'], true) : null;
|
||||
$entry['new_values'] = $entry['new_values'] ? json_decode($entry['new_values'], true) : null;
|
||||
}
|
||||
|
||||
jsonResponse([
|
||||
'success' => true,
|
||||
'data' => $entries,
|
||||
'pagination' => [
|
||||
'page' => $page,
|
||||
'limit' => $limit,
|
||||
'total' => $total,
|
||||
'pages' => ceil($total / $limit)
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* List all client logos
|
||||
*/
|
||||
function handleLogosList($db) {
|
||||
$stmt = $db->query("SELECT * FROM client_logos ORDER BY short_name");
|
||||
$logos = $stmt->fetchAll();
|
||||
|
||||
jsonResponse(['success' => true, 'data' => $logos]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save (create or update) a client logo
|
||||
*/
|
||||
function handleLogoSave($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);
|
||||
}
|
||||
|
||||
$shortName = trim($input['short_name'] ?? '');
|
||||
$logoUrl = trim($input['logo_url'] ?? '');
|
||||
|
||||
if (empty($shortName)) {
|
||||
jsonResponse(['error' => 'Short name is required'], 400);
|
||||
}
|
||||
|
||||
if (empty($logoUrl) || !filter_var($logoUrl, FILTER_VALIDATE_URL)) {
|
||||
jsonResponse(['error' => 'Valid logo URL is required'], 400);
|
||||
}
|
||||
|
||||
$stmt = $db->prepare("
|
||||
INSERT INTO client_logos (short_name, logo_url)
|
||||
VALUES (:short_name, :logo_url)
|
||||
ON DUPLICATE KEY UPDATE logo_url = VALUES(logo_url), updated_at = CURRENT_TIMESTAMP
|
||||
");
|
||||
|
||||
$stmt->execute([
|
||||
':short_name' => $shortName,
|
||||
':logo_url' => $logoUrl
|
||||
]);
|
||||
|
||||
jsonResponse(['success' => true, 'message' => 'Logo saved successfully']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a client logo
|
||||
*/
|
||||
function handleLogoDelete($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);
|
||||
}
|
||||
|
||||
$shortName = trim($input['short_name'] ?? '');
|
||||
|
||||
if (empty($shortName)) {
|
||||
jsonResponse(['error' => 'Short name is required'], 400);
|
||||
}
|
||||
|
||||
$stmt = $db->prepare("DELETE FROM client_logos WHERE short_name = :short_name");
|
||||
$stmt->execute([':short_name' => $shortName]);
|
||||
|
||||
jsonResponse(['success' => true, 'message' => 'Logo deleted successfully']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of unique client short names from entries
|
||||
*/
|
||||
function handleShortnamesList($db) {
|
||||
$stmt = $db->query("
|
||||
SELECT DISTINCT client_short_name
|
||||
FROM geofeed_entries
|
||||
WHERE client_short_name IS NOT NULL AND client_short_name != ''
|
||||
ORDER BY client_short_name
|
||||
");
|
||||
$shortnames = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||
|
||||
jsonResponse(['success' => true, 'data' => $shortnames]);
|
||||
}
|
||||
|
||||
1030
webapp/index.php
1030
webapp/index.php
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user