diff --git a/CHANGELOG.md b/CHANGELOG.md index 833d363..f9c80a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,6 +48,10 @@ - Adjusted endpoint paths to match controller filenames - Fix FolderController readOnly create folder permission +### Additional changes + +- Extend clean up expired shared entries + --- ## Changes 4/30/2025 v1.2.8 diff --git a/public/api/admin/readMetadata.php b/public/api/admin/readMetadata.php index 5a5baae..77f7902 100644 --- a/public/api/admin/readMetadata.php +++ b/public/api/admin/readMetadata.php @@ -3,42 +3,61 @@ require_once __DIR__ . '/../../../config/config.php'; -// Simple auth‐check: only admins may read these +// Only admins may read these if (empty($_SESSION['isAdmin']) || $_SESSION['isAdmin'] !== true) { http_response_code(403); - echo json_encode(['error'=>'Forbidden']); + echo json_encode(['error' => 'Forbidden']); exit; } -// Expect a ?file=share_links.json or share_folder_links.json +// Must supply ?file=share_links.json or share_folder_links.json if (empty($_GET['file'])) { http_response_code(400); - echo json_encode(['error'=>'Missing `file` parameter']); + echo json_encode(['error' => 'Missing `file` parameter']); exit; } $file = basename($_GET['file']); -$allowed = ['share_links.json','share_folder_links.json']; +$allowed = ['share_links.json', 'share_folder_links.json']; if (!in_array($file, $allowed, true)) { http_response_code(403); - echo json_encode(['error'=>'Invalid file requested']); + echo json_encode(['error' => 'Invalid file requested']); exit; } $path = META_DIR . $file; if (!file_exists($path)) { - http_response_code(404); - echo json_encode((object)[]); // return empty object + // Return empty object so JS sees `{}` not an error + http_response_code(200); + header('Content-Type: application/json'); + echo json_encode((object)[]); exit; } -$data = file_get_contents($path); -$json = json_decode($data, true); -if (json_last_error() !== JSON_ERROR_NONE) { +$jsonData = file_get_contents($path); +$data = json_decode($jsonData, true); +if (json_last_error() !== JSON_ERROR_NONE || !is_array($data)) { http_response_code(500); - echo json_encode(['error'=>'Corrupted JSON']); + echo json_encode(['error' => 'Corrupted JSON']); exit; } +// ——— Clean up expired entries ——— +$now = time(); +$changed = false; +foreach ($data as $token => $entry) { + if (!empty($entry['expires']) && $entry['expires'] < $now) { + unset($data[$token]); + $changed = true; + } +} +if ($changed) { + // overwrite file with cleaned data + file_put_contents($path, json_encode($data, JSON_PRETTY_PRINT)); +} + +// ——— Send cleaned data back ——— +http_response_code(200); header('Content-Type: application/json'); -echo json_encode($json); \ No newline at end of file +echo json_encode($data); +exit; \ No newline at end of file diff --git a/public/js/adminPanel.js b/public/js/adminPanel.js index 1296f7c..cb4a93f 100644 --- a/public/js/adminPanel.js +++ b/public/js/adminPanel.js @@ -184,49 +184,63 @@ function loadShareLinksSection() { const container = document.getElementById("shareLinksContent"); container.textContent = t("loading") + "..."; - // Helper to fetch a metadata file or return {} on any error - const fetchMeta = file => - fetch(`/api/admin/readMetadata.php?file=${file}`, { credentials: "include" }) - .then(r => r.ok ? r.json() : {}) // non-2xx → treat as empty - .catch(() => ({})); + // helper: fetch one metadata file, but never throw — + // on non-2xx (including 404) or network error, resolve to {} + function fetchMeta(fileName) { + return fetch(`/api/admin/readMetadata.php?file=${encodeURIComponent(fileName)}`, { + credentials: "include" + }) + .then(resp => { + if (!resp.ok) { + // 404 or any other non-OK → treat as empty + return {}; + } + return resp.json(); + }) + .catch(() => { + // network failure, parse error, etc → also empty + return {}; + }); + } Promise.all([ - fetchMeta("share_folder_links.json"), - fetchMeta("share_links.json") - ]) + fetchMeta("share_folder_links.json"), + fetchMeta("share_links.json") + ]) .then(([folders, files]) => { - // If nothing at all, show a friendly message - if (Object.keys(folders).length === 0 && Object.keys(files).length === 0) { - container.textContent = t("no_shared_links_available"); + // if *both* are empty, show "no shared links" + const hasAny = Object.keys(folders).length || Object.keys(files).length; + if (!hasAny) { + container.innerHTML = `

${t("no_shared_links_available")}

`; return; } let html = `
${t("folder_shares")}
${t("file_shares")}
`; @@ -243,11 +257,11 @@ function loadShareLinksSection() { : "/api/file/deleteShareLink.php"; fetch(endpoint, { - method: "POST", - credentials: "include", - headers: { "Content-Type": "application/x-www-form-urlencoded" }, - body: new URLSearchParams({ token }) - }) + method: "POST", + credentials: "include", + headers: { "Content-Type": "application/x-www-form-urlencoded" }, + body: new URLSearchParams({ token }) + }) .then(res => { if (!res.ok) throw new Error(`HTTP ${res.status}`); return res.json(); diff --git a/src/controllers/FileController.php b/src/controllers/FileController.php index 0c027b9..fa91ea9 100644 --- a/src/controllers/FileController.php +++ b/src/controllers/FileController.php @@ -1582,6 +1582,31 @@ class FileController echo json_encode($shareFile, JSON_PRETTY_PRINT); } + public function getAllShareLinks(): void + { + header('Content-Type: application/json'); + $shareFile = META_DIR . 'share_links.json'; + $links = file_exists($shareFile) + ? json_decode(file_get_contents($shareFile), true) ?? [] + : []; + $now = time(); + $cleaned = []; + + // remove expired + foreach ($links as $token => $record) { + if (!empty($record['expires']) && $record['expires'] < $now) { + continue; + } + $cleaned[$token] = $record; + } + + if (count($cleaned) !== count($links)) { + file_put_contents($shareFile, json_encode($cleaned, JSON_PRETTY_PRINT)); + } + + echo json_encode($cleaned); + } + /** * POST /api/file/deleteShareLink.php */ diff --git a/src/controllers/FolderController.php b/src/controllers/FolderController.php index ee704f1..78391c6 100644 --- a/src/controllers/FolderController.php +++ b/src/controllers/FolderController.php @@ -1082,11 +1082,30 @@ class FolderController /** * GET /api/folder/getShareFolderLinks.php */ - public function getShareFolderLinks() + public function getAllShareFolderLinks(): void { header('Content-Type: application/json'); - $links = FolderModel::getAllShareFolderLinks(); - echo json_encode($links, JSON_PRETTY_PRINT); + $shareFile = META_DIR . 'share_folder_links.json'; + $links = file_exists($shareFile) + ? json_decode(file_get_contents($shareFile), true) ?? [] + : []; + $now = time(); + $cleaned = []; + + // 1) Remove expired + foreach ($links as $token => $record) { + if (!empty($record['expires']) && $record['expires'] < $now) { + continue; + } + $cleaned[$token] = $record; + } + + // 2) Persist back if anything was pruned + if (count($cleaned) !== count($links)) { + file_put_contents($shareFile, json_encode($cleaned, JSON_PRETTY_PRINT)); + } + + echo json_encode($cleaned); } /**