Services

2026 06 16 Manager Cloudflare Review


apps/manager Cloudflare Pages review — 2026-06-16

Scope: apps/manager Cloudflare implementation/configuration only. This review used the Cloudflare Pages/Workers/Storage/DNS skills and the repository Cloudflare inventory at docs/cloudflare-review/2026-06-16-skill-inventory.md. No Cloudflare APIs were called, no deploys were run, and no secrets were printed.

Evidence inspected#

  • apps/manager/wrangler.toml:1-4 — Pages project name, output directory, compatibility date.
  • apps/manager/package.json:7-15 — Vite build/preview/typecheck/test scripts.
  • apps/manager/vite.config.ts:25-54VITE_BASE_PATH, build.outDir = "build", and chunking.
  • apps/manager/public/_headers:1-10 and apps/manager/public/_redirects:1 — Pages headers and SPA fallback.
  • .mise.toml:1442-1444, .mise.toml:1532-1534, .github/workflows/cd.yml:75-88 — build/deploy entrypoints.
  • packages/saas-api/src/client.ts:36-47, packages/saas-api/src/client.ts:66-68, packages/saas-api/src/client.ts:114-123 — browser API URL fallbacks.
  • apps/manager/.env.example:1-14, apps/manager/src/env.d.ts:4-10, turbo.json:153-163 — documented and cached build-time environment.

Verification run:

1
$ mise run cf:build:manager
2
✓ 2955 modules transformed.
3
build/index.html 1.07 kB │ gzip: 0.47 kB
4
build/assets/index-BapuG47C.css 26.48 kB │ gzip: 5.76 kB
5
build/assets/vendor-query-537Rw8Y5.js 35.63 kB │ gzip: 10.53 kB
6
build/assets/vendor-router-KdrO8a7-.js 87.38 kB │ gzip: 28.19 kB
7
build/assets/vendor-ui-YeKRssKa.js 109.81 kB │ gzip: 36.45 kB
8
build/assets/vendor-react-C4mGKPvO.js 142.43 kB │ gzip: 45.84 kB
9
build/assets/index-ME5Hu1xp.js 378.37 kB │ gzip: 97.68 kB
10
build/assets/vendor-charts-CtruBP5P.js 392.70 kB │ gzip: 108.01 kB
11
✓ built in 8.73s
12
13
$ mise exec -- pnpm exec node tools/scripts/check-cloudflare-artifact-budgets.mjs --app manager
14
manager: 11 files from apps/manager/build
15
OK javascriptGzip: 319.0 KiB / 1.50 MiB
16
OK assetsRaw: 1.12 MiB / 15.0 MiB
17
OK largestAssetRaw: 383.5 KiB / 4.00 MiB
18
OK workerRaw: 0 B / 1.00 MiB

Findings#

HIGH — production Pages deploys do not pin the production branch#

Evidence:

  • www deploys with --branch main in .mise.toml:1528-1530 and .github/workflows/cd.yml:78-82.
  • manager deploys omit --branch main in .mise.toml:1532-1534 and .github/workflows/cd.yml:84-88.
  • The CD workflow is tag-triggered (.github/workflows/cd.yml:1-5), so relying on Wrangler's inferred branch can create or update a preview deployment instead of the Pages production deployment.

Impact: manager.assistance.bg may not receive a production deployment even when CI reports a successful Pages upload.

Recommended fix: confirm the Pages production branch for assistance-manager, then add the explicit branch to both deploy surfaces, matching www if production is main:

bash
1
cd apps/manager && wrangler pages deploy build --project-name assistance-manager --branch main

HIGH — Cloudflare build-time API URLs are not enforced for production#

Evidence:

  • cf:build:manager only runs pnpm --filter manager build (.mise.toml:1442-1444), and CI builds manager without environment overrides (.github/workflows/cd.yml:75-76).
  • The API client falls back in-browser to the current hostname plus local/dev ports when VITE_AUTH_URL, VITE_API_URL, or VITE_LEARN_API_URL are absent (packages/saas-api/src/client.ts:36-47, packages/saas-api/src/client.ts:66-68, packages/saas-api/src/client.ts:114-123).
  • apps/manager/.env.example:1-14 documents local URLs for auth/billing and does not document the learn/academy URL. turbo.json:153-163 tracks only VITE_API_URL, VITE_AUTH_URL, VITE_BASE_PATH, and NODE_ENV for manager builds.
  • Manager admin academy routes use academyClient (apps/manager/src/routes/_admin/admin/academy/live-sessions.tsx:25, :63, :75, :113, :122, :320), which uses the VITE_LEARN_API_URL/:3065 fallback.

Impact: a Cloudflare-hosted browser can be compiled to call https://manager.assistance.bg:3046, https://manager.assistance.bg:3047/api/v1, and https://manager.assistance.bg:3065/api/v1, which are dev-port assumptions rather than durable Cloudflare production endpoints.

Recommended fix: make the production Pages build/deploy require explicit public API origins for auth, billing, and academy/learn. Add VITE_LEARN_API_URL to apps/manager/src/env.d.ts, apps/manager/.env.example, and turbo.json once the production origin is chosen. In CI/Pages, set the actual public values as environment variables/secrets; do not rely on fallback ports for production.

MEDIUM — custom domain/routing ownership is not represented as desired state in this repo#

Evidence:

  • apps/manager/wrangler.toml:1-4 declares only the Pages project name, output directory, and compatibility date; no routes/custom-domain state is represented there.
  • .mise.toml:1643-1655 expects https://manager.assistance.bg/ to be healthy, but the repo has no reviewed desired-state artifact binding manager.assistance.bg to the assistance-manager Pages project.
  • The Cloudflare Pages skill documents custom-domain binding as a REST/API operation and the Service Registry skill says registry-owned DNS/Caddy surfaces should remain coordinated rather than manually drifted.

Impact: an operator can rebuild and deploy the Pages project while the public hostname binding/DNS state remains undocumented or drifts outside the repository.

Recommended fix: document the current ownership path for manager.assistance.bg in .docs/cloudflare-deployment.md or a small runbook: Pages project, production branch, custom-domain binding owner, DNS owner, and the exact non-secret verification command. If DNS is registry-owned, use the Service Registry workflow for DNS records; if Cloudflare-owned, use the Cloudflare DNS desired-state workflow.

Positive findings#

  • Static Pages suitability is good: apps/manager/package.json:9 runs vite build, apps/manager/vite.config.ts:30-31 emits build/, and apps/manager/wrangler.toml:2-3 points Pages at the same project/output.
  • SPA routing is configured for Cloudflare Pages via apps/manager/public/_redirects:1 and was present in the built output.
  • Private/noindex and basic security headers are configured in apps/manager/public/_headers:1-10 and copied into build/_headers.
  • Artifact size is comfortably under the configured Cloudflare budget: JavaScript gzip is 319.0 KiB / 1.50 MiB, raw assets are 1.12 MiB / 15.0 MiB.
  • No Worker, KV, D1, R2, Durable Object, or service bindings are required by the current Manager Cloudflare shape. It is a static Vite SPA that talks to backend APIs over HTTP.
  • compatibility_date = "2025-04-01" is explicit (apps/manager/wrangler.toml:4). For the current static-only Pages deployment, no nodejs_compat or runtime compatibility flag is needed because there are no Pages Functions or Worker bindings.

Changes applied#

No implementation/configuration fixes were applied. The only repository change from this review is this evidence-backed audit document.