This commit is contained in:
portakal 2026-06-16 14:16:02 +03:00
commit 654eb49502
30 changed files with 4420 additions and 0 deletions

99
README.md Normal file
View file

@ -0,0 +1,99 @@
# 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 <user> -p <dbname> < 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
```