feat: add Layer 1 foundation - Docker, WhatsApp Core, API Gateway, Frontend setup

- docker-compose.yml with PostgreSQL, Redis, all services
- nginx/nginx.conf reverse proxy configuration
- services/whatsapp-core: package.json, tsconfig.json, Dockerfile
- services/api-gateway: requirements.txt, Dockerfile, app/__init__.py
- frontend: package.json, tsconfig.json, vite.config.ts, index.html, Dockerfile, nginx.conf

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Claude AI
2026-01-29 09:45:00 +00:00
parent b4355fbc96
commit 31d68bc118
15 changed files with 404 additions and 0 deletions

104
docker-compose.yml Normal file
View File

@@ -0,0 +1,104 @@
version: '3.8'
services:
postgres:
image: postgres:16-alpine
container_name: wac_postgres
restart: unless-stopped
environment:
POSTGRES_DB: ${DB_NAME:-whatsapp_central}
POSTGRES_USER: ${DB_USER:-whatsapp_admin}
POSTGRES_PASSWORD: ${DB_PASSWORD:?DB_PASSWORD required}
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-whatsapp_admin}"]
interval: 10s
timeout: 5s
retries: 5
networks:
- wac_network
redis:
image: redis:7-alpine
container_name: wac_redis
restart: unless-stopped
command: redis-server --appendonly yes
volumes:
- redis_data:/data
ports:
- "6379:6379"
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
networks:
- wac_network
whatsapp-core:
build:
context: ./services/whatsapp-core
dockerfile: Dockerfile
container_name: wac_whatsapp
restart: unless-stopped
environment:
NODE_ENV: ${NODE_ENV:-production}
REDIS_URL: redis://redis:6379
API_GATEWAY_URL: http://api-gateway:8000
WS_PORT: 3001
volumes:
- whatsapp_sessions:/app/sessions
ports:
- "3001:3001"
depends_on:
redis:
condition: service_healthy
networks:
- wac_network
api-gateway:
build:
context: ./services/api-gateway
dockerfile: Dockerfile
container_name: wac_api
restart: unless-stopped
environment:
DATABASE_URL: postgresql://${DB_USER:-whatsapp_admin}:${DB_PASSWORD}@postgres:5432/${DB_NAME:-whatsapp_central}
REDIS_URL: redis://redis:6379
JWT_SECRET: ${JWT_SECRET:?JWT_SECRET required}
WHATSAPP_CORE_URL: http://whatsapp-core:3001
CORS_ORIGINS: ${CORS_ORIGINS:-http://localhost:5173,http://localhost:3000}
ports:
- "8000:8000"
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
networks:
- wac_network
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
container_name: wac_frontend
restart: unless-stopped
ports:
- "3000:80"
depends_on:
- api-gateway
networks:
- wac_network
volumes:
postgres_data:
redis_data:
whatsapp_sessions:
networks:
wac_network:
driver: bridge

18
frontend/Dockerfile Normal file
View File

@@ -0,0 +1,18 @@
FROM node:20-alpine as builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

13
frontend/index.html Normal file
View File

@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>WhatsApp Centralizado</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

28
frontend/nginx.conf Normal file
View File

@@ -0,0 +1,28 @@
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://api-gateway:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location /auth {
proxy_pass http://api-gateway:8000;
proxy_set_header Host $host;
}
location /ws {
proxy_pass http://whatsapp-core:3001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}

30
frontend/package.json Normal file
View File

@@ -0,0 +1,30 @@
{
"name": "whatsapp-centralizado-frontend",
"private": true,
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview"
},
"dependencies": {
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^7.1.1",
"antd": "^5.23.1",
"@ant-design/icons": "^5.6.1",
"@tanstack/react-query": "^5.64.1",
"axios": "^1.7.9",
"zustand": "^5.0.3",
"socket.io-client": "^4.8.1",
"dayjs": "^1.11.13"
},
"devDependencies": {
"@types/react": "^18.3.18",
"@types/react-dom": "^18.3.5",
"@vitejs/plugin-react": "^4.3.4",
"typescript": "^5.7.3",
"vite": "^6.0.7"
}
}

25
frontend/tsconfig.json Normal file
View File

@@ -0,0 +1,25 @@
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}

View File

@@ -0,0 +1,10 @@
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}

29
frontend/vite.config.ts Normal file
View File

@@ -0,0 +1,29 @@
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
server: {
port: 5173,
proxy: {
'/api': {
target: 'http://localhost:8000',
changeOrigin: true,
},
'/auth': {
target: 'http://localhost:8000',
changeOrigin: true,
},
'/ws': {
target: 'http://localhost:3001',
ws: true,
},
},
},
});

50
nginx/nginx.conf Normal file
View File

@@ -0,0 +1,50 @@
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
upstream api {
server api-gateway:8000;
}
upstream whatsapp {
server whatsapp-core:3001;
}
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://api;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 300;
}
location /auth {
proxy_pass http://api;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location /ws {
proxy_pass http://whatsapp;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 86400;
}
}
}

View File

@@ -0,0 +1,17 @@
FROM python:3.11-slim
WORKDIR /app
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
libpq-dev \
&& rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app ./app
EXPOSE 8000
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]

View File

@@ -0,0 +1 @@
# WhatsApp Centralizado - API Gateway

View File

@@ -0,0 +1,12 @@
fastapi==0.115.6
uvicorn[standard]==0.34.0
sqlalchemy==2.0.36
alembic==1.14.0
psycopg2-binary==2.9.10
python-jose[cryptography]==3.3.0
passlib[bcrypt]==1.7.4
python-multipart==0.0.20
pydantic==2.10.4
pydantic-settings==2.7.1
redis==5.2.1
httpx==0.28.1

View File

@@ -0,0 +1,19 @@
FROM node:20-alpine
WORKDIR /app
RUN apk add --no-cache python3 make g++
COPY package*.json ./
RUN npm ci
COPY tsconfig.json ./
COPY src ./src
RUN npm run build
RUN mkdir -p /app/sessions
EXPOSE 3001
CMD ["npm", "start"]

View File

@@ -0,0 +1,29 @@
{
"name": "whatsapp-core",
"version": "1.0.0",
"description": "WhatsApp Core service using Baileys",
"main": "dist/index.js",
"scripts": {
"build": "tsc",
"start": "node dist/index.js",
"dev": "ts-node-dev --respawn src/index.ts",
"lint": "eslint src/**/*.ts"
},
"dependencies": {
"@whiskeysockets/baileys": "^6.7.16",
"express": "^4.21.2",
"socket.io": "^4.8.1",
"ioredis": "^5.4.1",
"pino": "^9.6.0",
"qrcode": "^1.5.4",
"uuid": "^11.0.5"
},
"devDependencies": {
"@types/express": "^5.0.0",
"@types/node": "^22.10.7",
"@types/uuid": "^10.0.0",
"@types/qrcode": "^1.5.5",
"typescript": "^5.7.3",
"ts-node-dev": "^2.0.0"
}
}

View File

@@ -0,0 +1,19 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "commonjs",
"lib": ["ES2022"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}