Extend clean up expired shared entries
This commit is contained in:
@@ -48,6 +48,10 @@
|
|||||||
- Adjusted endpoint paths to match controller filenames
|
- Adjusted endpoint paths to match controller filenames
|
||||||
- Fix FolderController readOnly create folder permission
|
- Fix FolderController readOnly create folder permission
|
||||||
|
|
||||||
|
### Additional changes
|
||||||
|
|
||||||
|
- Extend clean up expired shared entries
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Changes 4/30/2025 v1.2.8
|
## Changes 4/30/2025 v1.2.8
|
||||||
|
|||||||
@@ -3,42 +3,61 @@
|
|||||||
|
|
||||||
require_once __DIR__ . '/../../../config/config.php';
|
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) {
|
if (empty($_SESSION['isAdmin']) || $_SESSION['isAdmin'] !== true) {
|
||||||
http_response_code(403);
|
http_response_code(403);
|
||||||
echo json_encode(['error'=>'Forbidden']);
|
echo json_encode(['error' => 'Forbidden']);
|
||||||
exit;
|
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'])) {
|
if (empty($_GET['file'])) {
|
||||||
http_response_code(400);
|
http_response_code(400);
|
||||||
echo json_encode(['error'=>'Missing `file` parameter']);
|
echo json_encode(['error' => 'Missing `file` parameter']);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
$file = basename($_GET['file']);
|
$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)) {
|
if (!in_array($file, $allowed, true)) {
|
||||||
http_response_code(403);
|
http_response_code(403);
|
||||||
echo json_encode(['error'=>'Invalid file requested']);
|
echo json_encode(['error' => 'Invalid file requested']);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
$path = META_DIR . $file;
|
$path = META_DIR . $file;
|
||||||
if (!file_exists($path)) {
|
if (!file_exists($path)) {
|
||||||
http_response_code(404);
|
// Return empty object so JS sees `{}` not an error
|
||||||
echo json_encode((object)[]); // return empty object
|
http_response_code(200);
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
echo json_encode((object)[]);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = file_get_contents($path);
|
$jsonData = file_get_contents($path);
|
||||||
$json = json_decode($data, true);
|
$data = json_decode($jsonData, true);
|
||||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
if (json_last_error() !== JSON_ERROR_NONE || !is_array($data)) {
|
||||||
http_response_code(500);
|
http_response_code(500);
|
||||||
echo json_encode(['error'=>'Corrupted JSON']);
|
echo json_encode(['error' => 'Corrupted JSON']);
|
||||||
exit;
|
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');
|
header('Content-Type: application/json');
|
||||||
echo json_encode($json);
|
echo json_encode($data);
|
||||||
|
exit;
|
||||||
@@ -184,49 +184,63 @@ function loadShareLinksSection() {
|
|||||||
const container = document.getElementById("shareLinksContent");
|
const container = document.getElementById("shareLinksContent");
|
||||||
container.textContent = t("loading") + "...";
|
container.textContent = t("loading") + "...";
|
||||||
|
|
||||||
// Helper to fetch a metadata file or return {} on any error
|
// helper: fetch one metadata file, but never throw —
|
||||||
const fetchMeta = file =>
|
// on non-2xx (including 404) or network error, resolve to {}
|
||||||
fetch(`/api/admin/readMetadata.php?file=${file}`, { credentials: "include" })
|
function fetchMeta(fileName) {
|
||||||
.then(r => r.ok ? r.json() : {}) // non-2xx → treat as empty
|
return fetch(`/api/admin/readMetadata.php?file=${encodeURIComponent(fileName)}`, {
|
||||||
.catch(() => ({}));
|
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([
|
Promise.all([
|
||||||
fetchMeta("share_folder_links.json"),
|
fetchMeta("share_folder_links.json"),
|
||||||
fetchMeta("share_links.json")
|
fetchMeta("share_links.json")
|
||||||
])
|
])
|
||||||
.then(([folders, files]) => {
|
.then(([folders, files]) => {
|
||||||
// If nothing at all, show a friendly message
|
// if *both* are empty, show "no shared links"
|
||||||
if (Object.keys(folders).length === 0 && Object.keys(files).length === 0) {
|
const hasAny = Object.keys(folders).length || Object.keys(files).length;
|
||||||
container.textContent = t("no_shared_links_available");
|
if (!hasAny) {
|
||||||
|
container.innerHTML = `<p>${t("no_shared_links_available")}</p>`;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let html = `<h5>${t("folder_shares")}</h5><ul>`;
|
let html = `<h5>${t("folder_shares")}</h5><ul>`;
|
||||||
Object.entries(folders).forEach(([token, o]) => {
|
Object.entries(folders).forEach(([token, o]) => {
|
||||||
const lock = o.password ? `🔒 ` : "";
|
const lock = o.password ? "🔒 " : "";
|
||||||
html += `
|
html += `
|
||||||
<li>
|
<li>
|
||||||
${lock}<strong>${o.folder}</strong>
|
${lock}<strong>${o.folder}</strong>
|
||||||
<small>(${new Date(o.expires * 1000).toLocaleString()})</small>
|
<small>(${new Date(o.expires * 1000).toLocaleString()})</small>
|
||||||
<button type="button"
|
<button type="button"
|
||||||
data-key="${token}"
|
data-key="${token}"
|
||||||
data-type="folder"
|
data-type="folder"
|
||||||
class="btn btn-sm btn-link delete-share">🗑️</button>
|
class="btn btn-sm btn-link delete-share">🗑️</button>
|
||||||
</li>`;
|
</li>`;
|
||||||
});
|
});
|
||||||
|
|
||||||
html += `</ul><h5 style="margin-top:1em;">${t("file_shares")}</h5><ul>`;
|
html += `</ul><h5 style="margin-top:1em;">${t("file_shares")}</h5><ul>`;
|
||||||
Object.entries(files).forEach(([token, o]) => {
|
Object.entries(files).forEach(([token, o]) => {
|
||||||
const lock = o.password ? `🔒 ` : "";
|
const lock = o.password ? "🔒 " : "";
|
||||||
html += `
|
html += `
|
||||||
<li>
|
<li>
|
||||||
${lock}<strong>${o.folder}/${o.file}</strong>
|
${lock}<strong>${o.folder}/${o.file}</strong>
|
||||||
<small>(${new Date(o.expires * 1000).toLocaleString()})</small>
|
<small>(${new Date(o.expires * 1000).toLocaleString()})</small>
|
||||||
<button type="button"
|
<button type="button"
|
||||||
data-key="${token}"
|
data-key="${token}"
|
||||||
data-type="file"
|
data-type="file"
|
||||||
class="btn btn-sm btn-link delete-share">🗑️</button>
|
class="btn btn-sm btn-link delete-share">🗑️</button>
|
||||||
</li>`;
|
</li>`;
|
||||||
});
|
});
|
||||||
html += `</ul>`;
|
html += `</ul>`;
|
||||||
|
|
||||||
@@ -243,11 +257,11 @@ function loadShareLinksSection() {
|
|||||||
: "/api/file/deleteShareLink.php";
|
: "/api/file/deleteShareLink.php";
|
||||||
|
|
||||||
fetch(endpoint, {
|
fetch(endpoint, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
credentials: "include",
|
credentials: "include",
|
||||||
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
||||||
body: new URLSearchParams({ token })
|
body: new URLSearchParams({ token })
|
||||||
})
|
})
|
||||||
.then(res => {
|
.then(res => {
|
||||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||||
return res.json();
|
return res.json();
|
||||||
|
|||||||
@@ -1582,6 +1582,31 @@ class FileController
|
|||||||
echo json_encode($shareFile, JSON_PRETTY_PRINT);
|
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
|
* POST /api/file/deleteShareLink.php
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1082,11 +1082,30 @@ class FolderController
|
|||||||
/**
|
/**
|
||||||
* GET /api/folder/getShareFolderLinks.php
|
* GET /api/folder/getShareFolderLinks.php
|
||||||
*/
|
*/
|
||||||
public function getShareFolderLinks()
|
public function getAllShareFolderLinks(): void
|
||||||
{
|
{
|
||||||
header('Content-Type: application/json');
|
header('Content-Type: application/json');
|
||||||
$links = FolderModel::getAllShareFolderLinks();
|
$shareFile = META_DIR . 'share_folder_links.json';
|
||||||
echo json_encode($links, JSON_PRETTY_PRINT);
|
$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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user