Files
Krao Hasanee 0b4705311b Session 2026-05-30: task detail page overhaul
- 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>
2026-05-30 22:44:57 -04:00

92 lines
4.6 KiB
Markdown

# 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
1. Frontend gets the current Supabase session access token (`supabase.auth.getSession()`).
2. Every request to `/api/filebrowser` sends the token as both `Authorization: Bearer <token>` header and `sb_access_token` query param.
3. The API function validates the token against Supabase and loads the caller's `profiles` row.
4. If valid, the function uses **its own server-side FBQ token** (`FILEBROWSER_TOKEN` env var) to talk to FBQ — the frontend never sees the FBQ token.
5. 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**: `download` action → signed URL → hidden `<a>` click.
- **Multi-file / folder download**: `download-blob` streams each file, then JSZip builds a ZIP client-side.
- **Upload**: `upload-token` gets a short-lived FBQ token, then browser POSTs binary directly to `${fbqUrl}/api/resources?auth=${token}`.
- **Root path** per user is derived by `rootPath()` in `FileSharing.jsx` — team/external get `/`, clients with one company get `/Clients/{name}`, clients with multiple get `/Clients`.
- **Pinned folders** are persisted in `localStorage` keyed by `fbq_pins:{userId}`.
- **Nav tree** lazily loads folder children into a `navCache` ref; does not re-fetch already-loaded paths.
- **Drag-and-drop upload** uses `webkitGetAsEntry` to 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.