MCP: se retira por completo (endpoint, paquete api/app/mcp/ y dependencia). Tres razones, en orden de peso: - Nadie lo usa. - Llevaba roto desde antes de este trabajo. Con credencial valida devolvia 500: el handler construia un StreamableHTTPServerTransport nuevo por peticion, sin manejo de sesion. Lo que estaba expuesto a internet era la puerta abierta de un cuarto averiado. - Su SDK sin fijar tumbo el API entero en produccion al saltar a 2.0.0. Retirarlo es una mitigacion mas fuerte que autenticarlo, que fue lo que hizo el commit anterior. Recuperable con `git show edb500f5:api/app/mcp/server.py`. Se limpia tambien la ruta "/mcp" que el endpoint raiz seguia anunciando, y las menciones a MCP del docstring y la descripcion de Swagger. Dependencias: once de las doce eran rangos `>=` sin techo, o sea que cada reconstruccion era una tirada de dados contra PyPI. `pydantic>=2.0` habria aceptado pydantic 3 con la misma alegria con la que `mcp>=1.0.0` acepto 2.0.0. Ahora todas van fijadas a la version exacta que corre sana en produccion, capturada con pip freeze del contenedor healthy. El lado Next.js ya era reproducible via package-lock.json; por eso el web nunca se cayo durante el incidente y el api si. Docs actualizados: AGENTS.md, README.md, api/COTIZADOR_API_SKILL.md y el spec, que ademas registra en su seccion 0 las tres desviaciones de Fase 0 respecto a lo disenado (el Despliegue B cancelado, la retirada del MCP y la deriva de dependencia). Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
100 lines
4.2 KiB
Markdown
100 lines
4.2 KiB
Markdown
# Cotizador E3
|
|
|
|
Sistema de generación de cotizaciones para Consultoría E3 (marketing digital, Querétaro MX).
|
|
Servicios organizados en 4 fases, dos tipos de pago (único / mensual), planes CRM Bucéfalo
|
|
y financiamiento opcional. Exporta a PDF y Excel.
|
|
|
|
## Stack
|
|
|
|
- **Frontend / app web:** Next.js 16 (App Router) + React 19 + Tailwind CSS v4 + Zustand
|
|
- **ORM:** Prisma 7 (cliente generado en `src/generated/prisma`, driver adapter `PrismaPg`)
|
|
- **Base de datos:** PostgreSQL 16 (vía Docker)
|
|
- **Auth:** JWT (`jose`) en cookie httpOnly, contraseñas con `bcryptjs`
|
|
- **API alterna:** servicio Python FastAPI en [`api/`](api/) (para n8n / integraciones)
|
|
|
|
## Requisitos
|
|
|
|
- Node.js 20+
|
|
- Docker (para PostgreSQL)
|
|
- Un archivo `.env` en la raíz — copia [.env.example](.env.example) y define al menos `JWT_SECRET`
|
|
|
|
## Arranque rápido (Windows)
|
|
|
|
```bat
|
|
start.bat :: levanta PostgreSQL en Docker, aplica migraciones y arranca Next.js
|
|
stop.bat :: detiene todo
|
|
```
|
|
|
|
## Arranque manual
|
|
|
|
```bash
|
|
docker compose up -d postgres # base de datos
|
|
npx prisma migrate deploy # aplica migraciones
|
|
npx tsx prisma/seed.ts # carga catálogo (idempotente)
|
|
npm run dev # http://localhost:3000
|
|
```
|
|
|
|
## Comandos
|
|
|
|
| Comando | Propósito |
|
|
|---------|-----------|
|
|
| `npm run dev` | Servidor de desarrollo (puerto 3000) |
|
|
| `npm run build` | Build de producción (incluye chequeo de tipos) |
|
|
| `npm run lint` | ESLint |
|
|
| `npm run db:migrate` | `prisma migrate dev` |
|
|
| `npm run db:seed` | Carga de datos semilla |
|
|
| `npm run db:studio` | Prisma Studio |
|
|
| `npm run db:generate` | Regenera el cliente Prisma |
|
|
|
|
## API Python (opcional)
|
|
|
|
```bash
|
|
cd api
|
|
pip install -r requirements.txt
|
|
uvicorn main:app --reload --port 8000 # Swagger en /docs
|
|
```
|
|
|
|
Referencia completa de endpoints en [`api/COTIZADOR_API_SKILL.md`](api/COTIZADOR_API_SKILL.md).
|
|
|
|
## Despliegue en Coolify
|
|
|
|
El stack de producción está en [docker-compose.coolify.yml](docker-compose.coolify.yml): `postgres` + `web` (Next.js) + `api` (FastAPI). El contenedor `web` aplica las migraciones de Prisma automáticamente en cada arranque.
|
|
|
|
### Pasos
|
|
|
|
1. **Sube el repo a GitHub** (privado recomendado).
|
|
2. En Coolify: **+ New Resource → Docker Compose**, conecta el repo y la rama.
|
|
3. En la configuración del recurso, define **Docker Compose Location** = `/docker-compose.coolify.yml`.
|
|
4. Coolify detecta los servicios y asigna dominio a `web` (puerto 3000) y `api` (puerto 8000) vía las variables `SERVICE_FQDN_*` — configura los dominios deseados en la UI.
|
|
5. Define las variables de entorno en Coolify:
|
|
|
|
| Variable | Obligatoria | Notas |
|
|
|----------|-------------|-------|
|
|
| `JWT_SECRET` | Sí | `openssl rand -base64 32` |
|
|
| `API_KEY` | Sí (para el API) | Clave para agentes/n8n |
|
|
| `DB_PASSWORD` | Recomendada | Password de PostgreSQL (default `postgres`) |
|
|
| `RUN_SEED` | Primer deploy | `true` solo la primera vez; luego `false` |
|
|
| `SEED_ADMIN_EMAIL` / `SEED_ADMIN_PASSWORD` / `SEED_ADMIN_NAME` | Primer deploy | Usuario admin inicial |
|
|
| `SEED_ASESOR_EMAIL` / `SEED_ASESOR_PASSWORD` / `SEED_ASESOR_NAME` | No | Asesor opcional |
|
|
|
|
6. **Deploy.** Orden de arranque: `postgres` (healthy) → `web` (migra + seed + sirve) → `api`.
|
|
7. Después del primer despliegue exitoso, cambia `RUN_SEED` a `false` y redeploya (el seed es idempotente, pero no hace falta correrlo cada vez).
|
|
|
|
### Healthchecks
|
|
|
|
- Web: `GET /api/health` (verifica también la conexión a la BD)
|
|
- API: `GET /health`
|
|
|
|
### Notas
|
|
|
|
- No expongas el puerto 5432: los servicios se comunican por la red interna del compose.
|
|
- El volumen `postgres_data` persiste la base de datos entre deploys. No lo borres.
|
|
- El API expone REST en `https://<dominio-api>` con auth por `X-API-Key` o JWT. El servidor MCP se retiro el 2026-07-28 (ver `AGENTS.md`).
|
|
- Las versiones de `api/requirements.txt` estan fijas a proposito: un rango abierto dejo entrar `mcp` 2.0.0 y tumbo el API en produccion. Sube dependencias a proposito, no al redesplegar.
|
|
- La fórmula de financiamiento y los cálculos viven duplicados en `src/lib/calculators.ts` y `api/app/services/calculators.py` — mantenlos en paridad.
|
|
|
|
## Documentación para agentes
|
|
|
|
Las convenciones del proyecto, gotchas de Prisma 7 / PDFKit / Next.js 16 y el modelo de datos
|
|
están en [`AGENTS.md`](AGENTS.md) (importado por `CLAUDE.md`).
|