Skip to content

Deployment

PinTeach runs on 4 services across 3 providers. All infrastructure is in EU regions for GDPR compliance.

pinteach.com ─── Cloudflare Worker `pinteach-web` (repo aparte)
pinteach.app ─── Cloudflare Worker (React SPA)
api.pinteach.app ─── Cloudflare Worker (Hono), detrás del proxy strangler
docs.pinteach.app ─── Cloudflare Pages (Starlight docs, static)
Base ─── Cloudflare D1 (`pinteach-d1`, WEUR)
DNS + CDN ─── Cloudflare

Setting Value
Producción pinteach-d1 (dd50c4c0-92dd-409c-917d-f37208716989), WEUR
Desarrollo pinteach-d1-dev (9d54af21-acc8-429a-bf04-a9b3957c9e74)
Migraciones packages/db/src/migrations, las aplica deploy-api.yml
Local libsql sobre file:.dev/pinteach.db

Fue Neon Postgres por Hyperdrive hasta el corte a D1 del 7-sep-2026. Neon y la configuración de Hyperdrive se retiraron el 1-oct-2026; el export previo está en el bucket R2 privado pinteach-neon-export (ver apps/api/docs/CUTOVER-D1.md).


API — Cloudflare Worker (api.pinteach.app)

Section titled “API — Cloudflare Worker (api.pinteach.app)”

El servicio de Cloud Run se borró el 7-sep-2026 y con él el Dockerfile, el registro de imágenes y el min-instances=1 que hacía falta para que BullMQ no se durmiera. Hoy la API es un Worker.

Ajuste Valor
Worker pinteach-api (apps/api/wrangler.jsonc, bloque env.production)
Entrada src/worker/index.ts (Hono 4)
Base D1 por el binding DB
Otros bindings KV, CLASSROOM (Durable Object), CLASSROOM_ASSETS (R2), GOOGLE_QUEUE (Queues + DLQ)
Crons triggers.crons — ver Background Jobs
Dominio api.pinteach.app, propiedad del proxy deploy/api-proxy

api.pinteach.app no apunta al Worker directamente: el dueño del dominio es deploy/api-proxy, que enruta por prefijo. Su lista MIGRATED decide qué va al Worker por service binding; lo demás salía al origen de Cloud Run, que ya no existe. Es decir: un prefijo que el Worker sirve y MIGRATED no nombra da 404 en producción, sin error de despliegue ni log raro. Pasó con /api/classroom y con /_meta.

Por eso el cruce está gateado: apps/api/src/worker/__tests__/proxy-migrated.test.ts lee los dos ficheros y falla en las dos direcciones. Es la puerta del workflow deploy-proxy.yml, que además compila el proxy con --dry-run antes de desplegar.

Automático, por GitHub Actions:

Workflow Qué despliega Disparo
deploy-api.yml Worker pinteach-api push a main que toque apps/api/** o packages/**
deploy-proxy.yml Worker del proxy push a main que toque deploy/api-proxy/**, con el cruce de rutas como puerta
deploy-app.yml SPA (pinteach.app) push a main que toque apps/app/**

A mano, desde apps/api/:

Terminal window
CI=true CF_API_TOKEN="" CLOUDFLARE_API_TOKEN="" pnpm exec wrangler deploy --env production

Los secretos del Worker se declaran en apps/api/scripts/secretos.ts y se cruzan con lo desplegado usando pnpm --filter @pinteach/api sync-secrets. El origen es 1Password (Pinteach/pinteach-api-prod); nunca se teclean a mano.

Las de vars están en wrangler.jsonc (env.production); las secretas, en scripts/secretos.ts. Ya NO existen PORT, HOST ni REDIS_URL — eran del proceso Node.


pinteach.com lo sirve el Worker pinteach-web (repo enlolab/pinteach-web, código de jefe-sites) desde el 18-sep-2026. El sitio Astro de este monorepo (apps/web) se retiró el 1-oct-2026: los perfiles y el directorio viven en la SPA (/t, /t/$slug), ver la sección siguiente.


React SPA — Cloudflare Worker (pinteach.app)

Section titled “React SPA — Cloudflare Worker (pinteach.app)”
Setting Value
Provider Cloudflare Workers (Static Assets)
Framework React 19 + Vite 6 (static SPA)
Package @pinteach/app
Domain pinteach.app

The app is deployed as a Cloudflare Worker serving static assets via wrangler.toml. This gives better analytics, edge middleware support, and a clean .app domain separation from the public marketing site.

name = "pinteach-app"
compatibility_date = "2025-05-01"
compatibility_flags = ["nodejs_compat"]
[assets]
directory = "./dist"
html_handling = "auto-trailing-slash"
not_found_handling = "single-page-application"

not_found_handling = "single-page-application" ensures TanStack Router receives all paths and handles client-side routing correctly.

Automático por deploy-app.yml en cada push a main que toque apps/app/**. A mano:

Terminal window
pnpm --filter @pinteach/app deploy # vite build && wrangler deploy
Variable Value
VITE_API_URL https://api.pinteach.app/api

Platform Docs — Cloudflare Pages (docs.pinteach.app)

Section titled “Platform Docs — Cloudflare Pages (docs.pinteach.app)”
Setting Value
Provider Cloudflare Pages
Framework Astro Starlight (static)
Package @pinteach/platform
Domain docs.pinteach.app

Internal documentation. Not publicly linked from the marketing site.

Field Value
Framework preset Astro
Build command pnpm install && pnpm build --filter=@pinteach/platform
Build output directory apps/platform/dist
Variable Value
SKIP_DEPENDENCY_INSTALL true
NODE_VERSION 22

Type Name Target Proxy
Worker custom domain @ y www Worker pinteach-web (gestionado por Cloudflare)
Type Name Target Proxy
CNAME @ / root <worker>.workers.dev Proxied (orange)
CNAME api ghs.googlehosted.com DNS only (gray)
CNAME docs <pages-project>.pages.dev Proxied (orange)

Service Plan Estimated Cost
Cloudflare D1 incluido en Workers de pago $0
Cloudflare Workers API + proxy + SPA, plan de pago incluido
Cloudflare Pages Free (3 projects, unlimited bandwidth) $0/mo
Cloudflare Workers Free (100K requests/day) $0/mo
Total ~$16 — $45/mo

Scaling notes:

  • Cloudflare Workers free tier: 100K requests/day — sufficient for a teacher dashboard

deploy-api.yml y deploy-proxy.yml se disparan en cada push a main que toque sus rutas. Arriba está el detalle y el comando para desplegar a mano.

Cloudflare Pages auto-deploys on push to main:

  • docs.pinteach.app — rebuilds @pinteach/platform

Las aplica deploy-api.yml en cada despliegue (wrangler d1 migrations apply DB --env production --remote). A mano, desde apps/api/:

Terminal window
pnpm exec wrangler d1 migrations apply DB --env production --remote