Ir al contenido

Deploy y entornos

EMA Well se despliega como un único worker de Cloudflare que sirve tanto la API Hono como los assets estáticos del SPA. La operación es deliberadamente simple: un comando empaqueta y publica todo, sin distinción entre “deploy de frontend” y “deploy de backend”. Esa unificación elimina toda una categoría de errores típicos de proyectos con dos pipelines.

El despliegue se ejecuta desde la raíz del repo:

Ventana de terminal
cd emawell
npm install
npm run deploy

El script npm run deploy encapsula dos pasos secuenciales: primero vite build compila la SPA en dist/client, y a continuación wrangler deploy empuja el worker —API Hono más assets estáticos— a Cloudflare. El binding [assets] del wrangler config permite que el mismo worker sirva tanto rutas de API (bajo /api/*) como los archivos estáticos del SPA (todo lo demás), con fallback al index.html para que React Router maneje las rutas client-side.

El producto se apoya en cinco plataformas externas además de Cloudflare. La separación es intencional: cada servicio cumple una función específica y se mantiene aislado del resto, lo que evita acoplamientos innecesarios:

ComponentePlataformaFunción
API y assetsCloudflare Workers (un solo worker con binding [assets])Backend y frontend en un solo deploy
Base de datosSupabase (proyecto independiente emawell)Postgres aislado del resto del ecosistema
EmailLoops.soTransaccional, nurturing y resúmenes
PagosStripeSuscripción Pro mensual y anual
Push notificationsVAPID self-hostedWeb Push estándar

El worker depende de nueve secrets que se configuran via wrangler secret put <NOMBRE> o desde el dashboard de Cloudflare. Tres categorías agrupan los secrets por función:

Los secrets de SupabaseSUPABASE_URL, SUPABASE_ANON_KEY y SUPABASE_SERVICE_ROLE_KEY— controlan el acceso al Postgres aislado. La anon key se usa para queries con RLS aplicado (el flujo del usuario común); la service role key se usa solo en crons y jobs de administración que requieren bypass de RLS controlado.

Los secrets de integraciones externasLOOPS_API_KEY, ANTHROPIC_API_KEY, STRIPE_SECRET_KEY y STRIPE_WEBHOOK_SECRET— habilitan los servicios correspondientes. La key de Anthropic solo se usa cuando el tier del usuario es Pro, lo que mantiene los costos de IA acotados al ARR generado por suscripciones.

Los secrets de Push notificationsVAPID_PUBLIC_KEY y VAPID_PRIVATE_KEY— permiten enviar notificaciones push estándar a los usuarios que las habilitaron explícitamente.

El custom domain well.emahealth.io se configura en el dashboard de Cloudflare apuntando al worker. La PWA se instala desde ese dominio sin pasar por tiendas de aplicaciones, lo que evita el costo y la fricción de revisión de Apple Store y Google Play.

Dos crons corren periódicamente, definidos en wrangler.toml:

[triggers]
crons = [
"0 0 * * 0", # Domingo 00:00 UTC — weekly summary
"0 20 * * *", # Diario 20:00 UTC — streak at risk
]

El de domingo a la medianoche UTC dispara los resúmenes semanales para usuarios con al menos dos check-ins esa semana. El diario a las 20:00 UTC chequea quiénes no hicieron check-in ese día y, si habilitaron notificaciones, les manda alerta de racha en riesgo.

El repo sigue conventional commits (feat:, fix:, chore:, etc.) por consistencia con el resto del ecosistema. Los tests viven en cinco subcarpetasunit/, integration/, regression/, performance/, security/— ejecutables individualmente o en bloque con Vitest. El deploy es manual o mediante la integración Git de Cloudflare; deliberadamente no se mantiene una pipeline de GitHub Actions, porque el workflow del equipo prefiere que cada deploy sea una decisión consciente y no un efecto secundario de un merge.