4a3e12c85ac1be0da306abb4cb153081183c85ee
7 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
2d1fadb50c |
feat(offline): implicit net-state model, two-tier version gate, unified lobby
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 20s
CI / ui (pull_request) Successful in 1m12s
CI / conformance (pull_request) Successful in 10s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m54s
Land the offline-model redesign (ANDROID_PLAN.md O1-O7): replace the explicit offline toggle with a single detected net-state machine, unify the lobby, and add a two-tier client-version gate. Contour-safe: both version vars empty => dormant, the wire change is additive, so web/pwa/vk/tg behaviour is unchanged unless the gate is deliberately configured. O1 net-state reducer (test-first). O2 store + wiring (connection/offline shims; +@capacitor/network). O3 remove the offline toggle + migrate the pref. O4 two-tier gate: hard update_required degrades to an offline Update/Play-offline notice (not terminal); soft GATEWAY_RECOMMENDED_CLIENT_VERSION -> X-Update-Recommended nudge. O5 unified lobby (device-local + greyed-from-cache server games; self-set identity; closes G-step-0). O6 create flows (with-friends online/offline segment + offline dict guard). O7 docs. Telegram/VK stay online-only (contour review): the offline model is channel-gated via offlineCapable() (native + plain web; false in the mini-apps). offlineMode.active is hard-gated on it, so the whole model (blue chrome, unified/greyed lobby, transport kill switch, device-local vs_ai/hotseat create) stays inert in Telegram/VK regardless of the detected net state (closing a version-lock path that could have flipped them offline); and New Game's with-friends hides the online/offline segment there, leaving the remote invite alone. ARCHITECTURE already declared "Telegram/VK are exempt" - this makes the code match. Deploy/CI: wire the version gate through the deploy (GATEWAY_MIN_CLIENT_VERSION + GATEWAY_RECOMMENDED_CLIENT_VERSION as plain unprefixed vars via compose + ci.yaml + prod-deploy.yaml + write-prod-env.sh + .env.example) so the gate is live when set (test-contour commit-hash version fails open; empty => dormant). Disable the manual android-build CI workflow for now (rename .disabled). Bump the app bundle budget 127->130 KB for the added always-loaded wiring. Fix the UI Docker stage (gateway/Dockerfile): install --ignore-scripts so the Alpine/musl SPA build no longer tries to native-build sharp (a local android:assets tool, unused in the image); esbuild's binary is an optional dep so Vite still builds. Tests: gateway go green (two-tier gate over a real HTTP handler); docker build of the landing + gateway targets green locally; compose --no-interpolate confirms the gateway env; two new telegram.spec.ts e2e (offline signal keeps the chrome online + quick-match opponent choice visible => server enqueue; with-friends shows no pass-and-play), RED-verified; svelte-check 0/0, vitest 617, e2e 248 (chromium + webkit), build + bundle-size 127.8/130. |
||
|
|
4ba9da6721 |
feat(edge): community IP blocklist (Spamhaus DROP) at the gateway
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 24s
CI / ui (pull_request) Successful in 1m11s
CI / conformance (pull_request) Successful in 10s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m49s
Refuse a client whose IP is in a curated CIDR feed with 403 in the same
abuseGuard, before the fail2ban ban. Prod-only (keys by real client IP), off by
default.
- ratelimit.Blocklist: a sorted-range IPv4 matcher (binary search) with an
allowlist checked first; ParseDROP reads the feed; ApplyRefresh keeps the
last-good feed on a transient fetch failure and drops it fail-open once stale
(better to under-block than block a legitimate client on a frozen feed). A
separate static CIDR set, not the per-IP fail2ban store.
- gateway: a refresher goroutine re-fetches every few hours (bounded fetch + size
cap); config GATEWAY_BLOCKLIST_{ENABLED,URL,ALLOW,REFRESH,MAX_STALENESS}.
IPv6 is not matched (a v6 client is still covered by fail2ban / the honeypot).
- observability: gateway_blocklist_blocked_total + entries/age gauges; a Grafana
alert warns before the feed is dropped; service-overview panels.
- deploy: compose env + write-prod-env.sh + prod-deploy/rollback wiring (opt-in
via PROD_ vars).
- docs (ARCHITECTURE, deploy/README); unit tests (match, allowlist, IPv6 skip,
parser, fault-tolerance) + a 403 abuse-guard integration test.
|
||
|
|
0a0a9e5a8d |
fix(deploy): wire the Robokassa direct rail into the prod deploy + rollback
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 11s
CI / integration (pull_request) Successful in 27s
CI / ui (pull_request) Successful in 1m11s
CI / conformance (pull_request) Successful in 10s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m54s
The Robokassa credentials reached only the test contour (ci.yaml → TEST_ secrets). The prod-deploy / prod-rollback workflows and write-prod-env.sh never rendered BACKEND_ROBOKASSA_*, so on prod the shop login was empty and the direct RUB rail stayed disabled — the PROD_BACKEND_ROBOKASSA_* secrets went nowhere. Export the shop login + Password1/Password2 (secrets) and a BACKEND_ROBOKASSA_TEST flag (a variable, so go-live is a flag flip not a secret rotation) from both prod workflows, and emit the four ROBOKASSA_* vars from write-prod-env.sh (the shared deploy/rollback env renderer) so a rollback keeps the rail up. The compose already maps ROBOKASSA_* → BACKEND_ROBOKASSA_*. Document the new secrets/variable in the deploy README and .env.example. Password3 (Robokassa's JWT-invoice API) is unused. |
||
|
|
a58f2ce0e4 |
fix(deploy): support a non-standard S3 port for the pgBackRest endpoint
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 19s
CI / ui (pull_request) Successful in 1m8s
CI / conformance (pull_request) Successful in 10s
CI / gate (pull_request) Successful in 1s
CI / deploy (pull_request) Successful in 1m43s
The S3 endpoint is a host only; some providers publish a separate, non-443 port. Add an optional PGBACKREST_S3_PORT (default 443) alongside the endpoint host, wired through the prod overlay, write-prod-env.sh and both prod workflows, and clarify in the docs that the endpoint variable takes the host alone. |
||
|
|
e922f5f3b9 |
feat(deploy): continuous WAL archiving to S3 for point-in-time recovery
CI / changes (pull_request) Successful in 4s
CI / unit (pull_request) Successful in 12s
CI / integration (pull_request) Successful in 24s
CI / ui (pull_request) Successful in 1m9s
CI / conformance (pull_request) Successful in 10s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m41s
Add pgBackRest-based PITR for the production database, shipped disarmed (archive_mode and the base-backup timer gated off) so it stays inert until the operator arms it before real payments — an un-armed deploy cannot pile WAL onto the disk. - Postgres now builds from a thin image carrying pgBackRest (archive_command runs in-process); the prod overlay wires an encrypted (AES-256-CBC), path-style S3 repository with 30-day retention and a daily full base-backup systemd timer. - Repository config/secrets flow through the PROD_PGBACKREST_* Gitea set and write-prod-env.sh; prod-rollback re-renders the same env so a rollback cannot disarm archiving. - Grafana alerts watch pg_stat_archiver (failing / stalled), absent-safe on the test contour, which never archives. - Fix the pre-migration pg_dump to snapshot the whole database (was backend-only, silently excluding payments). - Document the PITR runbook, the arming sequence and the restore drill in deploy/README.md; record the measured cost/perf assessment. |
||
|
|
a0021d1994 |
feat(gateway): wire GATEWAY_HONEYTOKEN through the deploy
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 20s
CI / ui (pull_request) Successful in 1m6s
CI / conformance (pull_request) Successful in 10s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m44s
The planted honeytoken bearer trap already existed in the gateway + compose, but no workflow fed GATEWAY_HONEYTOKEN, so it was always empty (inert). Wire the per-contour TEST_/PROD_GATEWAY_HONEYTOKEN secret into the ci deploy, prod-deploy and prod-rollback env (the prod path via the shared deploy/write-prod-env.sh), document it (deploy/README + .env.example), and fix the stale compose comment. Empty secret = trap off (no ":?" guard), so a deploy is safe before the operator sets the value + plants the bait. On prod (IP ban on) presenting it earns a 24h ban + alarm; on test (ban off) it logs + a ban metric. GF_SMTP_ENABLED is enabled separately via the TEST_/PROD_GF_SMTP_ENABLED Gitea variables (=true) — no code change. |
||
|
|
7f85362288 |
chore(deploy): dedupe & regroup Gitea CI variables/secrets
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 16s
CI / ui (pull_request) Successful in 1m4s
CI / conformance (pull_request) Successful in 9s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m44s
Collapse identical TEST_/PROD_ pairs to single unprefixed Gitea entries, derive the public URLs from PUBLIC_BASE_URL at deploy time, and share the prod env.sh renderer between deploy and rollback. - Collapse to one unprefixed variable: DICT_VERSION, SMTP_RELAY_HOST/PORT/TLS, GRAFANA_SMTP_PORT, VITE_VK_APP_LINK, VITE_VK_APP_ID (one Selectel relay + one pair of VK apps serve every contour). Secrets collapsed by the owner: SMTP_RELAY_USER/PASS, GATEWAY_VK_APP_SECRET, GATEWAY_VK_ID_CLIENT_SECRET. - Derive at deploy from PUBLIC_BASE_URL (no longer stored): TELEGRAM_MINIAPP_URL, GRAFANA_ROOT_URL, VITE_VK_ID_REDIRECT_URL. Removes the prod/test asymmetry and fills the missing test VITE_VK_APP_ID (VK web login was half-configured on test). - Extract deploy/write-prod-env.sh + write-prod-bot-env.sh, shared by prod-deploy and prod-rollback so the two cannot drift: a rollback now re-renders the FULL runtime env (email / VK login / Grafana alerts previously went dark after a rollback) and passes TELEGRAM_SUPPORT_CHAT_ID. - Single-source DICT_VERSION (CI env + both deploys), fix the v1.3.0/v1.3.1 drift in .env.example/README, correct the misleading honeytoken/abuse-ban compose comment, and rewrite the deploy/README variable list (+ the previously undocumented GATEWAY_VK_APP_SECRET and TELEGRAM_SUPPORT_CHAT_ID). The Gitea variables are reworked via the API; the stale TEST_/PROD_ entries are deleted after the test contour goes green. |