- New TaskDetail replaces /tasks/:id (was v2 at /requests/:id/v2) - Overview tab: R00 request info, Requested By/Date/Sign Count inline row, Notes, Sign Family, files, amendments - Revisions tab: R01+ from submissions, newest first, same layout as overview, per-entry Amend button - Comments tab: single-line input, post on Enter, delete own comments, Supabase task_comments table - Folder tab: placeholder for file sharing - Tab badges showing revision/comment counts - Amend Request modal: drag-drop file zone, popupOverlayStyle/Surface, Save+Cancel buttons - Request Revision modal for clients on approved/invoiced/paid tasks - JSZip download-all with progress popup for submission files - Upload progress popup for Add Task and amend file uploads - FileAttachment drop zone: 8px radius, transparent bg, label style fix (no all-caps override) - PageLoader on task detail load - task_comments migration with RLS policies Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4.6 KiB
File Sharing — How It Works
Overview
File sharing proxies all file operations through a Vercel serverless function (/api/filebrowser) to a self-hosted FileBrowser Quantum instance. The frontend never speaks directly to FBQ. Auth is validated server-side via Supabase on every request.
Infrastructure
| Component | Location |
|---|---|
| FBQ instance | https://fourgebranding.krao.us (internal: 192.168.2.200:8082) |
| API proxy | api/filebrowser.js (Vercel function) |
| Frontend | src/pages/FileSharing.jsx |
Auth Flow
- Frontend gets the current Supabase session access token (
supabase.auth.getSession()). - Every request to
/api/filebrowsersends the token as bothAuthorization: Bearer <token>header andsb_access_tokenquery param. - The API function validates the token against Supabase and loads the caller's
profilesrow. - If valid, the function uses its own server-side FBQ token (
FILEBROWSER_TOKENenv var) to talk to FBQ — the frontend never sees the FBQ token. - The FBQ token is a long-lived API token. If it expires or returns 401, the function falls back to admin username/password login (
FILEBROWSER_ADMIN_USER/FILEBROWSER_ADMIN_PASS) to get a fresh token, cached in-memory for 30 minutes.
No JWT auth from the frontend to FBQ. That feature was removed — portal users authenticate via Supabase only.
Role-Based Access Control
The API function maps each portal role to a different FBQ root path:
| Portal role | FBQ root | Notes |
|---|---|---|
team |
FILEBROWSER_TEAM_ROOT (default /fourgebranding) |
Full access, sees everything under team root |
client |
FILEBROWSER_CLIENT_ROOT/{company-name} (default /fourgebranding/Clients/{name}) |
Scoped to their company folder; multi-company clients get a virtual root listing their companies |
external |
FILEBROWSER_SUBS_ROOT/{their-name} + assigned project folders |
Personal folder + read access to Projects/{project} folders they are members of via project_members table |
Virtual directories (e.g. the multi-company root for clients, the Projects/ node for externals) are synthesised by the API and never hit FBQ.
Path traversal (..) is blocked at normalizePath() — throws before any FBQ call.
API Actions (/api/filebrowser?action=...)
| Action | Method | What it does |
|---|---|---|
list |
GET | List folder contents; returns { entries, path, canGoUp, ... } |
download |
GET | Returns a signed FBQ download URL + token for direct browser download |
download-blob |
GET | Proxies the file bytes through Vercel (used for ZIP building) |
upload-token |
POST | Returns FBQ token + URL for direct upload from browser to FBQ |
mkdir |
POST | Creates a new folder |
delete |
DELETE | Deletes one file/folder |
rename |
POST | Renames a file/folder in place |
move |
POST | Moves or copies items; mode: 'copy' or mode: 'move' |
archive-move |
POST | Merge-moves a folder into a destination (merges contents if dest already exists) |
Frontend (FileSharing.jsx)
All FBQ calls go through the fbqProxy() helper which injects the current Supabase access token on every call.
- Single file download:
downloadaction → signed URL → hidden<a>click. - Multi-file / folder download:
download-blobstreams each file, then JSZip builds a ZIP client-side. - Upload:
upload-tokengets a short-lived FBQ token, then browser POSTs binary directly to${fbqUrl}/api/resources?auth=${token}. - Root path per user is derived by
rootPath()inFileSharing.jsx— team/external get/, clients with one company get/Clients/{name}, clients with multiple get/Clients. - Pinned folders are persisted in
localStoragekeyed byfbq_pins:{userId}. - Nav tree lazily loads folder children into a
navCacheref; does not re-fetch already-loaded paths. - Drag-and-drop upload uses
webkitGetAsEntryto walk dropped folder trees, creates missing directories first, then uploads files. - Context menu (right-click): Open, Download, Pin/Unpin, Copy, Cut, Paste here, Delete. Delete in context menu is restricted to
role === 'team'(accessScope.canManage).
Required Env Vars
FILEBROWSER_URL=https://fourgebranding.krao.us
FILEBROWSER_TOKEN=<long-lived FBQ API token>
FILEBROWSER_ADMIN_USER=admin
FILEBROWSER_ADMIN_PASS=<password>
FILEBROWSER_TEAM_ROOT=/fourgebranding
FILEBROWSER_CLIENT_ROOT=/fourgebranding/Clients
FILEBROWSER_SUBS_ROOT=/fourgebranding/team
FILEBROWSER_CLIENTS_ROOT=/fourgebranding/Clients
FILEBROWSER_TOKEN is the primary auth method. ADMIN_USER/ADMIN_PASS are only used for auto-refresh when the token expires.