# One-Time File Share A small self-hosted file-sharing app. You upload a file, get a **one-time-use download link**, hand it to a friend — after one successful download (or once it expires) the link is dead. Files persist in your account until you delete them, so you can mint a fresh link to re-share. Built to run on a headless Fedora box behind nginx at the sub-path `/fileShare`, alongside other apps. ## Features - **One-time download links**, reserved atomically so exactly one download succeeds (race-safe). - **Time-based expiry** on top of one-time use (`LINK_TTL_HOURS`). - **Accounts** with email verification (SMTP); unverified users can't upload. - **Per-user dashboard**: see each file's scan status, link status, expiry, copy the URL, mint a new link, or delete. - **Multi-file uploads** are bundled into a single `.zip` behind one link. - **Antivirus scan** on every upload via ClamAV (`clamd`); infected files are rejected and never get a link. - **Lazy background compression** (gzip) to save disk; transparently decompressed on download. - **Disk-space monitor** that refuses uploads when free space is low. - **Admin dashboard** to manage all users, files and links. - Whole site is `noindex` / unlisted. ## Requirements - Node.js ≥ 20 - MySQL (existing server is fine) - ClamAV `clamd` listening on TCP (optional in dev — set `DISABLE_AV=1`) - An SMTP account for verification emails (optional in dev — set `DISABLE_EMAIL_VERIFICATION=1`) ## Setup 1. **Database** — create the schema yourself (no auto-migration): ```bash mysql -u -p < schema.sql ``` 2. **Install deps**: ```bash npm install ``` 3. **Configure**: ```bash cp .env.example .env # set SESSION_SECRET, DB_*, ADMIN_EMAIL, PUBLIC_BASE_URL, SMTP_*, CLAMD_* node -e "console.log(require('crypto').randomBytes(32).toString('hex'))" # for SESSION_SECRET ``` 4. **Run**: ```bash npm start ``` The app listens on `127.0.0.1:$PORT` and mounts at `$BASE_PATH` (e.g. `http://127.0.0.1:8077/fileShare`). ## Local development (DB over SSH tunnel) Tunnel the server's MySQL to your laptop, then point `.env` at the tunnel: ```bash ssh -L 3307:127.0.0.1:3306 user@server ``` ``` DB_HOST=127.0.0.1 DB_PORT=3307 DISABLE_AV=1 # if you don't run clamd locally DISABLE_EMAIL_VERIFICATION=1 # auto-verifies new accounts; logs verify links PUBLIC_BASE_URL=http://127.0.0.1:8077/fileShare TRUST_PROXY=0 ``` ## Production (nginx + systemd) - Reverse proxy: see `deploy/nginx-fileShare.conf`. The app keeps the `/fileShare` prefix, so `proxy_pass` has **no trailing slash**. Raise `client_max_body_size` to match `MAX_UPLOAD_BYTES`. - Service: see `deploy/fileshare.service`. Set `PUBLIC_BASE_URL=https://your.domain/fileShare` and `TRUST_PROXY=1`. - The first account whose email equals `ADMIN_EMAIL` is auto-promoted to admin on startup. ## How it behaves (worth knowing) - **Aborted downloads still burn the link.** The token is reserved the moment a valid download starts, which is the safe reading of "one-time use." If a transfer fails, mint a new link from the dashboard. - **Infected uploads are deleted immediately** and never stored or linked. If `clamd` is unreachable, `AV_FAIL_MODE=block` (default) rejects the upload; `skip` accepts it and marks the scan `skipped`. - **Compression is lazy**: the upload returns instantly and a background worker gzips the file shortly after (skipping already-compressed types in `COMPRESS_SKIP_EXTS`). Downloads work whether or not compression has finished. ## Key env vars | Var | Purpose | |-----|---------| | `BASE_PATH` | Sub-path mount (e.g. `/fileShare`) | | `PUBLIC_BASE_URL` | Absolute URL used to build copyable links | | `LINK_TTL_HOURS` | Lifetime of a freshly minted link | | `MIN_FREE_BYTES` | Uploads refused below this free space | | `MAX_UPLOAD_BYTES` | Per-file cap | | `ADMIN_EMAIL` | Account promoted to admin on startup | | `AV_FAIL_MODE` | `block` or `skip` when clamd is down | | `DISABLE_AV` / `DISABLE_EMAIL_VERIFICATION` | Dev toggles | ## Project layout ``` src/ server.js app wiring, session, mounting, workers config.js env parsing db.js mysql2 pool auth.js sessions, register/login/logout, guards, CSRF check mailer.js SMTP verification emails links.js link minting + per-file link state storage.js on-disk path helpers log.js file + stdout logger / audit trail routes/ pages, upload, download, files, admin, verify services/ disk, antivirus, bundle (zip), compression (gzip) views.js server-rendered HTML public/style.css schema.sql run this against MySQL deploy/ nginx + systemd samples ```