Skip to content

Latest commit

 

History

History
360 lines (253 loc) · 18 KB

File metadata and controls

360 lines (253 loc) · 18 KB

FlareDrive

A lightweight, self-hosted file manager for Cloudflare R2. FlareDrive provides a React and Vite web interface, exposes a WebDAV API through Cloudflare Pages Functions, and stores files in R2. An optional D1 database stores sessions when password login is enabled.

Cloudflare's free tier includes 10 GB of R2 storage and 100,000 Pages Functions invocations per day. View official pricing

简体中文 | English

Highlights

File management

  • Grid and details layouts with sorting and grouping by name, modified time, type, or size
  • Search, breadcrumb navigation, and directory URL hashes synchronized with browser Back and Forward
  • Desktop marquee selection, Ctrl/Cmd multi-select, Shift range selection, and context menus
  • Move, copy, rename, delete, batch download, and copy-link actions
  • Recursive folder copy, move, and deletion, including drag-to-folder moves

Uploads and thumbnails

  • Upload one or many files, full folders, or drag-and-drop file and folder trees
  • Camera capture and image or video selection on supported mobile devices
  • Automatic R2 Multipart Upload for large files, with progress, cancellation, and failure states
  • Content-hashed upload thumbnails for images, videos, and PDFs, stored in a hidden internal namespace

Preview and editing

  • Zoomable image viewing plus native browser audio, video, and PDF viewing
  • Online text, Markdown, and HTML editing with save and save-as support
  • Preview, split, and edit modes for Markdown; sandboxed iframe previews for HTML
  • ZIP entry listing and individual entry downloads
  • Local DOCX, PPTX, XLSX, XLS, and CSV rendering inside an isolated sandboxed iframe
  • Format-level lazy loading so opening one Office type does not load the other Office renderers

Access control

  • Standard WebDAV Basic Auth administrator credentials
  • Optional single-account password login, D1 sessions, and sign-out-all-devices support
  • Optional Cloudflare Turnstile protection for the password login endpoint only
  • Scoped WebDAV tokens with read-only ro, read-write rw, and upload-only up permissions
  • Optional public read access limited to GET, HEAD, and PROPFIND

Screenshots

File management and selection

FlareDrive file management

Markdown editing and preview

FlareDrive Markdown editor

ZIP file preview

FlareDrive ZIP preview

Architecture

flowchart LR
  Browser["Web app<br/>React + Vite"]
  WebDAV["/webdav/*<br/>Pages Functions"]
  Auth["/api/auth/*<br/>Pages Functions"]
  R2["R2 Bucket<br/>BUCKET"]
  D1["D1 session database<br/>AUTH_DB, optional"]
  Office["Office preview iframe<br/>opaque origin"]

  Browser -->|WebDAV requests| WebDAV
  WebDAV --> R2
  Browser -->|Password login| Auth
  Auth --> D1
  Browser -. "ArrayBuffer over MessageChannel" .-> Office
Loading

The main application downloads an Office file through an authenticated /webdav request, then transfers its ArrayBuffer over a one-time MessageChannel. The iframe uses only sandbox="allow-scripts", without allow-same-origin; its sandbox and CSP jointly block network requests, forms, popups, objects, child frames, and top-level navigation.

Core technologies:

Layer Implementation
Web frontend React 19, MUI 9, Vite 8, TypeScript
API Cloudflare Pages Functions / Workers Runtime
File storage Cloudflare R2
Login sessions Cloudflare D1, optional
Document preview Viewer.js, PDF.js, docx-preview, pptx-preview, SheetJS, Univer

Deploy to Cloudflare Pages

Prerequisites

  • A Cloudflare account
  • An R2 bucket
  • A Git repository connected to Cloudflare Pages, or the Wrangler CLI

Pages build settings

Connect your fork or clone to Cloudflare Pages and use these settings:

Setting Value
Framework preset None
Build command npm run build
Build output directory build
Root directory Repository root

Cloudflare bindings

Configure at least the R2 binding before deployment:

Type Binding name Required Purpose
R2 Bucket BUCKET Yes Stores files and internal thumbnails
D1 Database AUTH_DB Password mode only Stores session hashes and expiration times

Redeploy after changing bindings or environment variables. public/_routes.json sends only /webdav/* and /api/auth/* through Pages Functions; all other paths are served as static assets.

Minimal Basic Auth configuration

The default authentication mode is basic. Set:

FLAREDRIVE_AUTH_MODE="basic"
WEBDAV_USERNAME="admin"
WEBDAV_PASSWORD="replace-with-a-strong-password"

The browser or WebDAV client displays an HTTP Basic Auth prompt when it first accesses WebDAV.

Deploy with Wrangler

npm ci
npm run build
npx wrangler pages deploy build --project-name <your-pages-project>

Authentication and access control

Mode comparison

Mode Web application WebDAV clients Primary configuration
basic Uses the browser HTTP Basic Auth challenge Administrator credentials or scoped tokens WEBDAV_USERNAME, WEBDAV_PASSWORD
password Login dialog and D1 session cookie Still use administrator Basic Auth or scoped tokens AUTH_DB, FLAREDRIVE_LOGIN_ACCOUNT, FLAREDRIVE_LOGIN_PRIVATE_KEY
Public Read Lists and reads files without authentication Reads without authentication WEBDAV_PUBLIC_READ="1"

Password mode provides one configured web login account. WebDAV clients continue to use WEBDAV_USERNAME, WEBDAV_PASSWORD, or scoped tokens.

Configure Password mode

  1. Create a D1 database and bind it as AUTH_DB.
  2. Apply the session schema:
npx wrangler d1 execute <database-name> --remote --file migrations/0001_auth_sessions.sql
  1. Generate a SHA-256 hash for the login password:
node -e "const crypto=require('crypto'); console.log(crypto.createHash('sha256').update(process.argv[1]).digest('hex'))" "replace-with-a-strong-password"
  1. Generate an ECDH P-256 private key:
node -e "const { webcrypto } = require('crypto'); (async () => { const pair = await webcrypto.subtle.generateKey({ name: 'ECDH', namedCurve: 'P-256' }, true, ['deriveKey']); console.log(JSON.stringify(await webcrypto.subtle.exportKey('jwk', pair.privateKey))); })()"
  1. Configure these variables:
FLAREDRIVE_AUTH_MODE="password"
FLAREDRIVE_LOGIN_ACCOUNT='{"username":"admin","password":"<sha256-hex>"}'
FLAREDRIVE_LOGIN_PRIVATE_KEY='<private-jwk-json>'

The web login payload is encrypted with an AES-GCM key derived from an ephemeral client ECDH P-256 key. After successful login, the server sets an opaque HttpOnly, SameSite=Lax session cookie; HTTPS requests also receive the Secure attribute. D1 stores only the session token's SHA-256 hash, not the raw token.

Enable Turnstile

Set both values to require Turnstile on the Password login endpoint:

FLAREDRIVE_TURNSTILE_SITE_KEY="<turnstile-site-key>"
FLAREDRIVE_TURNSTILE_SECRET_KEY="<turnstile-secret-key>"

Turnstile does not protect WebDAV Basic Auth and does not change scoped-token behavior.

Scoped WebDAV tokens

Generate a SHA-256 hash for each raw token secret, then place the hash in WEBDAV_ACCESS_TOKENS:

WEBDAV_ACCESS_TOKENS='[{"username":"phone","password":"<sha256-hex>","access":"rw","includes":["photos/phone/"],"excludes":["photos/phone/private/"]},{"username":"dropbox","password":"<sha256-hex>","access":"up","includes":["uploads/"],"excludes":[]}]'
access Allowed operations
ro GET, HEAD, and PROPFIND
rw Every supported WebDAV and multipart-upload operation
up File PUT, multipart create/upload/complete, and multipart abort; cannot read, list, create directories, copy, move, or delete existing files

includes and excludes contain R2 object-key paths, not complete URLs. A scope allows the path itself and its descendants; for example, photos/phone does not match photos/phonebook. Excludes take priority over includes, and COPY or MOVE destinations must also remain inside the allowed scope.

Enter the token username and the raw token secret in a WebDAV client, not the configured SHA-256 hash. A scoped client should connect directly to an allowed prefix, for example:

https://<your-domain>/webdav/photos/phone/

An up token cannot create parent directories, so the target parent must already exist unless the file is uploaded directly to the bucket root.

Bindings and environment variables

Name Type Default Description
BUCKET R2 binding None Required primary file storage
AUTH_DB D1 binding None Session database for Password mode
FLAREDRIVE_AUTH_MODE Variable basic basic or password
WEBDAV_USERNAME Secret/variable None WebDAV administrator username
WEBDAV_PASSWORD Secret None WebDAV administrator password
WEBDAV_ACCESS_TOKENS Secret None JSON array of scoped tokens
WEBDAV_PUBLIC_READ Variable Disabled Set to 1 to expose GET, HEAD, and PROPFIND publicly
FLAREDRIVE_LOGIN_ACCOUNT Secret None Single-account username and password-hash JSON
FLAREDRIVE_LOGIN_PRIVATE_KEY Secret None Private ECDH P-256 JWK used by Password login
FLAREDRIVE_TURNSTILE_SITE_KEY Variable None Turnstile frontend site key
FLAREDRIVE_TURNSTILE_SECRET_KEY Secret None Turnstile server-side secret key
FLAREDRIVE_SESSION_TTL_SECONDS Variable 86400 Standard login lifetime
FLAREDRIVE_REMEMBER_TTL_SECONDS Variable 604800 Keep-signed-in login lifetime

Store production keys in Cloudflare secrets or protected environment variables. Never commit them to Git.

Local development

Install and configure

npm ci

Copy the local configuration template:

# Windows PowerShell
Copy-Item .dev.vars.example .dev.vars
# macOS / Linux
cp .dev.vars.example .dev.vars

Edit .dev.vars. Basic mode does not require D1 initialization. For Password mode, run:

npx wrangler d1 execute AUTH_DB --local --config wrangler.local.jsonc --file migrations/0001_auth_sessions.sql

Start the frontend and backend:

npm run dev
Service Address Purpose
Vite http://127.0.0.1:3601 Frontend, HMR, and /webdav plus /api proxies
Wrangler Pages Dev http://127.0.0.1:3602 Pages Functions, local R2, and local D1

Open http://127.0.0.1:3601. In Basic mode, the frontend does not embed development credentials in request headers; the browser asks for credentials after /webdav/ returns an authentication challenge.

Build and preview

npm run build
npm run preview

npm run preview serves only the built static frontend at http://127.0.0.1:3600. It does not start Pages Functions.

Common commands

Command Purpose
npm run dev Start Vite and local Pages Functions together
npm run dev:frontend Start only Vite on port 3601
npm run dev:functions Start only Wrangler Pages Dev on port 3602
npm run build Type-check and create a production build in build/
npm run preview Preview the static frontend from build/
npm run lint Check src/, functions/, and build configuration

WebDAV clients

Administrator endpoint:

https://<your-domain>/webdav/

Supported methods are OPTIONS, PROPFIND, MKCOL, HEAD, GET, POST, PUT, COPY, MOVE, and DELETE. POST and some query-parameter forms of PUT and DELETE implement the web application's R2 Multipart Upload flow.

The web application uses a normal PUT below 100,000,000 bytes and switches to its custom multipart flow at that size. Standard WebDAV clients generally use only PUT and do not automatically understand FlareDrive's multipart API. Use the web application for large uploads.

Basic Auth sends Base64-encoded credentials with every request. Base64 is not encryption; always use HTTPS in production.

Preview support and limits

Type Behavior In-browser size limit
Images Zoomable Viewer.js surface No dedicated limit
Audio / video Native browser player No dedicated limit
PDF Browser viewer in a new window; PDF.js generates thumbnails No dedicated limit
Text / Markdown / HTML Preview, edit, save, and save as 2 MiB
ZIP Entry listing and individual downloads 30 MiB
DOCX / PPTX / XLSX / XLS / CSV Local parsing and rendering in a sandboxed iframe Less than 10 MiB

Office previews target compatibility and quick inspection. Complex fonts, animations, macros, embedded objects, and advanced formulas may not match desktop Office exactly. FlareDrive does not execute Office macros.

Security design

  • Office and HTML previews run in sandboxed iframes without allow-same-origin; the Office iframe sandbox and CSP block network access, forms, objects, nested frames, popups, and top-level navigation; DOCX additionally disables altChunk HTML and removes links and navigation after rendering; and Markdown rendering skips raw HTML
  • WebDAV forces HTML, SVG, XHTML, and similar active content to download with attachment disposition, nosniff, and a sandbox CSP
  • The internal _$flaredrive$/ namespace is hidden from normal listings and restricted to controlled thumbnail operations
  • Password mode stores only session token hashes in D1
  • Scoped tokens validate source paths, top-level COPY or MOVE destinations, and every recursively derived destination

FlareDrive is currently a single-account file manager and does not include a multi-tenant authorization model, operation audit logs, or malware scanning.

Project structure

index.html                     Main Vite entry
office-preview.html            Isolated Office iframe entry
src/                           React web application
src/office-preview/            DOCX, PPTX, and spreadsheet iframe runtime
functions/webdav/              WebDAV Pages Functions and method handlers
functions/api/auth/            Password-session API
functions/auth.ts              Login, Turnstile, cookie, and D1 session helpers
migrations/                    D1 schema
public/_headers                Office iframe CSP and static-asset CORS
public/_routes.json            Pages Functions route scope
vite.config.ts                 Vite, proxy, worker, and preview chunk configuration

Known boundaries

  • Password mode supports one configured account; it is not a multi-user drive
  • Public Read lets any visitor list and read exposed WebDAV content without authentication
  • Recursive copy, move, and delete operations on large folders can generate many R2 operations

Acknowledgments

License

MIT