Prepara deploy de Luisa + image-worker: Dockerfiles + GET endpoints
- Dockerfiles multistage (node:20-alpine) + .dockerignore para mvp/Whatsapp-bot (expone 3000+3001, persiste prompts) y mvp/image-worker (expone 3001). - Añade los 2 GET que el bot necesita y faltaban en la API: GET /api/leads/:id (estado del lead) y GET /api/leads/:id/conversacion (historial). - Limpia el package.json/lock de la raíz (baileys colado por error; no es workspace). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
10
mvp/Whatsapp-bot/.dockerignore
Normal file
10
mvp/Whatsapp-bot/.dockerignore
Normal file
@@ -0,0 +1,10 @@
|
||||
node_modules
|
||||
dist
|
||||
coverage
|
||||
.git
|
||||
.env
|
||||
.env.*
|
||||
auth_info_baileys
|
||||
*.tsbuildinfo
|
||||
npm-debug.log
|
||||
.DS_Store
|
||||
22
mvp/Whatsapp-bot/Dockerfile
Normal file
22
mvp/Whatsapp-bot/Dockerfile
Normal file
@@ -0,0 +1,22 @@
|
||||
# Agente WhatsApp Luisa (NestJS). El proceso escucha en dos puertos:
|
||||
# - PORT (3000): app NestJS
|
||||
# - WEBHOOK_PORT (3001): servidor HTTP de webhooks entrantes (/whatsapp-start, /whatsapp-pdf)
|
||||
# La sesión de Baileys se persiste en /app/auth_info_baileys → montar un volumen ahí.
|
||||
FROM node:20-alpine AS build
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
RUN npm ci
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
|
||||
FROM node:20-alpine
|
||||
WORKDIR /app
|
||||
ENV NODE_ENV=production
|
||||
ENV PORT=3000
|
||||
ENV WEBHOOK_PORT=3001
|
||||
COPY package*.json ./
|
||||
RUN npm ci --omit=dev && npm cache clean --force
|
||||
COPY --from=build /app/dist ./dist
|
||||
COPY --from=build /app/prompts ./prompts
|
||||
EXPOSE 3000 3001
|
||||
CMD ["node", "dist/main"]
|
||||
@@ -1,13 +1,29 @@
|
||||
import { eq } from 'drizzle-orm';
|
||||
import { db } from '@/db';
|
||||
import { leads, conversacionWhatsapp } from '@/db/schema';
|
||||
import { jsonResponse } from '@/lib/api/funnel-auth';
|
||||
import { autorizado, jsonResponse } from '@/lib/api/funnel-auth';
|
||||
import { validarBotRequest } from '@/lib/api/bot-request';
|
||||
import { conversacionSchema } from '@/lib/funnel/bot-schemas';
|
||||
|
||||
export const runtime = 'nodejs';
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
// Historial de la conversación de WhatsApp (orden cronológico) para que el bot recupere el contexto.
|
||||
export async function GET(req: Request, { params }: { params: Promise<{ id: string }> }) {
|
||||
if (!autorizado(req)) return jsonResponse({ ok: false, error: 'No autorizado.' }, 401);
|
||||
const { id } = await params;
|
||||
const turnos = await db
|
||||
.select({
|
||||
rol: conversacionWhatsapp.rol,
|
||||
mensaje: conversacionWhatsapp.mensaje,
|
||||
createdAt: conversacionWhatsapp.createdAt,
|
||||
})
|
||||
.from(conversacionWhatsapp)
|
||||
.where(eq(conversacionWhatsapp.leadId, id))
|
||||
.orderBy(conversacionWhatsapp.createdAt);
|
||||
return jsonResponse(turnos, 200);
|
||||
}
|
||||
|
||||
// Añade un turno de la conversación de WhatsApp al historial del lead, y opcionalmente actualiza
|
||||
// el estado del mensaje (estado_wa) y el paso del bot (bot_step) en el lead.
|
||||
export async function POST(req: Request, { params }: { params: Promise<{ id: string }> }) {
|
||||
|
||||
34
mvp/b2c/src/app/api/leads/[id]/route.ts
Normal file
34
mvp/b2c/src/app/api/leads/[id]/route.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { eq } from 'drizzle-orm';
|
||||
import { db } from '@/db';
|
||||
import { leads } from '@/db/schema';
|
||||
import { autorizado, jsonResponse } from '@/lib/api/funnel-auth';
|
||||
|
||||
export const runtime = 'nodejs';
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
// Estado del lead para el bot de WhatsApp: le permite retomar la conversación (botStep, viabilidad,
|
||||
// extracción en crudo) tras un reinicio sin perder contexto. Devuelve el objeto plano (no envuelto).
|
||||
export async function GET(req: Request, { params }: { params: Promise<{ id: string }> }) {
|
||||
if (!autorizado(req)) return jsonResponse({ ok: false, error: 'No autorizado.' }, 401);
|
||||
const { id } = await params;
|
||||
|
||||
const [lead] = await db
|
||||
.select({
|
||||
id: leads.id,
|
||||
nombre: leads.nombre,
|
||||
telefono: leads.telefono,
|
||||
botStep: leads.botStep,
|
||||
estadoWa: leads.estadoWa,
|
||||
espacio: leads.espacio,
|
||||
rangoM2: leads.rangoM2,
|
||||
estilo: leads.estilo,
|
||||
presupuestoDeclarado: leads.presupuestoDeclarado,
|
||||
viable: leads.viable,
|
||||
})
|
||||
.from(leads)
|
||||
.where(eq(leads.id, id))
|
||||
.limit(1);
|
||||
|
||||
if (!lead) return jsonResponse({ ok: false, error: 'Lead no encontrado.' }, 404);
|
||||
return jsonResponse(lead, 200);
|
||||
}
|
||||
9
mvp/image-worker/.dockerignore
Normal file
9
mvp/image-worker/.dockerignore
Normal file
@@ -0,0 +1,9 @@
|
||||
node_modules
|
||||
dist
|
||||
coverage
|
||||
.git
|
||||
.env
|
||||
.env.*
|
||||
*.tsbuildinfo
|
||||
npm-debug.log
|
||||
.DS_Store
|
||||
19
mvp/image-worker/Dockerfile
Normal file
19
mvp/image-worker/Dockerfile
Normal file
@@ -0,0 +1,19 @@
|
||||
# Worker de renders (NestJS). Recibe POST /perfil-completo, genera las imágenes "después" con IA
|
||||
# (OpenRouter) y las devuelve a la app vía POST /api/leads/:id/ingesta. Stateless.
|
||||
FROM node:20-alpine AS build
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
RUN npm ci
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
|
||||
FROM node:20-alpine
|
||||
WORKDIR /app
|
||||
ENV NODE_ENV=production
|
||||
ENV PORT=3001
|
||||
COPY package*.json ./
|
||||
RUN npm ci --omit=dev && npm cache clean --force
|
||||
COPY --from=build /app/dist ./dist
|
||||
COPY --from=build /app/prompts ./prompts
|
||||
EXPOSE 3001
|
||||
CMD ["node", "dist/main"]
|
||||
Reference in New Issue
Block a user