|
| 1 | +# bitsocial-previewer |
| 2 | + |
| 3 | +Link-preview + redirect server for Bitsocial clients. When someone shares a |
| 4 | +post link on Twitter/Telegram/Discord/etc., this server renders the |
| 5 | +OpenGraph/Twitter card for that post, then redirects real browsers into the app. |
| 6 | + |
| 7 | +It is the successor to the old [`plebbit-previewer`](https://github.com/plebbit/plebbit-previewer) |
| 8 | +(archived). Unlike that one — which forced every link to a single domain — this |
| 9 | +is **multi-tenant**: one running instance serves many clients and many mirror |
| 10 | +domains, picking the right profile from the request's `Host` header. |
| 11 | + |
| 12 | +``` |
| 13 | + ┌──────────── s.5chan.app/biz/thread/<cid> |
| 14 | + share link ───────► │ bitsocial-previewer ──► OG/Twitter card (for scrapers) |
| 15 | + (no #/ hash) └──────────── 302-ish JS redirect ──► 5chan.app/#/biz/thread/<cid> |
| 16 | +``` |
| 17 | + |
| 18 | +## How it works |
| 19 | + |
| 20 | +1. 5chan 0.9.1 copies **path-based** share links (no `#/` hash) so the path |
| 21 | + reaches the server: `https://s.5chan.app/<board>/thread/<cid>`. |
| 22 | +2. The server matches the path to a client profile + route, fetches the post by |
| 23 | + cid via `@pkcprotocol/pkc-js` (`getComment`), and builds the card. |
| 24 | +3. Browsers get redirected into the app's hash route; scrapers read the tags. |
| 25 | + |
| 26 | +`<board>` is usually a short **directory code** (e.g. `biz`). We pass it straight |
| 27 | +through to the app (which resolves it); the cid is authoritative for the preview, |
| 28 | +so the previewer never needs to resolve codes itself. |
| 29 | + |
| 30 | +### Recognised share-link formats (5chan) |
| 31 | + |
| 32 | +| Incoming on `s.5chan.app` | Preview | Redirects to | |
| 33 | +|---|---|---| |
| 34 | +| `/<board>/thread/<cid>` | rich (title/text/image) | `5chan.app/#/<board>/thread/<cid>` | |
| 35 | +| `/<board>/catalog` | generic board card | `5chan.app/#/<board>/catalog` | |
| 36 | +| `/<board>` | generic board card | `5chan.app/#/<board>` | |
| 37 | +| `/p/<address>/c/<cid>` (legacy) | rich | `5chan.app/#/<address>/thread/<cid>` | |
| 38 | + |
| 39 | +Unresolvable cid → still redirects to the app with a generic card (short cache), |
| 40 | +so a share link never dead-ends. |
| 41 | + |
| 42 | +## Layout |
| 43 | + |
| 44 | +| File | Purpose | |
| 45 | +|---|---| |
| 46 | +| [`lib/clients.js`](lib/clients.js) | **The per-client design.** Profiles (hostnames, app URL, routes) + host→profile + route matching. | |
| 47 | +| [`lib/html.js`](lib/html.js) | OG/Twitter tag rendering + redirect (HTML-escaped, XSS-safe). Pure. | |
| 48 | +| [`lib/pkc.js`](lib/pkc.js) | `@pkcprotocol/pkc-js` init + cached `getComment`. | |
| 49 | +| [`lib/media.js`](lib/media.js) | Comment media URL + external-link `og:image` scraping. | |
| 50 | +| [`start.js`](start.js) | Express wiring. | |
| 51 | +| [`config.js`](config.js) | Env-driven config (port, bind host, pkc options). | |
| 52 | + |
| 53 | +## Run locally |
| 54 | + |
| 55 | +```bash |
| 56 | +npm install |
| 57 | +npm start # listens on 127.0.0.1:3924, public-gateway fallback |
| 58 | +# in another shell: |
| 59 | +curl -s 'http://localhost:3924/biz/thread/<cid>' | grep -i og: |
| 60 | +``` |
| 61 | + |
| 62 | +```bash |
| 63 | +npm test # routing + HTML unit tests (no network/install needed for the pure logic) |
| 64 | +``` |
| 65 | + |
| 66 | +## Deploy (Caddy) |
| 67 | + |
| 68 | +Deployed under `/opt/bitsocial-previewer` like the box's other services. Config |
| 69 | +(protocol endpoints, port) is inline in [`docker-compose.yml`](docker-compose.yml), |
| 70 | +which uses `network_mode: host` so the previewer reaches the same warm local node |
| 71 | +the seeder uses (`getComment` resolves instantly from pinned 5chan content). |
| 72 | + |
| 73 | +```bash |
| 74 | +# on the box, in /opt/bitsocial-previewer (source synced or git-cloned): |
| 75 | +docker compose up -d --build # builds + runs on 127.0.0.1:3924 |
| 76 | +docker compose logs -f --tail=50 |
| 77 | +``` |
| 78 | + |
| 79 | +CI also publishes `ghcr.io/bitsocialnet/bitsocial-previewer:latest` |
| 80 | +([workflow](.github/workflows/docker.yml)); once that package is made public you |
| 81 | +can `docker compose pull && docker compose up -d` instead of building on the box. |
| 82 | + |
| 83 | +Point the local Caddy at it with [`deploy/Caddyfile.snippet`](deploy/Caddyfile.snippet), |
| 84 | +then do the Cloudflare DNS change in [`deploy/cloudflare.md`](deploy/cloudflare.md). |
| 85 | + |
| 86 | +## Add a client or a mirror |
| 87 | + |
| 88 | +- **New mirror domain for 5chan:** add it to the `5chan` profile's `hostnames` |
| 89 | + in [`lib/clients.js`](lib/clients.js), add a Caddy block + a DNS A record. |
| 90 | +- **New client (e.g. seedit):** fill in the `seedit` profile template |
| 91 | + (hostnames, `appBaseUrl`, route formats) and enable it. |
0 commit comments