forked from 4ducky/4ducky
Port 4ducky to Rust (Axum + SQLite)
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>
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user