Everything runs on Cloudflare, under one account, and deploys from main. Nothing is
deployed by hand: a push to main builds the site, and the API too when it touches
the API's inputs.
apps/web, a Worker with static assets (apps/web/wrangler.jsonc: hyoban-cc,
serving ./dist), deployed by Workers Builds:
- root directory
apps/web, installing from the repository root, build commandpnpm build, deploy commandnpx wrangler deploy; - build variables:
MOMENTS_API_TOKEN, which the build needs to read the API,VITE_MAPBOX_TOKEN, andMAPBOX_STATIC_TOKEN, a token without URL restrictions that draws the map pages' placeholders (without it they're left out); Workers Builds setsCI, which turns on the Moments' share cards; - share cards are cached between builds under
apps/web/node_modules/.astro/share-cards, which Cloudflare's build cache keeps, and redrawn only when what they show orapps/web/src/og-image.tschanges.
It builds on every push to main, and again whenever the API takes a write the site
shows: the API calls the build's deploy hook, kept in the API's DEPLOY_HOOK_URL
secret. In CI, without a token, or with one that can't fetch the export, the build fails:
the repository keeps no copy of the records. A local build without a token shows the
tests' made-up records instead.
Each build also reads /media/music/ from the NetEase Cloud Music playlist named
in site.config.json (music), from its public API with no login, and keeps the last copy
in the build cache under apps/web/node_modules/.astro; when NetEase can't be reached
it shows that copy, and with none the build fails.
The custom domain hyoban.cc and the image origin image.hyoban.cc (the R2 bucket)
are on the same zone, which needs Images transformations enabled: the site asks it
for resized photos and video posters (assets.md).
apps/api, at https://hyoban-cc-api.hyoban.workers.dev, also deployed by Workers
Builds:
-
root directory
apps/api, installing from the repository root, deploy commandpnpm run deploy, which applies new D1 migrations (migrations/) before it deploys the code that needs them; the build's API token needs D1 Edit, and a failing migration stops the deploy; -
it builds only for pushes to
maintouchingapps/api/,packages/data-spec/,packages/trip-map/orpnpm-lock.yaml, so keep such changes compatible with the app already installed on the phone; -
secrets, set with
wrangler secret put <name> --config apps/api/wrangler.jsonc:Secret What API_TOKENScomma-separated bearer tokens for writes, drafts, export DEPLOY_HOOK_URLthe site's deploy hook, called after a write it shows TELEGRAM_BOT_TOKENthe bot that posts new Moments to the Telegram channel -
bindings in
apps/api/wrangler.jsonc: D1DB(hyoban-cc, APAC) and R2BUCKET(blog-images), and a cron trigger every 15 minutes; -
vars there too:
TELEGRAM_CHAT_ID, the channel, whose bot must be an admin allowed to post and delete, andDIVISIONS_URL, DataV's GeoAtlas, where a new place's province, city and district are looked up.
The Worker is minified and has observability on, so its logs are kept in the
dashboard; they stream with npx wrangler tail hyoban-cc-api.
| Workflow | When | Does |
|---|---|---|
verify.yml |
push to main, PRs, weekly |
pnpm test, pnpm check, Moment audits, pnpm build; the Android app's unit tests and debug build; HereKit's Swift tests |
r2-backup.yml |
push to main, by hand |
a snapshot of the R2 bucket, in Git LFS on the r2-backup branch |
verify.yml exports the API into data/ to audit every Moment, builds the site from the
API, its share cards kept between runs in the Actions cache as Workers Builds keeps them,
and on its weekly run also checks every referenced R2 object (moments:audit --remote). The
records aren't backed up to GitHub. The workflows run Node 24; the repository pins no Node
version, so Workers Builds uses its own default.
r2-backup.yml checks out the existing r2-backup branch and commits to it, so that
branch has to exist, and the snapshot counts against the repository's Git LFS storage.
Repository secrets: MOMENTS_API_TOKEN (one of the API's API_TOKENS) for the export
and the build in verify.yml, and R2_ACCESS_KEY_ID / R2_SECRET_ACCESS_KEY for the R2 backup.
D1's 30 days of Time Travel and the R2 snapshot on the r2-backup branch are the way
back; the records have no copy in git. A data/ exported earlier can be restored over the
deployed database with vp run data:import --remote (data.md).
A restore with Time Travel puts back meta's database too, and the app's copy, ahead of
the restore, would carry on from rows the database no longer has. Give it a new one from
apps/api, so every copy reads all the rows again:
npx wrangler d1 execute hyoban-cc --remote --command "UPDATE meta SET value = CAST(abs(random() % 9007199254740991) AS TEXT) WHERE key = 'database'"The API comes first: the site's build reads it, and the API needs the site's deploy hook, which exists only once the site's build does.
- Fill in
site.config.json(FORKING.md). In the account itsassetsname, create the R2 bucketblog-imagesand connectimage.hyoban.ccto it. wrangler d1 create hyoban-cc --location apac, and put the new id inapps/api/wrangler.jsonc.- Create the Telegram bot and make it an admin of the channel.
- Set the API's secrets, all but
DEPLOY_HOOK_URL, connectapps/apito Workers Builds as above and push tomain. - Restore the records from an exported
data/withvp run data:import --remote(data.md). - Connect
apps/webto Workers Builds as thehyoban-ccWorker, withMOMENTS_API_TOKENset to one of the API's tokens and the Mapbox variables, add the custom domainhyoban.cc, and enable Images transformations on its zone. - Create the site build's deploy hook and set it as the API's
DEPLOY_HOOK_URL. - In GitHub, set the repository secrets above and create the
r2-backupbranch.
Not deployed anywhere: build the release APK with ./gradlew :app:assembleRelease in
apps/android and install it on the phone with adb install -r
(its README). Its API token is set on its settings page.