v1
This commit is contained in:
commit
654eb49502
30 changed files with 4420 additions and 0 deletions
99
README.md
Normal file
99
README.md
Normal 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
|
||||
```
|
||||
Loading…
Add table
Add a link
Reference in a new issue