Ir al contenido

Runbooks

Procedimientos operativos. Para cada uno: qué hace, cuándo se ejecuta, comandos exactos, qué verificar al final.

  1. Deploy de tcgcards-web (frontend)
  2. Deploy de tcgcards-api (backend)
  3. Rollback de un deploy
  4. Restaurar backup de Mongo
  5. Rotar un secret
  6. Agregar/quitar admin a un usuario
  7. Forzar la ejecución de un cron job
  8. Actualizar/agregar credenciales Mercado Pago
  9. Investigar un error en producción
  10. Cambiar dominio

Cuándo: cada vez que quieres que un cambio del repo tcgcards-web llegue a tcgcards.cl.

Procedimiento:

  1. Estar en main y tener los cambios pusheados a GitHub.
  2. Esperar. Vercel hace el deploy automáticamente al detectar el push.

Si quieres forzar un deploy sin nuevos commits (raro):

Ventana de terminal
cd /Users/yoel/Documents/Repos/tcgcards-web
vercel --prod

Cómo verificar:

Tiempo total: ~2-3 min.


Cuándo: cada vez que quieres que un cambio del repo tcgcards-api llegue a producción. Esto no es automático.

Procedimiento:

Ventana de terminal
cd /Users/yoel/Documents/Repos/tcgcards-api
# 1. Verificar que tests + build pasen local
npm test
npm run build
# 2. Pushear a main (si tienes cambios sin pushear)
git push
# 3. Deploy a Cloud Run
gcloud run deploy tcgcards-api \
--source . \
--region us-central1 \
--project api-cards-prod \
--quiet

Cómo verificar:

Ventana de terminal
# Status del servicio
gcloud run services describe tcgcards-api \
--region us-central1 \
--project api-cards-prod \
--format="value(status.conditions[0].type,status.conditions[0].status,status.latestReadyRevisionName)"
# Esperado: "Ready True tcgcards-api-XXXXX-xxx"
# Sanity check endpoint público
curl -s -o /dev/null -w "%{http_code}\n" \
"https://tcgcards-api-1033181994095.us-central1.run.app/api/v1/health"
# Esperado: 200

Tiempo total: 5-8 min (build + container push + revision routing).


  1. https://vercel.comtcgcards-web → Deployments
  2. Encuentra el deploy anterior bueno.
  3. Click en los ... → “Promote to Production”.

Toma ~30 segundos. No requiere CLI.

Ventana de terminal
# 1. Listar revisiones recientes
gcloud run revisions list \
--service tcgcards-api \
--region us-central1 \
--project api-cards-prod \
--limit 5
# 2. Apuntar 100% del tráfico a una revisión específica
gcloud run services update-traffic tcgcards-api \
--to-revisions=tcgcards-api-00128-gpd=100 \
--region us-central1 \
--project api-cards-prod

(Reemplaza tcgcards-api-00128-gpd por la revisión deseada.)

Cómo verificar: comando describe igual que en deploy.


Cuándo: corrupción de datos, borrado accidental, o restauración en ambiente paralelo.

RPO efectivo: ~1 hora (backups hourly via Cloud Scheduler).

Ventana de terminal
brew tap mongodb/brew
brew install mongodb-database-tools
brew install mongodb-community
brew services start mongodb-community
Ventana de terminal
# 1. Buscar el dump más reciente en GCS
mkdir -p /tmp/restore-drill
LATEST=$(gcloud storage ls gs://tcgcards-mongo-backups/ \
--project=api-cards-prod | sort | tail -1)
echo "Último backup: $LATEST"
# 2. Descargar
gcloud storage cp "$LATEST" /tmp/restore-drill/dump.archive.gz \
--project=api-cards-prod
# 3. Validar integridad
gzip -t /tmp/restore-drill/dump.archive.gz
# 4. Restore contra Mongo local (DESTRUCTIVO si tienes data local)
mongorestore --archive=/tmp/restore-drill/dump.archive.gz --gzip --drop

Cómo verificar:

Ventana de terminal
mongosh
> use api-cards
> db.cards.countDocuments() // debería coincidir con prod
> db.orders.countDocuments()

Para restore a un cluster Atlas distinto del de producción, usar --uri="mongodb+srv://..." en mongorestore.

Runbook detallado del drill exitoso: tcgcards-api/docs/runbooks/restore-drill-2026-05-11.md.


Ventana de terminal
# 1. Generar el nuevo valor (ejemplo para secret aleatorio de 32 bytes hex)
NEW_VALUE=$(openssl rand -hex 32)
# 2. Agregar como nueva versión
echo -n "$NEW_VALUE" | gcloud secrets versions add INTERNAL_JWT_SECRET \
--data-file=- \
--project=api-cards-prod
# 3. Verificar
gcloud secrets versions access latest \
--secret=INTERNAL_JWT_SECRET \
--project=api-cards-prod
# 4. Redeploy del Cloud Run para que aplique
cd /Users/yoel/Documents/Repos/tcgcards-api
gcloud run deploy tcgcards-api \
--source . \
--region us-central1 \
--project api-cards-prod \
--quiet
Ventana de terminal
# Listar
vercel env ls
# Quitar el viejo
vercel env rm INTERNAL_JWT_SECRET production
# Agregar el nuevo (interactive, pide valor)
vercel env add INTERNAL_JWT_SECRET production
# Redeploy para que aplique
vercel --prod

Cambiar valor sensible y mantener compatibilidad

Sección titulada «Cambiar valor sensible y mantener compatibilidad»

Para minimizar downtime al rotar secrets compartidos:

  1. Generar nuevo valor.
  2. Setear en backend + redeploy.
  3. Setear en frontend + redeploy.
  4. (Entre paso 2 y 3 hay ~2 min de incompatibilidad. Hacer en horario de menor tráfico.)

No hay UI para esto. Se hace por Mongo directo.

Ventana de terminal
# Conectar
mongosh "<MONGODB_URI>"
> use api-cards
# Hacer admin
> db.users.updateOne({ username: "ejemplo" }, { $set: { isAdmin: true } })
# Quitar admin
> db.users.updateOne({ username: "ejemplo" }, { $set: { isAdmin: false } })
# Verificar
> db.users.findOne({ username: "ejemplo" }, { isAdmin: 1, username: 1 })

El cambio aplica inmediatamente — el siguiente request del user con sesión activa lo verá como admin. No hace falta reiniciar nada.


Ventana de terminal
gcloud scheduler jobs run orders-tick \
--project=api-cards-prod \
--location=us-central1

Reemplaza orders-tick por el job que quieras. Lista completa:

  • mongo-backup-hourly
  • orders-tick
  • orders-awaiting-payment-cleanup
  • wallet-reconcile-daily
  • refunds-reconcile-daily

Cómo verificar:

Ventana de terminal
# Ver logs del Cloud Run del último minuto
gcloud run services logs read tcgcards-api \
--region=us-central1 \
--project=api-cards-prod \
--limit=50

Buscar líneas con prefijo cron. (e.g., cron.canceled, cron.completed).


Actualizar/agregar credenciales Mercado Pago

Sección titulada «Actualizar/agregar credenciales Mercado Pago»

Cuándo: se necesita rotar MP_ACCESS_TOKEN, el webhook secret, o cuando MP cambia algo del lado de ellos.

Ventana de terminal
# 1. Obtener token nuevo desde el panel MP → Credenciales productivas (o test si testing)
# El token es de la forma APP_USR-XXXXXX-XXXXXX-XXXXXX-XXXXXX
# 2. Actualizar el secret
echo -n "APP_USR-..." | gcloud secrets versions add MP_ACCESS_TOKEN \
--data-file=- \
--project=api-cards-prod
# 3. Redeploy
cd /Users/yoel/Documents/Repos/tcgcards-api
gcloud run deploy tcgcards-api --source . --region us-central1 --project api-cards-prod --quiet

Para el MP_WEBHOOK_SECRET:

  1. En el panel MP, configurar el webhook con el endpoint: https://tcgcards-api-1033181994095.us-central1.run.app/api/v1/webhooks/mp
  2. MP genera un secret — copiarlo
  3. echo -n "SECRET" | gcloud secrets versions add MP_WEBHOOK_SECRET --data-file=- --project=api-cards-prod
  4. Redeploy.

https://sentry.io → proyecto correspondiente (web o api) → buscar el error por mensaje o usuario.

Ventana de terminal
gcloud run services logs read tcgcards-api \
--region=us-central1 \
--project=api-cards-prod \
--limit=100 \
--format=json | jq '.[] | select(.severity == "ERROR")'

3. Mongo directo (datos del user/orden afectada)

Sección titulada «3. Mongo directo (datos del user/orden afectada)»
Ventana de terminal
mongosh "<MONGODB_URI>"
> use api-cards
> db.orders.findOne({ _id: "order-xxxxx" })
> db.users.findOne({ email: "afectado@example.com" }, { walletBalance: 1, status: 1 })

https://app.posthog.com → buscar por user properties (email/username).


Si alguna vez se cambia el dominio tcgcards.cl a otro:

  1. DNS en Cloudflare: apuntar el nuevo dominio a Vercel + Cloud Run.
  2. Vercel: agregar el nuevo dominio en el proyecto tcgcards-web → Settings → Domains.
  3. Env vars del web: actualizar NEXT_PUBLIC_SITE_URL → redeploy Vercel.
  4. Env vars del api: actualizar WEB_PUBLIC_URL (afecta los emails que mandamos) → redeploy Cloud Run.
  5. OAuth Google: agregar https://nuevo-dominio.cl/api/auth/callback/google a los Authorized Redirect URIs en Google Cloud Console.
  6. MP webhooks: registrar el nuevo URL en el panel MP.
  7. Resend: verificar el nuevo dominio (SPF + DKIM en Cloudflare DNS) para poder mandar emails como notificaciones@nuevo-dominio.
  8. Email Routing en Cloudflare: repetir setup soporte@ y disputas@ para el nuevo dominio.
  9. Sentry: agregar el nuevo dominio a CORS allowlist en cada proyecto.
  10. Cloudflare Access (docs): actualizar la aplicación de Zero Trust si el dominio de docs también cambia.

Es un cambio grande — hacer en ventana de mantenimiento, con OK explícito.