VYBECODEDDDDDDDDD
This commit is contained in:
+247
@@ -0,0 +1,247 @@
|
||||
# Hosting Guide
|
||||
|
||||
Step-by-step guide to run **File Drop** on a VPS behind a reverse proxy with
|
||||
TLS. The app itself does not terminate TLS — nginx or Caddy does.
|
||||
|
||||
Assumes a fresh Ubuntu/Debian VPS with a domain pointing at it. Adjust paths and
|
||||
commands for your distro.
|
||||
|
||||
---
|
||||
|
||||
## 1. Prerequisites
|
||||
|
||||
- A VPS (any distro Bun supports).
|
||||
- A domain (or subdomain) you control, e.g. `files.example.com`.
|
||||
- DNS A record: `files.example.com` → your server's public IP.
|
||||
|
||||
## 2. Install Bun
|
||||
|
||||
```bash
|
||||
curl -fsSL https://bun.sh/install | bash
|
||||
export PATH="$HOME/.bun/bin:$PATH" # add to ~/.bashrc
|
||||
bun --version
|
||||
```
|
||||
|
||||
## 3. Create a dedicated user and directory
|
||||
|
||||
Running as a non-root user is strongly recommended.
|
||||
|
||||
```bash
|
||||
sudo useradd --system --shell /usr/sbin/nologin --create-home filehost
|
||||
sudo mkdir -p /opt/filehost
|
||||
sudo chown filehost:filehost /opt/filehost
|
||||
```
|
||||
|
||||
## 4. Put the code in place
|
||||
|
||||
```bash
|
||||
# from your local machine (the project root):
|
||||
rsync -avz --exclude node_modules --exclude storage --exclude .env \
|
||||
./ filehost@your-server:/opt/filehost/
|
||||
```
|
||||
|
||||
On the server:
|
||||
|
||||
```bash
|
||||
cd /opt/filehost
|
||||
sudo -u filehost bun install # dev deps (typecheck only), no runtime deps
|
||||
sudo -u filehost mkdir -p storage
|
||||
sudo chown -R filehost:filehost /opt/filehost
|
||||
```
|
||||
|
||||
## 5. Configure
|
||||
|
||||
```bash
|
||||
sudo -u filehost cp .env.example /opt/filehost/.env
|
||||
sudo -u filehost nano /opt/filehost/.env
|
||||
```
|
||||
|
||||
Relevant variables:
|
||||
|
||||
```
|
||||
HOST=127.0.0.1 # bind loopback only; the proxy talks to it
|
||||
PORT=3000
|
||||
STORAGE_DIR=/opt/filehost/storage
|
||||
MAX_FILE_SIZE=5GB # must be <= proxy upload limit
|
||||
RATE_LIMIT_ENABLED=1
|
||||
TRUST_PROXY=1 # proxy sets X-Forwarded-For
|
||||
```
|
||||
|
||||
Binding `HOST=127.0.0.1` (instead of `0.0.0.0`) keeps the app private on the
|
||||
loopback interface so only the reverse proxy can reach it.
|
||||
|
||||
## 6. Install the systemd service
|
||||
|
||||
```bash
|
||||
sudo cp /opt/filehost/deploy/filehost.service /etc/systemd/system/filehost.service
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable --now filehost
|
||||
sudo systemctl status filehost # expect "active (running)"
|
||||
journalctl -u filehost -f # follow logs
|
||||
```
|
||||
|
||||
The unit runs as `filehost`, reads `/opt/filehost/.env`, and is hardened with
|
||||
`ProtectSystem=full`, `ProtectHome=true`, `NoNewPrivileges=true`, and read-write
|
||||
access only to the storage directory.
|
||||
|
||||
## 7. Firewall
|
||||
|
||||
Open only the ports the proxy needs:
|
||||
|
||||
```bash
|
||||
sudo ufw allow 80,443/tcp
|
||||
sudo ufw allow OpenSSH
|
||||
sudo ufw enable
|
||||
```
|
||||
|
||||
Do not open port 3000 to the public (it's bound to loopback).
|
||||
|
||||
## 8. Reverse proxy
|
||||
|
||||
### nginx (with TLS via certbot)
|
||||
|
||||
```bash
|
||||
sudo apt install nginx certbot python3-certbot-nginx
|
||||
```
|
||||
|
||||
`/etc/nginx/sites-available/files.example.com`:
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name files.example.com;
|
||||
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/html;
|
||||
}
|
||||
|
||||
location / {
|
||||
client_max_body_size 6g; # >= MAX_FILE_SIZE
|
||||
proxy_request_buffering off; # stream uploads (critical for large files)
|
||||
proxy_http_version 1.1;
|
||||
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_read_timeout 300s;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
sudo ln -s /etc/nginx/sites-available/files.example.com /etc/nginx/sites-enabled/
|
||||
sudo nginx -t && sudo systemctl reload nginx
|
||||
sudo certbot --nginx -d files.example.com # obtains + wires TLS, auto-renews
|
||||
```
|
||||
|
||||
> `proxy_request_buffering off` matters: with it on, nginx buffers the whole
|
||||
> upload to a temp file before forwarding, defeating streaming and doubling disk
|
||||
> usage for large files.
|
||||
|
||||
### Caddy (auto TLS, minimal config)
|
||||
|
||||
```bash
|
||||
sudo apt install caddy
|
||||
```
|
||||
|
||||
`/etc/caddy/Caddyfile`:
|
||||
|
||||
```
|
||||
files.example.com {
|
||||
reverse_proxy 127.0.0.1:3000
|
||||
|
||||
request_body {
|
||||
max_size 6GB
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
sudo systemctl reload caddy
|
||||
```
|
||||
|
||||
Caddy obtains and renews TLS automatically.
|
||||
|
||||
### Cloudflare
|
||||
|
||||
If you proxy through Cloudflare:
|
||||
|
||||
- Set the DNS record to **Proxied**.
|
||||
- Uploads/downloads are subject to Cloudflare's size caps (free plan ~100MB).
|
||||
For larger files, add a **Cache rule** or **Origin rule** that bypasses
|
||||
Cloudflare for `/api/upload` and `/d/*` so traffic hits your origin directly.
|
||||
- Enable `TRUST_PROXY=1`; the app reads `CF-Connecting-IP` as the client IP.
|
||||
|
||||
## 9. Verification
|
||||
|
||||
```bash
|
||||
curl https://files.example.com/api/health
|
||||
./scripts/smoke-test.sh # BASE_URL=https://files.example.com
|
||||
```
|
||||
|
||||
Then in a browser:
|
||||
|
||||
1. Upload a file.
|
||||
2. Copy the private link; open it in a new private window → downloads.
|
||||
3. Open the link with a modified character → 404.
|
||||
4. Restart the service, confirm the link still works:
|
||||
`sudo systemctl restart filehost`.
|
||||
|
||||
## 10. Backups
|
||||
|
||||
Stop-free snapshot of the storage directory (files + metadata):
|
||||
|
||||
```bash
|
||||
tar -czf /var/backups/filehost-$(date +%F).tar.gz -C /opt/filehost/storage .
|
||||
```
|
||||
|
||||
Nightly cron (`crontab -e`):
|
||||
|
||||
```cron
|
||||
0 2 * * * tar -czf /var/backups/filehost-$(date +\%F).tar.gz -C /opt/filehost/storage .
|
||||
```
|
||||
|
||||
Restore:
|
||||
|
||||
```bash
|
||||
sudo systemctl stop filehost
|
||||
sudo rm -rf /opt/filehost/storage
|
||||
sudo tar -xzf /var/backups/filehost-<date>.tar.gz -C /opt/filehost/storage
|
||||
sudo chown -R filehost:filehost /opt/filehost/storage
|
||||
sudo systemctl start filehost
|
||||
```
|
||||
|
||||
The startup sweep repairs any half-written records automatically.
|
||||
|
||||
## 11. Updating
|
||||
|
||||
```bash
|
||||
# pull/push the new code to /opt/filehost, then:
|
||||
sudo -u filehost bun install
|
||||
sudo systemctl restart filehost
|
||||
```
|
||||
|
||||
Files in `storage/` are untouched by updates and keep working.
|
||||
|
||||
## 12. Troubleshooting
|
||||
|
||||
| Symptom | Likely cause / fix |
|
||||
| --- | --- |
|
||||
| `413 Request Entity Too Large` | `MAX_FILE_SIZE` bigger than the proxy's `client_max_body_size` / `request_body.max_size`. Raise the proxy limit. |
|
||||
| `502 Bad Gateway` on large uploads | Proxy buffering on. Set `proxy_request_buffering off` (nginx). |
|
||||
| Slow uploads / high memory | Proxy buffering uploads; disable it so the app streams to disk. |
|
||||
| Download link works locally but not through the proxy | Wrong `Host`/`proxy_pass`; check the proxy config and that `HOST` in `.env` is `127.0.0.1`. |
|
||||
| Rate-limited (`429`) through a proxy | Real IP not seen; enable `TRUST_PROXY=1`. |
|
||||
| Disk fills up | Uploads are permanent by design. Monitor disk; see Backups and the delete API. |
|
||||
| Keys appear in logs | They shouldn't. Only ids/sizes/names are logged; if you see one, you added logging. |
|
||||
| After restart files 404 | Metadata or file missing/renamed; check `storage/metadata` and the startup sweep log line. |
|
||||
|
||||
## Security recap
|
||||
|
||||
- The app binds to loopback; only the proxy is exposed.
|
||||
- Download/deletion require the unguessable key; everything else is `404`.
|
||||
- Rate limiting protects the keyed endpoints; `TRUST_PROXY=1` must only be set
|
||||
when a proxy really sets `X-Forwarded-For`/`CF-Connecting-IP`.
|
||||
- Files never expire. Deletion requires possession of the key
|
||||
(`DELETE /api/files/:id` with `X-Delete-Key`).
|
||||
Reference in New Issue
Block a user