feat(permissions)!: granular ACL (bypassOwnership/canShare/canZip/viewOwnOnly), admin panel v1.4.0 UI, and broad hardening across controllers/models/frontend (closes #53)

This commit is contained in:
Ryan
2025-10-15 23:56:39 -04:00
committed by GitHub
parent f2ab2a96bc
commit 25ce6a76be
14 changed files with 2554 additions and 2206 deletions

View File

@@ -55,6 +55,18 @@ window.advancedSearchEnabled = false;
* --- Helper Functions ---
*/
// Safely parse JSON; if server returned HTML/text, throw it as a readable error.
async function safeJson(res) {
const text = await res.text();
try {
return JSON.parse(text);
} catch {
// Common cases: PHP notice/HTML, "Access forbidden.", etc.
const msg = (text || '').toString().trim();
throw new Error(msg || `Unexpected ${res.status} ${res.statusText} from ${res.url || 'request'}`);
}
}
/**
* Convert a file size string (e.g. "456.9KB", "1.2 MB", "1024") into bytes.
*/
@@ -219,8 +231,14 @@ export async function loadFileList(folderParam) {
try {
// Kick off both in parallel, but we'll render as soon as FILES are ready
const filesPromise = fetch(`/api/file/getFileList.php?folder=${encodeURIComponent(folder)}&recursive=1&t=${Date.now()}`);
const foldersPromise = fetch(`/api/folder/getFolderList.php?folder=${encodeURIComponent(folder)}`);
const filesPromise = fetch(
`/api/file/getFileList.php?folder=${encodeURIComponent(folder)}&recursive=1&t=${Date.now()}`,
{ credentials: 'include' }
);
const foldersPromise = fetch(
`/api/folder/getFolderList.php?folder=${encodeURIComponent(folder)}`,
{ credentials: 'include' }
);
// ----- FILES FIRST -----
const filesRes = await filesPromise;
@@ -230,7 +248,10 @@ export async function loadFileList(folderParam) {
throw new Error("Unauthorized");
}
const data = await filesRes.json();
const data = await safeJson(filesRes);
if (data.error) {
throw new Error(typeof data.error === 'string' ? data.error : 'Server returned an error.');
}
// If another loadFileList ran after this one, bail before touching the DOM
if (reqId !== __fileListReqSeq) return [];
@@ -403,7 +424,7 @@ export async function loadFileList(folderParam) {
// ----- FOLDERS NEXT (populate strip when ready; doesn't block rows) -----
try {
const foldersRes = await foldersPromise;
const folderRaw = await foldersRes.json();
const folderRaw = await safeJson(foldersRes).catch(() => []); // don't block file render on folder strip issues
if (reqId !== __fileListReqSeq) return data.files;
// --- build ONLY the *direct* children of current folder ---