forked from 4ducky/4ducky
Same design, layout, URLs and features as the PHP version: browse grid, image view with AJAX comments, BBCode, likes, uploads (file, URL and bookmarklet), quicktagger, profile, block list, inbox, invites, Hall of Ducks, Truhe, abuse reports and the admin tools. - Parameterized SQL, escaped templates (askama), argon2 passwords, server-side sessions, CSRF tokens on every write, strict script CSP - SSRF-safe remote image fetching, magic-byte upload checks - Self-hosted captcha replaces reCAPTCHA v1; analytics/social widgets removed - Vanilla JS replaces jQuery and its plugins with the same DOM behaviour - Reverse proxy support (frontend_path, trust_proxy, secure_cookies) - Admin CLI: admin, settings, invites See README.md for running it and the full list of changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
92 lines
4.6 KiB
Markdown
92 lines
4.6 KiB
Markdown
# 4ducky.com
|
||
|
||
Rust port of the original 4ducky.com image board (a Picturelicious fork, PHP 5 / MySQL, ~2012–2016).
|
||
Design, layout, URLs and features are kept 1:1. The original PHP source is preserved untouched in
|
||
[`legacy/`](legacy/).
|
||
|
||
**Stack:** Rust · Axum · SQLite (sqlx) · Askama templates · argon2 · vanilla JS
|
||
|
||
## Running
|
||
|
||
```sh
|
||
cp config.example.toml config.toml # optional, defaults work for local use
|
||
cargo run --release # http://127.0.0.1:8080
|
||
```
|
||
|
||
Run it from the repository root: the server serves `media/`, `images/` and `favicon.ico` from there.
|
||
The SQLite database and all uploads live in `data_dir` (default `data/`, git-ignored). Migrations run
|
||
automatically on start.
|
||
|
||
### Behind a reverse proxy
|
||
|
||
Set these in `config.toml`, otherwise form posts are rejected as cross-site and every visitor
|
||
looks like `127.0.0.1` (which closes registration via the one-account-per-IP lock):
|
||
|
||
```toml
|
||
frontend_path = "https://your-domain.tld/" # the public URL, exactly as in the address bar
|
||
trust_proxy = true # take the client IP from X-Forwarded-For
|
||
secure_cookies = true # when the proxy terminates HTTPS
|
||
```
|
||
|
||
The proxy must append the client address to `X-Forwarded-For` (nginx:
|
||
`proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;`, Caddy does it by default).
|
||
|
||
### Admin CLI
|
||
|
||
```sh
|
||
fourducky admin <name> [--revoke] # grant/revoke admin
|
||
fourducky settings --register on|off --upload on|off --message '<b>Dolan:</b> "..."'
|
||
fourducky invites <name> --count 5 # create invite codes (was runfav.php)
|
||
```
|
||
|
||
Register an account on the site first, then make it admin. User id 1 is the "superadmin" by default
|
||
(HoF / Ban links), see `superadmins` in the config.
|
||
|
||
## Layout
|
||
|
||
| Path | What |
|
||
|---|---|
|
||
| `src/` | server: routing (`main.rs`), sessions/CSRF (`app.rs`, `auth.rs`), pages (`browse.rs`, `view.rs`, `account.rs`, `inbox.rs`, `misc.rs`), uploads (`uploader.rs`, `imaging.rs`, `fetch.rs`), `grid.rs`, `bbcode.rs`, `captcha.rs` |
|
||
| `templates/` | Askama templates, ported markup from `legacy/templates/*.tpl.php` |
|
||
| `media/` | original CSS/images, plus `4ducky.js` (replaces jQuery + picturelicious.js/functions.js/jqpr.js/lightbox.js) and `4ducky.css` (small additions) |
|
||
| `migrations/` | SQLite schema (from `legacy/dumb.sql`) |
|
||
| `legacy/` | the original PHP site, unchanged |
|
||
|
||
## Differences to the PHP version
|
||
|
||
Behaviour and look are the same; these are the deliberate changes:
|
||
|
||
**Security**
|
||
- All SQL is parameterized (the inbox had SQL injection; the DB layer used `preg_replace /e`).
|
||
- Output is escaped by default; BBCode runs on escaped text and only allows http(s) URLs. User
|
||
websites must be http(s) (no more `javascript:` links). Abuse report links are only clickable when http(s).
|
||
- Passwords: argon2id instead of unsalted md5. Sessions are random tokens stored hashed server-side.
|
||
- CSRF tokens on every form, AJAX call and state-changing link (logout, reset unread, manage actions);
|
||
cross-site POSTs are rejected.
|
||
- Strict Content-Security-Policy (`script-src 'self'`): inline `onclick`/`<script>` became `data-*`
|
||
hooks in `media/4ducky.js`.
|
||
- Remote image fetching (URL upload, comment URLs, bookmarklet) blocks private/loopback/link-local
|
||
addresses, limits redirects, size (8 MB) and time.
|
||
- Uploads are checked by magic bytes, decoded with size limits, and get server-generated file names.
|
||
- Admin endpoints check permissions server-side (HoF/Ban only for `superadmins`); TimeBlock duration
|
||
is computed on the server.
|
||
- Login, register, abuse and PMs are rate limited per IP/user.
|
||
|
||
**Replaced or removed**
|
||
- reCAPTCHA v1 → self-hosted image captcha.
|
||
- Google Analytics, AddThis, Facebook like-box, coin widget → removed. `/chat`, `/forum`, `/shop` keep
|
||
their layout and take an optional embed URL from the config.
|
||
- No e-mail: accounts are active right after registering (as they were in practice).
|
||
- The bookmarklet opens a small first-party popup with a "Post" button instead of uploading from a
|
||
cross-site iframe (modern browsers block third-party cookies; the old flow was also a CSRF hole).
|
||
- jQuery slimScroll → native thin scrollbars.
|
||
|
||
**Bugs fixed on the way**
|
||
- White-style users couldn't comment (form was inside the black-style branch).
|
||
- Blocking a user from the profile page wiped your website field.
|
||
- Comments could still be posted to closed threads via AJAX.
|
||
- `.b2 { width: 64x }` CSS typo made guest teaser thumbnails overflow (fixed in `4ducky.css`).
|
||
- Quicktagger referenced a missing `#home` element and never worked.
|
||
|
||
The original content is kept verbatim, including rank badges and texts, for archival purposes.
|