Record phase 4 website, ArcViewer and Caddy results in the plan

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-08 21:02:00 -08:00
co-authored by Claude Opus 5.5
parent abf5d69d6f
commit 2955a03386
+72 -4
View File
@@ -625,6 +625,68 @@ on the profile.
### Phase 4: Website, viewer, Caddy ### Phase 4: Website, viewer, Caddy
**Steps 1–2 done and both checks passed 2026-11-08**, on a dev stand-in for
the vhost: the importer into `.data/plebleader/dev/db/`, the API on 8042
(`PublicBaseUrl=http://127.0.0.1:8890`), and an ad-hoc `nix shell
nixpkgs#caddy` on `http://127.0.0.1:8890` with the routes below
(`.data/plebleader/dev/{run-api.sh,Caddyfile}`). Use `127.0.0.1`, not
`localhost`: the website sends `localhost` to the real upstream API. Commits:
server `7cc964a`, website `f5194ee3`, `9ebe84ea`, `8d540642`, all local
(not pushed yet). Step 3 (nix-pleb) is still open.
- **Phase 3 check passed.** `/u/pleb`: 11,608.35pp, #3,861 global, US #1,643,
672 ranked and 3,565 total scores, unranked difficulties with stars (8.33★
on the Phase 1 test map), the skill triangle with monthly history. The
leaderboard page and `/score/{id}` (accuracy graphs from local score stats)
work too.
- **Server fix:** the profile page calls `GET /v2/player/{id}/hydrate`, which
the live API has and the public source doesn't (404, "Player not found").
`Controllers/MirrorHydrateController.cs` rebuilds it from
`PlayerController.Get` plus the `profile` object that the website's
`process-hydrate.js` reads.
- **Server run note:** `dotnet build` doesn't copy `wwwroot/` into `bin/`.
Without it `PreviewController`'s constructor throws on `/images/Star.png`,
and any controller that depends on it (`/user`, `/user/config`) returns 500.
The dev run passes `--webroot <src>/wwwroot`; the Nix package uses `dotnet
publish`, which copies it.
- **Website, as built:** `IS_MIRROR` in `api-queue.js` is true when the API
resolves to `/cors/blapi/`. In that case `BL_REPLAYS_URL` is `/watch/` and
score stats come from `/cdn/scorestats/`. Every hardcoded
`allpoland.github.io/ArcViewer/` link points at `/watch/` (except the clan
page's embed). `public/watch/index.html` takes BL replay-viewer or ArcViewer
parameters (`link`, `replayURL`, `scoreId`, `id`, optional `hash`) and
redirects to `/arcviewer/?replayURL=…&url=/cdn/maps/<hash>.zip&noProxy=true`.
It reads the hash from the replay filename
(`<score>-<player>-<diff>-<mode>-<HASH>.bsor`); for `scoreId` it first asks
`/cors/blapi/score/{id}` for the replay URL. Phase 5 can pass `hash=` for
`.dat` replays. The analyzer icons are off by default (an upstream Patreon
service). Clans, events, the socket page and the star-rating graph are still
visible; they just show nothing.
- **ArcViewer facts:** without `noProxy=true` it routes every non-whitelisted
URL through `cors.bsmg.dev`, which can't reach a LAN host. When both `id`
and `url` are given, `id` wins, so never send both. With no map parameter
it looks up the replay's own hash on BeatSaver. The `deploy` build ships
uncompressed `Build/` files, so no `Content-Encoding` headers are needed.
Its own calls to `api.beatleader.com` (player card, leaderboard info) fail
from this origin, so the player card stays blank; fixing that needs a Unity
build, so it's left as is.
- **Map zips:** BeatSaver's CDN serves `https://r2cdn.beatsaver.com/<hash>.zip`
with `Access-Control-Allow-Origin: *` (it rejects HEAD, use GET). Caddy
redirects to it when the archive has no zip, so the website always links
the local path and the ~630 older maps outside `--maps-since` still play.
- **Nix:** the website fork's `flake.nix` builds `packages.default` (yarn
v1 offline, ~16 s; deps hash `sha256-ThGvMJfU…`). Its `postPatch` turns the
themes' Google Fonts `@import url(...)` into `@import (css) url(...)`,
because less would fetch them at build time. ArcViewer `deploy` at
`c776256497b66f7c91a74162cfcd943b0f45ee2e` is
`sha256-4A+aVnFrKyWQjFnH2N/CwoVIlwrcgV2fYYrtaOiodo0=` for `fetchFromGitHub`.
Still open for step 3: push both forks to Gitea, add the website flake as a
nix-pleb input (nix-pleb currently has an uncommitted, staged `flake.lock`
change that isn't ours), and give Caddy read access to the state dir
(`StateDirectoryMode=0750` and `caddy` in group `plebleader`, phase 7). The
vhost below is the tested version.
1. In the website fork, point `BL_REPLAYS_URL` (and the "watch replay" links) 1. In the website fork, point `BL_REPLAYS_URL` (and the "watch replay" links)
at `/arcviewer/?replayURL=<encoded local replay url>&url=<encoded /cdn/maps/<hash>.zip>`. at `/arcviewer/?replayURL=<encoded local replay url>&url=<encoded /cdn/maps/<hash>.zip>`.
Fall back to `id=<bsr>` only for hashes that have no local zip. Hide or stub Fall back to `id=<bsr>` only for hashes that have no local zip. Hide or stub
@@ -645,20 +707,26 @@ on the profile.
@plebleader_allowed remote_ip 127.0.0.1/32 ::1/128 192.168.1.36/32 192.168.254.21/32 @plebleader_allowed remote_ip 127.0.0.1/32 ::1/128 192.168.1.36/32 192.168.254.21/32
handle @plebleader_allowed { handle @plebleader_allowed {
handle_path /cors/blapi/* { reverse_proxy http://127.0.0.1:8042 } handle_path /cors/blapi/* { reverse_proxy http://127.0.0.1:8042 }
handle_path /cdn/maps/* { root * /var/lib/plebleader/archive/beatsaver/zips; file_server } handle_path /cdn/maps/* {
root * /var/lib/plebleader/archive/beatsaver/zips
@nozip not file
redir @nozip https://r2cdn.beatsaver.com{path} 302
file_server
}
handle_path /cdn/* { root * /var/lib/plebleader/storage; file_server } handle_path /cdn/* { root * /var/lib/plebleader/storage; file_server }
handle_path /arcviewer/* { root * ${arcviewer}; file_server } handle_path /arcviewer/* { root * ${arcviewer}; file_server }
handle_path /cors/cdn-assets-bl/* { reverse_proxy https://cdn.assets.beatleader.com { header_up Host {upstream_hostport} } } handle_path /cors/cdn-assets-bl/* { reverse_proxy https://cdn.assets.beatleader.com { header_up Host {upstream_hostport} } }
handle_path /cors/cdnbeatsaver/* { reverse_proxy https://cdn.beatsaver.com { header_up Host {upstream_hostport} } } handle_path /cors/cdnbeatsaver/* { reverse_proxy https://cdn.beatsaver.com { header_up Host {upstream_hostport} } }
handle { root * ${plebleader-website}; try_files {path} /index.html; file_server } # {path}/index.html serves /watch/; without it the SPA's 404 answers
handle { root * ${plebleader-website}; try_files {path} {path}/index.html /index.html; file_server }
} }
handle { respond "Forbidden" 403 } handle { respond "Forbidden" 403 }
''; '';
}; };
``` ```
ArcViewer's WebGL build may need `Content-Encoding` headers for `.br`/`.gz` Checked: the `deploy` branch ships uncompressed `Build/` files, so no
files in `Build/`. Check which ones the `deploy` branch ships. `Content-Encoding` headers are needed.
**Check:** in Chromium, `https://incineroar.brenise.dev:4442/u/pleb` shows the **Check:** in Chromium, `https://incineroar.brenise.dev:4442/u/pleb` shows the
profile and scores, and a replay opens in ArcViewer. profile and scores, and a replay opens in ArcViewer.