From 1a64f5bba5400ee5eb1f30eb69e8a6f320dbb3b7 Mon Sep 17 00:00:00 2001 From: Mike Rushton Date: Fri, 20 Feb 2026 09:47:30 -0500 Subject: [PATCH 1/3] added admin page utilize json file for printer profiles --- admin/admin.css | 96 ++++++++++++++++++++++++ admin/auth.php | 25 +++++++ admin/delete.php | 16 ++++ admin/edit.php | 152 ++++++++++++++++++++++++++++++++++++++ admin/index.php | 162 +++++++++++++++++++++++++++++++++++++++++ admin/protect.php | 8 ++ admin/test_printer.php | 46 ++++++++++++ get_printer_data.php | 33 +++------ 8 files changed, 516 insertions(+), 22 deletions(-) create mode 100644 admin/admin.css create mode 100644 admin/auth.php create mode 100644 admin/delete.php create mode 100644 admin/edit.php create mode 100644 admin/index.php create mode 100644 admin/protect.php create mode 100644 admin/test_printer.php diff --git a/admin/admin.css b/admin/admin.css new file mode 100644 index 0000000..2750abc --- /dev/null +++ b/admin/admin.css @@ -0,0 +1,96 @@ +body { + font-family: Arial, sans-serif; + background: linear-gradient(135deg, #1f2933, #3a4a5a); + margin: 0; + display: flex; + justify-content: center; + align-items: center; + min-height: 100vh; + color: #333; +} + +.card { + background: white; + padding: 30px; + border-radius: 12px; + width: 900px; + box-shadow: 0 10px 30px rgba(0,0,0,0.3); +} + +h1 { + text-align: center; + margin-top: 0; +} + +table { + width: 100%; + border-collapse: collapse; + margin-top: 15px; +} + +th { + background: #4CAF50; + color: white; + padding: 10px; +} + +td { + padding: 10px; + border-bottom: 1px solid #ddd; +} + +.inactive { + background: #eee; + color: #888; +} + +.button { + background: #4CAF50; + color: white; + padding: 8px 14px; + border-radius: 6px; + text-decoration: none; +} + +.button.red { + background: #d9534f; +} + +.test-result { + margin-left: 10px; + font-weight: bold; +} + +.form-row { + display: flex; + align-items: center; + gap: 10px; + flex-wrap: wrap; +} + +.form-row input[type="text"] { + width: 260px; /* pick what looks good */ +} + +form input[type="text"], +form input[type="url"] { + width: 100%; + margin-bottom: 10px; +} + +.form-row input[type="text"] { + width: auto; + margin-bottom: 0; +} + +.form-row input.printer-name { + width: 180px; +} + +.form-row input[name="url"] { + width: 200px; +} + +.form-row input[name="apiKey"] { + width: 260px; +} diff --git a/admin/auth.php b/admin/auth.php new file mode 100644 index 0000000..f730ed3 --- /dev/null +++ b/admin/auth.php @@ -0,0 +1,25 @@ + +
+

Admin Login

+ + +
+ diff --git a/admin/delete.php b/admin/delete.php new file mode 100644 index 0000000..b34b71f --- /dev/null +++ b/admin/delete.php @@ -0,0 +1,16 @@ + '', + 'url' => '', + 'apiKey' => '', + 'active' => true +]; + +function getPrinterName($url, $apiKey) { + + $base = rtrim($url, '/'); + + $opts = [ + "http" => [ + "method" => "GET", + "header" => "X-Api-Key: $apiKey\r\n", + "timeout" => 5 + ] + ]; + + $context = stream_context_create($opts); + $json = @file_get_contents($base . '/api/settings', false, $context); + + + if ($json === false) { + error_log("API FAILED: " . $base . "/api/settings"); + return ''; + } + + $data = json_decode($json, true); + + return $data['appearance']['name'] ?? ''; +} + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + + // ALWAYS take values from the form first + $url = $_POST['url'] ?? ''; + $apiKey = $_POST['apiKey'] ?? ''; + + // Fetch live name using the POSTed values + $printerName = getPrinterName($url, $apiKey); + + // If editing and API failed → keep existing name + if ($id !== null && empty($printerName) && !empty($printers[$id]['printerName'])) { + $printerName = $printers[$id]['printerName']; + } + + // If adding and API failed → stop + if ($id === null && empty($printerName)) { + die("Could not contact printer API."); + } + + // Build data from POST (THIS IS CORRECT) + $data = [ + 'printerName' => $printerName, + 'url' => $url, + 'apiKey' => $apiKey, + 'active' => isset($_POST['active']) + ]; + + // Save + if ($id !== null) { + $printers[$id] = $data; + } else { + $printers[] = $data; + } + + file_put_contents($file, json_encode($printers, JSON_PRETTY_PRINT)); + + if (file_exists($cacheFile)) { + unlink($cacheFile); + } + + header('Location: index.php'); + exit; +} +?> + + + +
+ +

+ +
+ +
+ + + + + + + + + + + + +
+ +

+ + + +
+ +
+ + + + + diff --git a/admin/index.php b/admin/index.php new file mode 100644 index 0000000..a75a683 --- /dev/null +++ b/admin/index.php @@ -0,0 +1,162 @@ + !empty($p['active']))); + } elseif ($group === 'inactive') { + $keys = array_keys(array_filter($printers, fn($p) => empty($p['active']))); + } else { + // fallback: whole list + $keys = array_keys($printers); + } + + $pos = array_search($i, $keys, true); + + if ($pos !== false) { + + // move up within that group + if ($dir === 'up' && $pos > 0) { + $a = $keys[$pos]; + $b = $keys[$pos - 1]; + [$printers[$b], $printers[$a]] = [$printers[$a], $printers[$b]]; + } + + // move down within that group + if ($dir === 'down' && $pos < count($keys) - 1) { + $a = $keys[$pos]; + $b = $keys[$pos + 1]; + [$printers[$b], $printers[$a]] = [$printers[$a], $printers[$b]]; + } + + file_put_contents($file, json_encode($printers, JSON_PRETTY_PRINT)); + if (file_exists($cacheFile)) { + unlink($cacheFile); + } + } + + header("Location: index.php"); + exit; +} + +$active = []; +$inactive = []; + +foreach ($printers as $index => $printer) { + if ($printer['active']) { + $active[$index] = $printer; + } else { + $inactive[$index] = $printer; + } +} + +?> + + + +
+ +

Printer Admin

+ +➕ Add Printer + +

Active Printers

+ + + + + + + + + $p): ?> + + + + + + +
NameURLActions
+Edit +Delete +
+ + + + +
+ +
+ + + + +
+ + + +
+ +

Inactive Printers

+ + + + + + + + + $p): ?> + + + + + + +
NameURLActions
+Edit +Delete +
+ +
+ + + + diff --git a/admin/protect.php b/admin/protect.php new file mode 100644 index 0000000..d0398d7 --- /dev/null +++ b/admin/protect.php @@ -0,0 +1,8 @@ + false, 'message' => 'Missing URL or API key']); + exit; +} + +/* + * Always normalize to base URL + * so it works whether user pastes: + * http://host + * http://host/ + * http://host/api + */ +$base = preg_replace('#/api/?$#', '', rtrim($url, '/')); +$endpoint = $base . '/api/settings'; // same source your main page uses + +$opts = [ + "http" => [ + "method" => "GET", + "header" => "X-Api-Key: $apiKey\r\n", + "timeout" => 5 + ] +]; + +$context = stream_context_create($opts); +$response = @file_get_contents($endpoint, false, $context); + +// error_log("DEBUG endpoint=" . $endpoint); +// error_log("DEBUG response=" . $response); + +if ($response === false) { + echo json_encode(['success' => false, 'message' => 'Connection failed']); + exit; +} + +$data = json_decode($response, true); + +if (!empty($data['appearance']['name'])) { + echo json_encode(['success' => true, 'name' => $data['appearance']['name']]); +} else { + echo json_encode(['success' => false, 'message' => 'Invalid API response']); +} diff --git a/get_printer_data.php b/get_printer_data.php index 0df36eb..caa66ce 100644 --- a/get_printer_data.php +++ b/get_printer_data.php @@ -1,24 +1,6 @@ 'printer1', - 'url' => 'http://printer1.example.com/api/', - 'apiKey' => 'apikey1', - ), - - array( - 'printerName' => 'printer2', - 'url' => 'http://printer2.example.com/api/', - 'apiKey' => 'apikey2', - ), - array( - 'printerName' => 'printer3', - 'url' => 'http://printer3.example.com/api/', - 'apiKey' => 'apikey3', - ), - -); +$printers = json_decode(file_get_contents(__DIR__ . '/printers.json'), true); $cacheFile = '/tmp/printer_data_cache.json'; // Set the cache file path @@ -40,12 +22,17 @@ function fetchPrinterData() { // Loop through each printer foreach ($printers as $printer) { + if (!($printer['active'] ?? true)) { + continue; + } + $printerName = $printer['printerName']; $printerUrl = $printer['url']; $printerApiKey = $printer['apiKey']; // Make a request to get the current job information - $urlJob = $printerUrl . 'job'; + $apiBase = rtrim($printerUrl, '/') . '/api/'; + $urlJob = $apiBase . 'job'; $chJob = curl_init(); curl_setopt($chJob, CURLOPT_URL, $urlJob); curl_setopt($chJob, CURLOPT_RETURNTRANSFER, true); @@ -54,7 +41,7 @@ function fetchPrinterData() { curl_close($chJob); // Make a request to get the server settings - $urlSettings = $printerUrl . 'settings'; + $urlSettings = $apiBase . 'settings'; $chSettings = curl_init(); curl_setopt($chSettings, CURLOPT_URL, $urlSettings); curl_setopt($chSettings, CURLOPT_RETURNTRANSFER, true); @@ -80,7 +67,7 @@ function fetchPrinterData() { 'progress' => '', 'elapsed' => '', 'left' => '', - 'colorClass' => $colorClass, + 'colorClass' => $colorClass, ); } else { $printerData[] = array( @@ -90,9 +77,11 @@ function fetchPrinterData() { 'elapsed' => (isset($job['progress']['printTime'])) ? formatTime($job['progress']['printTime']) : '', 'left' => (isset($job['progress']['printTimeLeft'])) ? formatTime($job['progress']['printTimeLeft']) : '', 'colorClass' => $colorClass, + 'active' => $printer['active'] ?? true ); } } + usort($printerData, fn($a, $b) => ($b['active'] ?? true) <=> ($a['active'] ?? true)); return $printerData; } From ce671b1573b6f9d3fcc67945c4974d418314c7c4 Mon Sep 17 00:00:00 2001 From: Mike Rushton Date: Sat, 21 Feb 2026 12:56:17 -0500 Subject: [PATCH 2/3] protect test_printers.php move printers.json our of url path --- admin/delete.php | 2 +- admin/edit.php | 2 +- admin/index.php | 2 +- admin/test_printer.php | 1 + get_printer_data.php | 2 +- 5 files changed, 5 insertions(+), 4 deletions(-) diff --git a/admin/delete.php b/admin/delete.php index b34b71f..5acdab4 100644 --- a/admin/delete.php +++ b/admin/delete.php @@ -1,7 +1,7 @@ Date: Sat, 21 Feb 2026 22:20:23 -0500 Subject: [PATCH 3/3] comma separated list of ip's, CIDR's or partial ip --- admin/auth.php | 45 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/admin/auth.php b/admin/auth.php index f730ed3..8fe1aa4 100644 --- a/admin/auth.php +++ b/admin/auth.php @@ -1,12 +1,46 @@ -