The bingo card ran inside the asxpio container, dispatched by Host. Give the public site its own repo, image and deploy so it can be released and mirrored independently of asxp.io. Editing stays in the asxp.io admin, which keeps owning the schema and the photo pipeline; this app only reads the shared bingo tables and the bingo/ photos, through a read-only DB role and MinIO user. Tests load a snapshot of the asxpio-built schema (test/schema.sql). Assisted-by: Claude:opus-5
64 lines
2.9 KiB
Markdown
64 lines
2.9 KiB
Markdown
# datacenterbingo
|
||
|
||
Public site for datacenterbingo.xyz: a 5×5 bingo card of Russia's largest
|
||
datacenters, crossed out on publicly sourced incidents. Ruby/Sinatra,
|
||
read-only. Primary repo `git.czsk.it/asxpi/datacenterbingo` (Forgejo, CI and
|
||
deploy); `github.com/asxpi/datacenterbingo` is a push mirror.
|
||
|
||
## Shared with asxpio
|
||
|
||
Content is edited in the asxp.io admin (`/admin/bingo`, repo `asxpio`:
|
||
`lib/bingo.rb`, `lib/bingo_admin.rb`). Both apps use the same Postgres tables
|
||
(`bingo_datacenters`, `bingo_incidents`) and the photos under `bingo/` in
|
||
asxpio's MinIO bucket.
|
||
|
||
- **asxpio owns the schema.** Migrations live there. After it adds a
|
||
bingo migration, refresh `test/schema.sql` (instructions in its header)
|
||
and update the models here.
|
||
- `lib/bingo.rb` here is the read side of asxpio's: models, `Board`,
|
||
`PROVIDERS`, the `T` UI strings, and photo/IndexNow key reading. The
|
||
admin side (writes, photo upload and resizing, IndexNow pings, ranking,
|
||
seeding, `guess_provider`) stays in asxpio. Keep shared parts in step:
|
||
`PROVIDERS`, `Board`, and model methods.
|
||
- Prod uses a read-only DB role `bingo` (SELECT on the two tables) and a
|
||
MinIO user limited to `s3:GetObject` on `asxpio-invoices/bingo/*`. Both
|
||
are provisioned by the `storage` repo (`init/`), from the same
|
||
`BINGO_*` secrets set on both repos.
|
||
|
||
## Layout
|
||
|
||
- `app.rb`: `BingoWeb`. Routes: `/` (redirect by Accept-Language), `/ru|en|uk`
|
||
(card; `?view=all`, `?p[]=provider`), `/:locale/dc/:slug`, `/sitemap.xml`,
|
||
`/robots.txt`, `/p/:file` (photo proxy, immutable), `/<indexnow-key>.txt`,
|
||
icons, `/healthz`.
|
||
- `config.ru`: `www.` → `BINGO_HOST` 301.
|
||
- `views/`: `layout.erb` (CSS and SEO head), `index.erb` (card, provider
|
||
filter, dialogs), `dc.erb`, partials. `assets/`: icons.
|
||
- Card rules: always 5×5. The bare locale URL is the operational card
|
||
(indexed). `?view=all` and provider-filtered URLs are noindex, with
|
||
canonical pointing to the bare page.
|
||
|
||
## Dev and tests
|
||
|
||
`nix develop`, then `bin/test`. That provisions an ephemeral Postgres and
|
||
loads `test/schema.sql`. Run `cp .env.example .env` for a local server
|
||
(`rackup`), pointing it at a DB that has the bingo tables.
|
||
|
||
## Deploy
|
||
|
||
On push to `main`, `.forgejo/workflows/deploy.yaml` tests, builds with kaniko
|
||
to `$FJ_REGISTRY/$FJ_USER/datacenterbingo`, then over SSH writes
|
||
`/opt/datacenterbingo/{docker-compose.yml,.env}` and runs
|
||
`docker compose up -d`. The container is on the `traefik` and `storage`
|
||
networks, and the Traefik router `datacenterbingo` takes `BINGO_HOST` and
|
||
`www.`.
|
||
|
||
Secrets:
|
||
|
||
| Secret | Purpose |
|
||
|---|---|
|
||
| `FJ_REGISTRY`, `FJ_USER`, `FJ_TOKEN` | kaniko clone + push (`write:package`, `read:repository`); Forgejo reserves the `FORGEJO_` prefix |
|
||
| `DEPLOY_IP`, `DEPLOY_USER`, `DEPLOY_SSH_KEY` | SSH to prod |
|
||
| `BINGO_DB_PASSWORD` | read-only Postgres role `bingo` |
|
||
| `BINGO_S3_ACCESS_KEY`, `BINGO_S3_SECRET_KEY` | read-only MinIO user |
|
||
| `INDEXNOW` | same key as asxpio's `INDEXNOW` (served here, pinged from there) |
|