Self-hosted file transfer app built with Phoenix. Admins upload files and generate share links; anyone with the link can browse, download, and leave comments. No accounts required for recipients.
- Share links — short tokens or custom slugs (e.g.
/t/my-report) - Password protection — optional per-transfer password
- Download limits — expire a transfer after N downloads
- Expiry — transfers auto-expire and files are cleaned up after a grace period
- Comments — recipients can leave comments, which require admin approval before appearing
- Analytics — per-transfer preview and download event log with IP, user agent, referrer, and bot detection; CSV export
- Admin dashboard — recent activity, storage summary, cleanup log, comment moderation
- Trending page — public landing page showing active transfers ordered by activity
mix deps.get
mix ecto.setup
mix assets.build
mix phx.serverVisit localhost:4000 for the public trending page.
The admin area is at localhost:4000/admin (HTTP basic auth).
Default credentials (development only):
ADMIN_USERNAME=admin
ADMIN_PASSWORD=password
Only authenticated admins can upload files. The upload form is at localhost:4000/admin/uploads.
All settings are read from environment variables at runtime.
| Variable | Default | Description |
|---|---|---|
APP_NAME |
easydl |
Display name shown in the header and browser title |
BASE_URL |
http://localhost:4000 |
Base URL used when generating share links |
ADMIN_USERNAME |
admin |
HTTP basic auth username for the admin area |
ADMIN_PASSWORD |
password |
HTTP basic auth password — change before deploying |
UPLOAD_STORAGE_PATH |
var/uploads |
Directory where uploaded files are stored |
MAX_FILE_SIZE |
2147483648 (2 GB) |
Maximum size of a single file in bytes |
MAX_TOTAL_TRANSFER_SIZE |
5368709120 (5 GB) |
Maximum combined size of all files in one transfer |
MAX_FILES_PER_TRANSFER |
20 |
Maximum number of files per transfer |
DEFAULT_EXPIRY_DAYS |
7 |
Default expiry when creating a transfer |
MAX_EXPIRY_DAYS |
30 |
Maximum expiry an admin can select |
COMMENT_AUTO_APPROVE |
false |
Set to true to skip comment moderation |
MAX_COMMENT_LENGTH |
1000 |
Maximum comment body length in characters |
CLEANUP_GRACE_HOURS |
24 |
Hours after expiry before files are deleted from disk |
ANALYTICS_RETENTION_DAYS |
180 |
Days to keep preview and download event records before pruning |
CLEANUP_WORKER |
true |
Set to false to disable the background cleanup GenServer |
ALLOWED_EXTENSIONS |
(all) | Comma-separated whitelist of file extensions (e.g. pdf,zip) |
BLOCKED_EXTENSIONS |
(none) | Comma-separated blacklist of file extensions (e.g. exe,sh) |
TRUSTED_PROXY_RANGES |
127.0.0.1,::1 |
Comma-separated list of proxy IPs whose forwarded headers are trusted |
Uploaded files are stored outside priv/static and served through the application, not as static assets. Each transfer gets its own directory under UPLOAD_STORAGE_PATH:
var/uploads/
{token}/
{random}.pdf
{random}.zip
__easydl_archive.zip ← generated on first "download all" request
ZIP archives are built on demand and cached. Duplicate filenames within a transfer are de-duplicated automatically.
Expired transfers are marked automatically as the app checks expiry on access. A supervised worker also runs hourly to bulk-expire and then delete files past the grace period. You can trigger cleanup manually:
mix easydl.cleanupCleanup results are logged to the cleanup_logs table and visible in the admin dashboard.
mix precommit # compile (warnings as errors), check unused deps, format, test
mix test # run tests onlymix ecto.reset # drop and recreate
mix ecto.migrate