Plataforma de crowdfunding para startups — monorepo con pnpm + Turborepo. GitHub Actions valida cada PR, Dokploy clona y construye las imágenes directamente en el servidor OVH.
Frontend architecture reference:
apps/web/ARCHITECTURE.md— routes, data flow, component map, Strapi conventions.
┌─────────────────────────────────────────────────────┐
│ GitHub (gratis) │
│ Pull Request → GitHub Actions (ci.yml) │
│ • lint + type-check + build (sin Docker) │
│ • Required check para mergear a main │
│ Merge a main → push │
└───────────────────────┬─────────────────────────────┘
│ webhook nativo (GitHub App)
┌───────────────────────▼─────────────────────────────┐
│ OVH (servidor propio) │
│ Dokploy │
│ • Detecta el push y filtra por Watch Paths │
│ • git clone del repo en el servidor │
│ • docker build con cache local (rápido) │
│ • ignixio-web :3000 (contenedor web) │
│ • ignixio-api :1337 (contenedor api) │
│ • PostgreSQL :5432 (servicio Dokploy) │
└─────────────────────────────────────────────────────┘
ignixio/
├── apps/
│ ├── api/ # Strapi v5 (backend / CMS)
│ │ ├── src/
│ │ │ ├── api/ # Content types: project, pledge, category, saved-project, global, auth-provider
│ │ │ ├── middlewares/ # rate-limit, security
│ │ │ └── utils/ # populate-sanitizer
│ │ ├── .dockerignore
│ │ ├── Dockerfile # Multi-stage con turbo prune
│ │ ├── package.json
│ │ └── README.md
│ └── web/ # Next.js 16 (frontend)
│ ├── app/ # App Router (auth, account, dashboard, studio, projects, landing…)
│ ├── actions/ # Server Actions
│ ├── components/ # Componentes por dominio
│ ├── hooks/ # Custom hooks
│ ├── lib/ # Strapi client, analytics, utils
│ ├── store/ # Estado global (Zustand)
│ ├── .dockerignore
│ ├── Dockerfile # Multi-stage con turbo prune + standalone
│ ├── package.json
│ └── README.md
├── packages/
│ ├── eslint-config/ # @ignixio/eslint-config
│ └── typescript-config/ # @ignixio/typescript-config
├── .github/
│ └── workflows/
│ └── ci.yml # CI: lint + type-check + build de validación
├── .dockerignore
├── .editorconfig
├── .env.example # Plantilla de todas las variables de entorno
├── .gitignore
├── .npmrc
├── .nvmrc # Node 20 LTS
├── .prettierrc
├── docker-compose.dev.yml # PostgreSQL local para desarrollo
├── package.json
├── pnpm-workspace.yaml
└── turbo.json
- Node.js 20 LTS (ver
.nvmrc) - pnpm 9.x
- Docker Desktop (para PostgreSQL en desarrollo)
pnpm installcp .env.example .env
# Edita .env con tus valores
# Para el frontend también necesitas apps/web/.env.local (ver apps/web/README.md)pnpm devEl script dev levanta PostgreSQL con Docker Compose y arrancan Strapi y Next.js en paralelo. No hace falta ejecutar Docker por separado.
O en terminales separadas:
docker compose -f docker-compose.dev.yml up -d # PostgreSQL
pnpm --filter @ignixio/api dev # → http://localhost:1337/admin
pnpm --filter @ignixio/web dev # → http://localhost:3000La primera vez que arranques Strapi te pedirá crear un usuario administrador desde el panel.
docker compose -f docker-compose.dev.yml down
# Strapi y Next.js se detienen con Ctrl+C en cada terminal| Servicio | URL |
|---|---|
| Frontend (Next.js) | http://localhost:3000 |
| Backend (Strapi) | http://localhost:1337 |
| Panel admin Strapi | http://localhost:1337/admin |
| PostgreSQL | localhost:5432 (strapi_dev) |
El flujo de aporte usa siempre documentId (proyecto y pledge). Tras POST /api/pledges, el frontend confirma el pago simulado con POST /api/pledges/:documentId/confirm, lo que marca el pledge como pagado y suma el importe a project.raised_amount. Los endpoints GET /api/users/me/pledges y GET /api/users/me/projects/:documentId/pledges alimentan Activity y la pestaña Funding del Creator Studio. La integración con Stripe queda fuera de este MVP.
El despliegue es automático y se reparte entre GitHub Actions (validación) y Dokploy (build + deploy):
Pull Request → main
│
▼
GitHub Actions (.github/workflows/ci.yml)
├── Detecta qué cambió (apps/web, apps/api o ambas)
├── lint + type-check + build (sin Docker)
└── Required check: bloquea el merge si falla
│
▼ (merge a main)
git push origin main
│
▼
Dokploy (OVH) — GitHub App connection
├── Recibe el webhook y filtra por Watch Paths
├── git clone del repo en el servidor
├── docker build con cache local (rápido)
└── Reinicia el contenedor afectado (web, api o ambos)
Tres mecanismos garantizan que solo se despliega lo correcto:
- Branch protection en GitHub sobre
main: el merge requiere CI verde, asímainsiempre es desplegable. - Auto-deploy on push en Dokploy: cualquier commit en
maindispara el ciclo. - Watch Paths por aplicación en Dokploy: filtran qué contenedor se reconstruye. Un cambio solo en
apps/api/**no toca el contenedor web, y viceversa.
Antes del primer despliegue debes tener configurado en Dokploy:
- PostgreSQL 16 corriendo como servicio
- Aplicación
ignixio-apiconectada a GitHub (Source = GitHub, branchmain, Dockerfileapps/api/Dockerfile) con dominioignixio-api.silvak.dev+ HTTPS y Watch Paths configurados - Aplicación
ignixio-webconectada a GitHub (Source = GitHub, branchmain, Dockerfileapps/web/Dockerfile) con dominioignixio.silvak.dev+ HTTPS y Watch Paths configurados
Y en GitHub:
| Variable / Setting | Tipo | Valor |
|---|---|---|
NEXT_PUBLIC_STRAPI_URL |
Variable | https://ignixio-api.silvak.dev (usada por CI) |
Branch protection main |
Setting | Require status checks: Validate web, Validate api |
Ya no se usan los secrets
DOKPLOY_WEBHOOK_WEBniDOKPLOY_WEBHOOK_API. Si vienen de la configuración anterior, bórralos.
Consulta la sección Despliegue en Dokploy para la configuración completa paso a paso.
- Re-ejecutar validación: GitHub → Actions → CI → Run workflow (opción
web,apioboth). - Forzar despliegue sin commit: Dokploy → la app que quieras → botón Redeploy.
Aunque ya no se construyen en CI, puedes reproducir lo que Dokploy hace en OVH:
# Imagen del frontend
docker build -f apps/web/Dockerfile -t ignixio-web:local \
--build-arg NEXT_PUBLIC_STRAPI_URL=http://localhost:1337 .
docker run -p 3000:3000 ignixio-web:local
# Imagen del backend
docker build -f apps/api/Dockerfile -t ignixio-api:local .
docker run -p 1337:1337 --env-file apps/api/.env ignixio-api:local| Comando | Descripción |
|---|---|
pnpm dev |
Levanta Postgres + todas las apps en modo desarrollo |
pnpm build |
Compila todas las apps (con cache de Turborepo) |
pnpm lint |
Ejecuta ESLint en todas las apps |
pnpm type-check |
Comprobación de tipos TypeScript |
pnpm clean |
Elimina artifacts de build y node_modules |
pnpm prune:web |
Genera out/ con el subgrafo de web (para Docker) |
pnpm prune:api |
Genera out/ con el subgrafo de api (para Docker) |
pnpm format |
Formatea el código con Prettier |
El archivo .github/workflows/ci.yml se encarga solo de validar. No construye imágenes Docker ni publica nada en registros — eso lo hace Dokploy directamente en el servidor OVH.
Jobs del pipeline:
detect-changes: usadorny/paths-filterpara decidir si los cambios afectan aapps/web/**(máspackages/**,pnpm-lock.yaml,package.json,turbo.json,.npmrc) y/o aapps/api/**.validate-web(condicional): instala dependencias con cache de pnpm, ejecutalint,type-checkybuild(sin Docker) del paquete@ignixio/web.validate-api(condicional): mismo flujo para@ignixio/api. Inyecta variables dummy (APP_KEYS,JWT_SECRET, etc.) que Strapi exige en build time.
Para que el CI actúe como puerta antes del despliegue, configúralo como required check en main:
- Ve a Settings → Branches → Branch protection rules → Add rule.
- Branch name pattern:
main. - Marca Require a pull request before merging.
- Marca Require status checks to pass before merging y selecciona:
Validate webValidate api
- Guarda.
Ve a Settings → Secrets and variables → Actions:
| Nombre | Tipo | Descripción |
|---|---|---|
NEXT_PUBLIC_STRAPI_URL |
Variable | URL pública de Strapi (https://ignixio-api.silvak.dev). El job validate-web la usa para que next build no inline un valor incorrecto. |
Si vienes de la configuración anterior, borra los secrets
DOKPLOY_WEBHOOK_WEByDOKPLOY_WEBHOOK_API: ya no se usan.
Desde Actions → CI → Run workflow, puedes forzar web, api o both.
Dokploy actúa en modo GitHub App connection: detecta el push a main, clona el repo en el servidor y construye la imagen Docker localmente usando el Dockerfile correspondiente. Los Watch Paths deciden si cada contenedor se reconstruye o no.
Los dominios de producción son:
| App | URL |
|---|---|
| Frontend | https://ignixio.silvak.dev |
| API / Admin | https://ignixio-api.silvak.dev |
En Dokploy → Settings → Git Providers → GitHub → Install. Esto instala la GitHub App de Dokploy en tu cuenta u organización y le da acceso al repo ignixio. Es zero-config: no hace falta crear PATs ni registrar ghcr.io.
En Dokploy → Services → New Service → Database → PostgreSQL 16.
Anota el host interno, usuario y contraseña generados (se usan en las variables de la API).
En Dokploy → Applications → New Application:
- Source: GitHub (el conectado en el Paso 1)
- Repository:
<tu-usuario>/ignixio - Branch:
main - Build Type: Dockerfile
- Dockerfile path:
apps/api/Dockerfile - Build context:
.(raíz del repo, necesario para queturbo prunevea todo el monorepo) - Auto Deploy: ON
- Port:
1337 - Domains:
ignixio-api.silvak.dev— activa HTTPS (Let's Encrypt) en la pestaña Domains
Variables de entorno (pestaña Environment):
NODE_ENV=production
HOST=0.0.0.0
PORT=1337
PUBLIC_URL=https://ignixio-api.silvak.dev
IS_PROXIED=true
CORS_ORIGINS=https://ignixio.silvak.dev,https://ignixio-api.silvak.dev
APP_KEYS=<openssl rand -base64 32>,<openssl rand -base64 32>
API_TOKEN_SALT=<openssl rand -base64 32>
ADMIN_JWT_SECRET=<openssl rand -base64 32>
TRANSFER_TOKEN_SALT=<openssl rand -base64 32>
JWT_SECRET=<openssl rand -base64 32>
ENCRYPTION_KEY=<openssl rand -base64 32>
DATABASE_CLIENT=postgres
DATABASE_HOST=<host interno de PostgreSQL en Dokploy>
DATABASE_PORT=5432
DATABASE_NAME=<nombre de la base de datos>
DATABASE_USERNAME=<usuario>
DATABASE_PASSWORD=<contraseña>
DATABASE_SSL=false
CLOUDINARY_NAME=<cloud name>
CLOUDINARY_KEY=<api key>
CLOUDINARY_SECRET=<api secret>
AUTH_GOOGLE_ID=<client id de Google>
AUTH_GOOGLE_SECRET=<client secret de Google>
RESEND_API_KEY=<api key de Resend>
EMAIL_FROM=Ignixio <noreply@tudominio.com>
EMAIL_REPLY_TO=noreply@tudominio.com
FRONTEND_URL=https://ignixio.silvak.dev
En Dokploy → Applications → New Application:
- Source: GitHub
- Repository:
<tu-usuario>/ignixio - Branch:
main - Build Type: Dockerfile
- Dockerfile path:
apps/web/Dockerfile - Build context:
. - Auto Deploy: ON
- Port:
3000 - Domains:
ignixio.silvak.dev— activa HTTPS (Let's Encrypt) en la pestaña Domains
Build Args (pestaña Build → Build Args, se inyectan como ARG en el Dockerfile):
NEXT_PUBLIC_STRAPI_URL=https://ignixio-api.silvak.dev
NEXT_PUBLIC_GOOGLE_ENABLED=true
Variables de entorno (pestaña Environment, disponibles en runtime):
NODE_ENV=production
NEXT_PUBLIC_STRAPI_URL=https://ignixio-api.silvak.dev
STRAPI_URL=https://ignixio-api.silvak.dev
STRAPI_API_TOKEN=<token read-only generado en el panel de Strapi>
AUTH_SECRET=<openssl rand -base64 32>
AUTH_URL=https://ignixio.silvak.dev
NEXT_PUBLIC_APP_URL=https://ignixio.silvak.dev
AUTH_GOOGLE_ID=<client id de Google>
AUTH_GOOGLE_SECRET=<client secret de Google>
NEXT_PUBLIC_GOOGLE_ENABLED=true
NEXT_PUBLIC_STRAPI_URLaparece dos veces a propósito: una como Build Arg (lo necesita Next al compilar el bundle del cliente) y otra como env var de runtime.
AUTH_SECRETes obligatorio en producción. Sin él,/api/auth/sessiondevuelve 500 y las rutas/loginy/registerpueden redirigir incorrectamente al dashboard. Tras añadirlo, redeployignixio-web.
Sin Watch Paths, cualquier push a main reconstruiría las dos apps aunque solo cambies un README. Para evitarlo, en cada aplicación ve a la pestaña Deployments → Watch Paths y añade los siguientes patrones, pulsando el botón "+" tras cada uno (si solo escribes y das a "Save" sin pulsar "+", no se guardan — issue conocido #2415).
ignixio-web:
apps/web/**
packages/**
pnpm-lock.yaml
package.json
turbo.json
.npmrc
ignixio-api:
apps/api/**
packages/**
pnpm-lock.yaml
package.json
turbo.json
.npmrc
Con esta configuración:
- Cambio solo en
apps/web/**→ solo reconstruyeignixio-web. - Cambio solo en
apps/api/**→ solo reconstruyeignixio-api. - Cambio en
packages/**,pnpm-lock.yaml,turbo.jsonopackage.jsonraíz → reconstruye ambas (es lo correcto, son compartidos). - Cambio solo en
README.md,.github/**,docker-compose.dev.yml, etc. → no reconstruye nada.
- Abre un PR contra
main. Comprueba que el workflowCIse ejecuta y aparece como required check. - Mergea el PR. Dokploy debería recibir el webhook al instante y, según los paths tocados, reconstruir
ignixio-web,ignixio-api, ambas o ninguna. - Sigue el progreso en Dokploy → la app → pestaña Deployments.
https://ignixio-api.silvak.dev/_health→ responde204 No Content.https://ignixio-api.silvak.dev/admin→ carga el panel sin advertencias de mixed-content.- Sube una imagen desde el panel → la URL debe comenzar con
https://res.cloudinary.com/. - Desde DevTools en
https://ignixio.silvak.dev, ejecuta:La respuesta debe incluir la cabecerafetch('https://ignixio-api.silvak.dev/api/...')
Access-Control-Allow-Origin: https://ignixio.silvak.dev. - En los logs de Strapi confirma que no aparece el warning
koa-session: secure cookies require https. - NextAuth en el frontend:
curl.exe -s "https://ignixio.silvak.dev/api/auth/session" # Debe devolver {} o null, no 500 ni mensaje de configuración curl.exe -sI "https://ignixio.silvak.dev/login" # Sin cookies de sesión: debe ser 200, no 307 Location: /dashboard
Reproduce localmente lo que Dokploy ejecuta en OVH:
# Probar la imagen web localmente
docker build -f apps/web/Dockerfile -t ignixio-web:local \
--build-arg NEXT_PUBLIC_STRAPI_URL=http://localhost:1337 .
docker run -p 3000:3000 ignixio-web:local
# Probar la imagen api localmente
docker build -f apps/api/Dockerfile -t ignixio-api:local .
docker run -p 1337:1337 --env-file .env ignixio-api:local