Some checks are pending
CI Pipeline / changes (push) Waiting to run
CI Pipeline / core (push) Blocked by required conditions
CI Pipeline / trading-backend (push) Blocked by required conditions
CI Pipeline / trading-data-service (push) Blocked by required conditions
CI Pipeline / trading-frontend (push) Blocked by required conditions
CI Pipeline / erp-core (push) Blocked by required conditions
CI Pipeline / erp-mecanicas (push) Blocked by required conditions
CI Pipeline / gamilit-backend (push) Blocked by required conditions
CI Pipeline / gamilit-frontend (push) Blocked by required conditions
Core: - Add catalog reference implementations (auth, payments, notifications, websocket, etc.) - New agent profiles: Database Auditor, Integration Validator, LLM Agent, Policy Auditor, Trading Strategist - Update SIMCO directives and add escalation/git guidelines - Add deployment inventory and audit execution reports Projects: - erp-suite: DevOps configs, Dockerfiles, shared libs, vertical enhancements - gamilit: Test structure, admin controllers, service refactoring, husky/commitlint - trading-platform: MT4 gateway, auth controllers, admin frontend, deployment scripts - platform_marketing_content: Full DevOps setup, tests, Docker configs - betting-analytics/inmobiliaria-analytics: Initial app structure 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
42 KiB
42 KiB
Arquitectura de Despliegue - Workspace Multi-Proyecto
Resumen Ejecutivo
| Aspecto | Valor |
|---|---|
| Servidor Principal | 72.60.226.4 |
| Servidor Gamilit | 74.208.126.102 |
| Reverse Proxy | Nginx |
| Registry Git | Gitea (72.60.226.4:3000) / GitHub (gamilit) |
Métodos de Despliegue por Servidor
| Servidor | Proyectos | Método CI/CD | Process Manager |
|---|---|---|---|
| 72.60.226.4 | trading-platform, erp-suite, pmc, betting, inmobiliaria | Jenkins + Docker | Docker Compose |
| 74.208.126.102 | gamilit | Manual (git pull + PM2) | PM2 Cluster |
IMPORTANTE: Gamilit NO usa Jenkins. Se despliega manualmente con PM2.
1. Arquitectura de Servidores
┌─────────────────────────────────────────────────────────────────────────────┐
│ SERVIDOR PRINCIPAL │
│ 72.60.226.4 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────────────────────────────────────────┐ │
│ │ NGINX │ │ DOCKER CONTAINERS │ │
│ │ (Port 80) │───>│ │ │
│ │ (Port 443) │ │ ┌─────────────┐ ┌─────────────┐ │ │
│ └─────────────┘ │ │ trading- │ │ erp-suite │ │ │
│ │ │ │ platform │ │ (verticales)│ │ │
│ │ │ │ FE:3080 │ │ FE:3010-3070│ │ │
│ │ │ │ BE:3081 │ │ BE:3011-3071│ │ │
│ │ │ └─────────────┘ └─────────────┘ │ │
│ │ │ │ │
│ │ │ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ │ pmc │ │ betting/ │ │ │
│ │ │ │ FE:3110 │ │ inmobiliaria│ │ │
│ │ │ │ BE:3111 │ │ (reservado) │ │ │
│ │ │ └─────────────┘ └─────────────┘ │ │
│ │ └─────────────────────────────────────────────────┘ │
│ │ │
│ ┌─────────────┐ ┌─────────────────────────────────────────────────┐ │
│ │ JENKINS │ │ INFRAESTRUCTURA │ │
│ │ (Port 8080)│ │ PostgreSQL: 5432 │ Redis: 6379 │ │
│ └─────────────┘ │ Gitea: 3000 │ Registry: 5000 │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ SERVIDOR GAMILIT │
│ 74.208.126.102 │
├─────────────────────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────────────────────────────────────────┐ │
│ │ NGINX │ │ PM2 CLUSTER │ │
│ │ (Port 80) │───>│ Backend: 2 instances (Port 3006) │ │
│ │ (Port 443) │ │ Frontend: 1 instance (Port 3005) │ │
│ └─────────────┘ └─────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ PostgreSQL: 5432 (gamilit_platform) │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
2. Estructura de Repositorios
2.1 Repositorios Independientes por Proyecto
| Proyecto | Repositorio | Servidor Deploy |
|---|---|---|
| gamilit | github.com/rckrdmrd/gamilit-workspace.git |
74.208.126.102 |
| trading-platform | 72.60.226.4:3000/rckrdmrd/trading-platform.git |
72.60.226.4 |
| erp-suite | 72.60.226.4:3000/rckrdmrd/erp-suite.git |
72.60.226.4 |
| platform-marketing-content | 72.60.226.4:3000/rckrdmrd/pmc.git |
72.60.226.4 |
| betting-analytics | 72.60.226.4:3000/rckrdmrd/betting-analytics.git |
72.60.226.4 |
| inmobiliaria-analytics | 72.60.226.4:3000/rckrdmrd/inmobiliaria-analytics.git |
72.60.226.4 |
2.2 Estructura Interna de Cada Repositorio
proyecto/
├── apps/
│ ├── backend/
│ │ ├── src/
│ │ ├── Dockerfile
│ │ ├── package.json
│ │ ├── .env.example
│ │ └── .env.production
│ └── frontend/
│ ├── src/
│ ├── Dockerfile
│ ├── nginx.conf
│ ├── package.json
│ ├── .env.example
│ └── .env.production
├── database/
│ ├── schemas/
│ ├── seeds/
│ └── migrations/
├── docker/
│ ├── docker-compose.yml
│ ├── docker-compose.prod.yml
│ └── .env.docker
├── jenkins/
│ └── Jenkinsfile
├── nginx/
│ └── project.conf
├── scripts/
│ ├── deploy.sh
│ ├── rollback.sh
│ └── health-check.sh
├── .env.ports
└── README.md
3. Asignación de Subdominios
3.1 Servidor Principal (72.60.226.4)
| Subdominio | Proyecto | Frontend | Backend API |
|---|---|---|---|
trading.isem.dev |
trading-platform | 3080 | 3081 |
api.trading.isem.dev |
trading-platform | - | 3081 |
erp.isem.dev |
erp-core | 3010 | 3011 |
api.erp.isem.dev |
erp-core | - | 3011 |
construccion.erp.isem.dev |
construccion | 3020 | 3021 |
vidrio.erp.isem.dev |
vidrio-templado | 3030 | 3031 |
mecanicas.erp.isem.dev |
mecanicas-diesel | 3040 | 3041 |
retail.erp.isem.dev |
retail | 3050 | 3051 |
clinicas.erp.isem.dev |
clinicas | 3060 | 3061 |
pos.erp.isem.dev |
pos-micro | 3070 | 3071 |
pmc.isem.dev |
platform-marketing-content | 3110 | 3111 |
api.pmc.isem.dev |
platform-marketing-content | - | 3111 |
betting.isem.dev |
betting-analytics | 3090 | 3091 |
inmobiliaria.isem.dev |
inmobiliaria-analytics | 3100 | 3101 |
3.2 Servidor Gamilit (74.208.126.102)
| Subdominio | Proyecto | Frontend | Backend API |
|---|---|---|---|
gamilit.com |
gamilit | 3005 | 3006 |
api.gamilit.com |
gamilit | - | 3006 |
app.gamilit.com |
gamilit | 3005 | - |
4. Configuración Nginx
4.1 Nginx Principal (72.60.226.4)
Archivo: /etc/nginx/nginx.conf
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
multi_accept on;
use epoll;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Logging
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
# Performance
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
client_max_body_size 100M;
# Gzip
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml application/json application/javascript
application/xml application/xml+rss text/javascript application/x-javascript;
# Rate Limiting
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
# Upstreams
include /etc/nginx/upstreams/*.conf;
# Virtual Hosts
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
4.2 Upstreams por Proyecto
Archivo: /etc/nginx/upstreams/projects.conf
# =============================================================================
# TRADING PLATFORM
# =============================================================================
upstream trading_frontend {
server 127.0.0.1:3080;
keepalive 32;
}
upstream trading_backend {
server 127.0.0.1:3081;
keepalive 32;
}
upstream trading_websocket {
server 127.0.0.1:3082;
keepalive 32;
}
# =============================================================================
# ERP SUITE
# =============================================================================
upstream erp_core_frontend {
server 127.0.0.1:3010;
keepalive 32;
}
upstream erp_core_backend {
server 127.0.0.1:3011;
keepalive 32;
}
upstream erp_construccion_frontend {
server 127.0.0.1:3020;
keepalive 32;
}
upstream erp_construccion_backend {
server 127.0.0.1:3021;
keepalive 32;
}
upstream erp_vidrio_frontend {
server 127.0.0.1:3030;
keepalive 32;
}
upstream erp_vidrio_backend {
server 127.0.0.1:3031;
keepalive 32;
}
upstream erp_mecanicas_frontend {
server 127.0.0.1:3040;
keepalive 32;
}
upstream erp_mecanicas_backend {
server 127.0.0.1:3041;
keepalive 32;
}
upstream erp_retail_frontend {
server 127.0.0.1:3050;
keepalive 32;
}
upstream erp_retail_backend {
server 127.0.0.1:3051;
keepalive 32;
}
upstream erp_clinicas_frontend {
server 127.0.0.1:3060;
keepalive 32;
}
upstream erp_clinicas_backend {
server 127.0.0.1:3061;
keepalive 32;
}
upstream erp_pos_frontend {
server 127.0.0.1:3070;
keepalive 32;
}
upstream erp_pos_backend {
server 127.0.0.1:3071;
keepalive 32;
}
# =============================================================================
# PLATFORM MARKETING CONTENT
# =============================================================================
upstream pmc_frontend {
server 127.0.0.1:3110;
keepalive 32;
}
upstream pmc_backend {
server 127.0.0.1:3111;
keepalive 32;
}
# =============================================================================
# BETTING ANALYTICS (RESERVADO)
# =============================================================================
upstream betting_frontend {
server 127.0.0.1:3090;
keepalive 32;
}
upstream betting_backend {
server 127.0.0.1:3091;
keepalive 32;
}
# =============================================================================
# INMOBILIARIA ANALYTICS (RESERVADO)
# =============================================================================
upstream inmobiliaria_frontend {
server 127.0.0.1:3100;
keepalive 32;
}
upstream inmobiliaria_backend {
server 127.0.0.1:3101;
keepalive 32;
}
4.3 Virtual Host Template
Archivo: /etc/nginx/conf.d/trading.conf
# =============================================================================
# TRADING PLATFORM - trading.isem.dev
# =============================================================================
# HTTP -> HTTPS redirect
server {
listen 80;
server_name trading.isem.dev api.trading.isem.dev;
return 301 https://$server_name$request_uri;
}
# Frontend
server {
listen 443 ssl http2;
server_name trading.isem.dev;
# SSL Configuration
ssl_certificate /etc/letsencrypt/live/isem.dev/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/isem.dev/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
# Security Headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';" always;
# Frontend proxy
location / {
proxy_pass http://trading_frontend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
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_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
# Static assets caching
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
proxy_pass http://trading_frontend;
expires 1y;
add_header Cache-Control "public, immutable";
}
# WebSocket
location /ws {
proxy_pass http://trading_websocket;
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;
}
}
# Backend API
server {
listen 443 ssl http2;
server_name api.trading.isem.dev;
ssl_certificate /etc/letsencrypt/live/isem.dev/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/isem.dev/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
# Rate limiting
limit_req zone=api_limit burst=20 nodelay;
limit_conn conn_limit 10;
# API proxy
location / {
proxy_pass http://trading_backend;
proxy_http_version 1.1;
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_set_header X-Forwarded-Proto $scheme;
# CORS headers
add_header Access-Control-Allow-Origin $http_origin always;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
add_header Access-Control-Allow-Headers "Authorization, Content-Type, X-Requested-With" always;
add_header Access-Control-Allow-Credentials "true" always;
if ($request_method = OPTIONS) {
return 204;
}
}
# Health check endpoint
location /health {
proxy_pass http://trading_backend/health;
access_log off;
}
}
5. Despliegue con PM2 (Gamilit)
IMPORTANTE: Gamilit NO usa Jenkins ni Docker. Usa PM2 como gestor de procesos.
5.1 Configuración PM2 (74.208.126.102)
Archivo: ecosystem.config.js
module.exports = {
apps: [
{
name: 'gamilit-backend',
cwd: './apps/backend',
script: 'dist/main.js',
instances: 2, // Cluster mode con 2 instancias
exec_mode: 'cluster',
env_production: {
NODE_ENV: 'production',
PORT: 3006,
},
max_memory_restart: '1G',
error_file: '../../logs/backend-error.log',
out_file: '../../logs/backend-out.log',
},
{
name: 'gamilit-frontend',
cwd: './apps/frontend',
script: 'npx',
args: 'vite preview --port 3005 --host 0.0.0.0',
instances: 1,
exec_mode: 'fork',
env_production: {
NODE_ENV: 'production',
},
max_memory_restart: '512M',
},
],
};
5.2 Comandos de Despliegue Gamilit
# Conectar al servidor
ssh isem@74.208.126.102
# Ir al directorio del proyecto
cd /home/isem/workspace/workspace-gamilit/gamilit/projects/gamilit
# Pull cambios
git pull origin main
# Instalar dependencias
npm install
# Build backend y frontend
npm run build:all
# Reiniciar con PM2
pm2 reload ecosystem.config.js --env production
# Guardar configuración
pm2 save
# Verificar estado
pm2 status
pm2 logs
5.3 Script de Despliegue Automatizado
Archivo: apps/devops/scripts/deploy.sh
./deploy.sh --env prod # Despliegue completo
./deploy.sh --env prod --skip-db # Sin reiniciar BD
./deploy.sh --env prod --dry-run # Simular despliegue
6. Jenkins Pipeline (Solo 72.60.226.4)
NOTA: Esta sección solo aplica para proyectos en el servidor 72.60.226.4. Gamilit usa PM2 (ver sección 5).
6.1 Estructura Jenkins
/var/jenkins_home/
├── jobs/
│ ├── trading-platform/
│ │ ├── backend/
│ │ └── frontend/
│ ├── erp-suite/
│ │ ├── erp-core/
│ │ ├── construccion/
│ │ ├── vidrio-templado/
│ │ └── ...
│ └── pmc/
│ ├── backend/
│ └── frontend/
├── shared-libraries/
│ └── vars/
│ ├── deployNode.groovy
│ ├── deployDocker.groovy
│ └── notifySlack.groovy
└── credentials/
├── ssh-keys/
└── docker-registry/
6.2 Jenkinsfile Template
Archivo: jenkins/Jenkinsfile
pipeline {
agent any
environment {
PROJECT_NAME = 'trading-platform'
DOCKER_REGISTRY = '72.60.226.4:5000'
DEPLOY_SERVER = '72.60.226.4'
DEPLOY_USER = 'deploy'
// Credenciales
DOCKER_CREDENTIALS = credentials('docker-registry')
SSH_KEY = credentials('deploy-ssh-key')
// Versión
VERSION = "${env.BUILD_NUMBER}"
GIT_COMMIT_SHORT = sh(script: "git rev-parse --short HEAD", returnStdout: true).trim()
}
options {
timeout(time: 30, unit: 'MINUTES')
disableConcurrentBuilds()
buildDiscarder(logRotator(numToKeepStr: '10'))
}
stages {
stage('Checkout') {
steps {
checkout scm
script {
env.GIT_BRANCH = sh(script: 'git rev-parse --abbrev-ref HEAD', returnStdout: true).trim()
}
}
}
stage('Install Dependencies') {
parallel {
stage('Backend') {
steps {
dir('apps/backend') {
sh 'npm ci'
}
}
}
stage('Frontend') {
steps {
dir('apps/frontend') {
sh 'npm ci'
}
}
}
}
}
stage('Lint & Test') {
parallel {
stage('Backend Lint') {
steps {
dir('apps/backend') {
sh 'npm run lint'
}
}
}
stage('Backend Test') {
steps {
dir('apps/backend') {
sh 'npm run test'
}
}
}
stage('Frontend Lint') {
steps {
dir('apps/frontend') {
sh 'npm run lint'
}
}
}
stage('Frontend Test') {
steps {
dir('apps/frontend') {
sh 'npm run test'
}
}
}
}
}
stage('Build') {
parallel {
stage('Build Backend') {
steps {
dir('apps/backend') {
sh 'npm run build'
}
}
}
stage('Build Frontend') {
steps {
dir('apps/frontend') {
sh 'npm run build'
}
}
}
}
}
stage('Docker Build & Push') {
when {
anyOf {
branch 'main'
branch 'develop'
}
}
parallel {
stage('Backend Image') {
steps {
dir('apps/backend') {
script {
def image = docker.build("${DOCKER_REGISTRY}/${PROJECT_NAME}-backend:${VERSION}")
docker.withRegistry("https://${DOCKER_REGISTRY}", 'docker-registry') {
image.push()
image.push('latest')
}
}
}
}
}
stage('Frontend Image') {
steps {
dir('apps/frontend') {
script {
def image = docker.build("${DOCKER_REGISTRY}/${PROJECT_NAME}-frontend:${VERSION}")
docker.withRegistry("https://${DOCKER_REGISTRY}", 'docker-registry') {
image.push()
image.push('latest')
}
}
}
}
}
}
}
stage('Deploy to Staging') {
when {
branch 'develop'
}
steps {
script {
deployToServer('staging')
}
}
}
stage('Deploy to Production') {
when {
branch 'main'
}
steps {
input message: 'Deploy to Production?', ok: 'Deploy'
script {
deployToServer('production')
}
}
}
stage('Health Check') {
steps {
script {
def healthUrl = env.GIT_BRANCH == 'main'
? "https://api.trading.isem.dev/health"
: "https://staging.api.trading.isem.dev/health"
retry(3) {
sleep(time: 10, unit: 'SECONDS')
sh "curl -f ${healthUrl} || exit 1"
}
}
}
}
}
post {
success {
slackSend(
color: 'good',
message: "✅ ${PROJECT_NAME} deployed successfully! Build #${BUILD_NUMBER}"
)
}
failure {
slackSend(
color: 'danger',
message: "❌ ${PROJECT_NAME} deployment failed! Build #${BUILD_NUMBER}"
)
}
always {
cleanWs()
}
}
}
def deployToServer(String environment) {
def composeFile = environment == 'production'
? 'docker/docker-compose.prod.yml'
: 'docker/docker-compose.staging.yml'
sshagent(['deploy-ssh-key']) {
sh """
ssh -o StrictHostKeyChecking=no ${DEPLOY_USER}@${DEPLOY_SERVER} '
cd /opt/apps/${PROJECT_NAME}
docker-compose -f ${composeFile} pull
docker-compose -f ${composeFile} up -d
docker system prune -f
'
"""
}
}
6.3 Pipeline para ERP-Suite (Multi-Vertical)
Archivo: erp-suite/jenkins/Jenkinsfile
pipeline {
agent any
parameters {
choice(
name: 'VERTICAL',
choices: ['erp-core', 'construccion', 'vidrio-templado', 'mecanicas-diesel', 'retail', 'clinicas', 'pos-micro', 'all'],
description: 'Select vertical to deploy'
)
choice(
name: 'ENVIRONMENT',
choices: ['staging', 'production'],
description: 'Target environment'
)
}
environment {
DOCKER_REGISTRY = '72.60.226.4:5000'
DEPLOY_SERVER = '72.60.226.4'
}
stages {
stage('Determine Verticals') {
steps {
script {
if (params.VERTICAL == 'all') {
env.VERTICALS = 'erp-core,construccion,vidrio-templado,mecanicas-diesel,retail,clinicas,pos-micro'
} else {
env.VERTICALS = params.VERTICAL
}
}
}
}
stage('Build & Deploy Verticals') {
steps {
script {
def verticals = env.VERTICALS.split(',')
def parallelStages = [:]
verticals.each { vertical ->
parallelStages[vertical] = {
stage("Build ${vertical}") {
dir("apps/${getVerticalPath(vertical)}") {
sh 'npm ci && npm run build'
}
}
stage("Docker ${vertical}") {
dir("apps/${getVerticalPath(vertical)}") {
def backendImage = docker.build("${DOCKER_REGISTRY}/erp-${vertical}-backend:${BUILD_NUMBER}")
def frontendImage = docker.build("${DOCKER_REGISTRY}/erp-${vertical}-frontend:${BUILD_NUMBER}")
docker.withRegistry("https://${DOCKER_REGISTRY}", 'docker-registry') {
backendImage.push()
frontendImage.push()
}
}
}
}
}
parallel parallelStages
}
}
}
stage('Deploy') {
steps {
script {
def verticals = env.VERTICALS.split(',')
verticals.each { vertical ->
deployVertical(vertical, params.ENVIRONMENT)
}
}
}
}
}
}
def getVerticalPath(String vertical) {
if (vertical == 'erp-core') return 'erp-core'
if (vertical == 'pos-micro') return 'products/pos-micro'
return "verticales/${vertical}"
}
def deployVertical(String vertical, String environment) {
def ports = getVerticalPorts(vertical)
sshagent(['deploy-ssh-key']) {
sh """
ssh deploy@${DEPLOY_SERVER} '
cd /opt/apps/erp-suite/${vertical}
docker-compose -f docker-compose.${environment}.yml pull
docker-compose -f docker-compose.${environment}.yml up -d
'
"""
}
}
def getVerticalPorts(String vertical) {
def portMap = [
'erp-core': [fe: 3010, be: 3011],
'construccion': [fe: 3020, be: 3021],
'vidrio-templado': [fe: 3030, be: 3031],
'mecanicas-diesel': [fe: 3040, be: 3041],
'retail': [fe: 3050, be: 3051],
'clinicas': [fe: 3060, be: 3061],
'pos-micro': [fe: 3070, be: 3071]
]
return portMap[vertical]
}
7. Variables de Entorno
7.1 Estructura por Ambiente
.env.development # Desarrollo local
.env.staging # Ambiente de pruebas
.env.production # Producción
.env.example # Template con placeholders
7.2 Template Variables Backend
Archivo: .env.production.template
# =============================================================================
# [PROJECT_NAME] Backend - Production Environment
# =============================================================================
# Application
NODE_ENV=production
PORT=${BACKEND_PORT}
API_PREFIX=api
API_VERSION=v1
# Server
SERVER_URL=https://api.${SUBDOMAIN}.isem.dev
FRONTEND_URL=https://${SUBDOMAIN}.isem.dev
# Database
DB_HOST=${DB_HOST:-localhost}
DB_PORT=${DB_PORT:-5432}
DB_NAME=${DB_NAME}
DB_USER=${DB_USER}
DB_PASSWORD=${DB_PASSWORD}
DB_SSL=true
DB_POOL_MAX=20
# Redis
REDIS_HOST=${REDIS_HOST:-localhost}
REDIS_PORT=${REDIS_PORT:-6379}
REDIS_PASSWORD=${REDIS_PASSWORD}
# JWT (CHANGE IN PRODUCTION!)
JWT_SECRET=${JWT_SECRET}
JWT_EXPIRES_IN=15m
JWT_REFRESH_SECRET=${JWT_REFRESH_SECRET}
JWT_REFRESH_EXPIRES_IN=7d
# CORS
CORS_ORIGIN=https://${SUBDOMAIN}.isem.dev
# Security
ENABLE_SWAGGER=false
RATE_LIMIT_WINDOW_MS=60000
RATE_LIMIT_MAX=100
# Logging
LOG_LEVEL=warn
LOG_TO_FILE=true
LOG_FILE_PATH=/var/log/${PROJECT_NAME}/app.log
7.3 Template Variables Frontend
Archivo: .env.production.template
# =============================================================================
# [PROJECT_NAME] Frontend - Production Environment
# =============================================================================
# Application
VITE_APP_NAME=${PROJECT_NAME}
VITE_APP_VERSION=${VERSION}
VITE_APP_ENV=production
# API Configuration
VITE_API_URL=https://api.${SUBDOMAIN}.isem.dev
VITE_API_PREFIX=api/v1
VITE_API_TIMEOUT=30000
# WebSocket
VITE_WS_URL=wss://api.${SUBDOMAIN}.isem.dev/ws
# Features
VITE_ENABLE_DEBUG=false
VITE_ENABLE_ANALYTICS=true
VITE_LOG_LEVEL=error
# External Services
VITE_SENTRY_DSN=${SENTRY_DSN}
VITE_GOOGLE_ANALYTICS_ID=${GA_ID}
7.4 Matriz de Variables por Proyecto
| Variable | gamilit | trading | erp-core | pmc |
|---|---|---|---|---|
| BACKEND_PORT | 3006 | 3081 | 3011 | 3111 |
| FRONTEND_PORT | 3005 | 3080 | 3010 | 3110 |
| DB_NAME | gamilit_platform | orbiquant_platform | erp_generic | pmc_dev |
| DB_USER | gamilit_user | orbiquant_user | erp_admin | pmc_user |
| SUBDOMAIN | gamilit.com | trading.isem.dev | erp.isem.dev | pmc.isem.dev |
| SERVER | 74.208.126.102 | 72.60.226.4 | 72.60.226.4 | 72.60.226.4 |
8. Docker Compose Producción
Archivo: docker/docker-compose.prod.yml
version: '3.8'
services:
backend:
image: ${DOCKER_REGISTRY}/${PROJECT_NAME}-backend:${VERSION:-latest}
container_name: ${PROJECT_NAME}-backend
restart: unless-stopped
ports:
- "${BACKEND_PORT}:${BACKEND_PORT}"
environment:
- NODE_ENV=production
env_file:
- ../apps/backend/.env.production
volumes:
- backend-logs:/var/log/${PROJECT_NAME}
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:${BACKEND_PORT}/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
networks:
- app-network
deploy:
resources:
limits:
cpus: '1'
memory: 512M
reservations:
cpus: '0.5'
memory: 256M
frontend:
image: ${DOCKER_REGISTRY}/${PROJECT_NAME}-frontend:${VERSION:-latest}
container_name: ${PROJECT_NAME}-frontend
restart: unless-stopped
ports:
- "${FRONTEND_PORT}:80"
depends_on:
- backend
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:80"]
interval: 30s
timeout: 10s
retries: 3
networks:
- app-network
deploy:
resources:
limits:
cpus: '0.5'
memory: 128M
volumes:
backend-logs:
networks:
app-network:
external: true
name: isem-network
9. Scripts de Despliegue
9.1 Script de Deploy Automatizado
Archivo: scripts/deploy.sh
#!/bin/bash
# =============================================================================
# Deploy Script - Multi-Project
# =============================================================================
set -e
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# Configuration
PROJECT_NAME="${PROJECT_NAME:-}"
ENVIRONMENT="${ENVIRONMENT:-production}"
VERSION="${VERSION:-latest}"
DEPLOY_SERVER="${DEPLOY_SERVER:-72.60.226.4}"
DOCKER_REGISTRY="${DOCKER_REGISTRY:-72.60.226.4:5000}"
# Functions
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
check_prerequisites() {
log_info "Checking prerequisites..."
command -v docker >/dev/null 2>&1 || { log_error "Docker not installed"; exit 1; }
command -v ssh >/dev/null 2>&1 || { log_error "SSH not available"; exit 1; }
if [ -z "$PROJECT_NAME" ]; then
log_error "PROJECT_NAME not set"
exit 1
fi
}
build_images() {
log_info "Building Docker images..."
# Backend
docker build -t ${DOCKER_REGISTRY}/${PROJECT_NAME}-backend:${VERSION} \
-f apps/backend/Dockerfile apps/backend/
# Frontend
docker build -t ${DOCKER_REGISTRY}/${PROJECT_NAME}-frontend:${VERSION} \
-f apps/frontend/Dockerfile apps/frontend/
}
push_images() {
log_info "Pushing images to registry..."
docker push ${DOCKER_REGISTRY}/${PROJECT_NAME}-backend:${VERSION}
docker push ${DOCKER_REGISTRY}/${PROJECT_NAME}-frontend:${VERSION}
# Tag as latest
docker tag ${DOCKER_REGISTRY}/${PROJECT_NAME}-backend:${VERSION} \
${DOCKER_REGISTRY}/${PROJECT_NAME}-backend:latest
docker tag ${DOCKER_REGISTRY}/${PROJECT_NAME}-frontend:${VERSION} \
${DOCKER_REGISTRY}/${PROJECT_NAME}-frontend:latest
docker push ${DOCKER_REGISTRY}/${PROJECT_NAME}-backend:latest
docker push ${DOCKER_REGISTRY}/${PROJECT_NAME}-frontend:latest
}
deploy_to_server() {
log_info "Deploying to ${DEPLOY_SERVER}..."
ssh deploy@${DEPLOY_SERVER} << EOF
cd /opt/apps/${PROJECT_NAME}
# Pull latest images
docker-compose -f docker-compose.${ENVIRONMENT}.yml pull
# Stop current containers
docker-compose -f docker-compose.${ENVIRONMENT}.yml down
# Start new containers
docker-compose -f docker-compose.${ENVIRONMENT}.yml up -d
# Cleanup
docker system prune -f
# Health check
sleep 10
curl -f http://localhost:${BACKEND_PORT}/health || exit 1
EOF
}
rollback() {
log_warn "Rolling back to previous version..."
ssh deploy@${DEPLOY_SERVER} << EOF
cd /opt/apps/${PROJECT_NAME}
docker-compose -f docker-compose.${ENVIRONMENT}.yml down
# Use previous tag
sed -i 's/:latest/:previous/g' docker-compose.${ENVIRONMENT}.yml
docker-compose -f docker-compose.${ENVIRONMENT}.yml up -d
EOF
}
# Main
main() {
check_prerequisites
case "$1" in
build)
build_images
;;
push)
push_images
;;
deploy)
deploy_to_server
;;
full)
build_images
push_images
deploy_to_server
;;
rollback)
rollback
;;
*)
echo "Usage: $0 {build|push|deploy|full|rollback}"
exit 1
;;
esac
}
main "$@"
9.2 Script de Configuración de Nginx
Archivo: scripts/setup-nginx.sh
#!/bin/bash
# =============================================================================
# Setup Nginx for Multi-Project
# =============================================================================
set -e
NGINX_CONF_DIR="/etc/nginx"
PROJECTS_CONF_DIR="${NGINX_CONF_DIR}/conf.d"
UPSTREAMS_DIR="${NGINX_CONF_DIR}/upstreams"
# Project configurations
declare -A PROJECTS=(
["trading"]="3080:3081:3082"
["erp-core"]="3010:3011"
["construccion"]="3020:3021"
["vidrio"]="3030:3031"
["mecanicas"]="3040:3041"
["retail"]="3050:3051"
["clinicas"]="3060:3061"
["pos"]="3070:3071"
["pmc"]="3110:3111"
["betting"]="3090:3091"
["inmobiliaria"]="3100:3101"
)
create_upstream() {
local name=$1
local ports=$2
IFS=':' read -ra PORT_ARRAY <<< "$ports"
local fe_port=${PORT_ARRAY[0]}
local be_port=${PORT_ARRAY[1]}
local ws_port=${PORT_ARRAY[2]:-}
cat > "${UPSTREAMS_DIR}/${name}.conf" << EOF
upstream ${name}_frontend {
server 127.0.0.1:${fe_port};
keepalive 32;
}
upstream ${name}_backend {
server 127.0.0.1:${be_port};
keepalive 32;
}
EOF
if [ -n "$ws_port" ]; then
cat >> "${UPSTREAMS_DIR}/${name}.conf" << EOF
upstream ${name}_websocket {
server 127.0.0.1:${ws_port};
keepalive 32;
}
EOF
fi
}
create_vhost() {
local name=$1
local subdomain="${name}.isem.dev"
cat > "${PROJECTS_CONF_DIR}/${name}.conf" << EOF
server {
listen 80;
server_name ${subdomain} api.${subdomain};
return 301 https://\$server_name\$request_uri;
}
server {
listen 443 ssl http2;
server_name ${subdomain};
ssl_certificate /etc/letsencrypt/live/isem.dev/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/isem.dev/privkey.pem;
location / {
proxy_pass http://${name}_frontend;
proxy_http_version 1.1;
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_set_header X-Forwarded-Proto \$scheme;
}
}
server {
listen 443 ssl http2;
server_name api.${subdomain};
ssl_certificate /etc/letsencrypt/live/isem.dev/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/isem.dev/privkey.pem;
location / {
proxy_pass http://${name}_backend;
proxy_http_version 1.1;
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_set_header X-Forwarded-Proto \$scheme;
}
}
EOF
}
# Main
mkdir -p "${UPSTREAMS_DIR}"
for project in "${!PROJECTS[@]}"; do
echo "Configuring ${project}..."
create_upstream "$project" "${PROJECTS[$project]}"
create_vhost "$project"
done
# Test configuration
nginx -t
# Reload
systemctl reload nginx
echo "Nginx configured successfully!"
10. Checklist de Implementación
Fase 1: Infraestructura Base (Servidor 72.60.226.4)
- Instalar Docker y Docker Compose
- Configurar Docker Registry privado (puerto 5000)
- Instalar y configurar Jenkins
- Instalar Nginx
- Obtener certificados SSL (Let's Encrypt wildcard para *.isem.dev)
- Configurar red Docker compartida (
isem-network) - Crear directorios base
/opt/apps/{proyecto}
Fase 2: Separación de Repositorios
- Crear repositorios en Gitea para cada proyecto
- Migrar código de monorepo a repos individuales
- Configurar webhooks de Gitea a Jenkins
- Actualizar referencias de git remotes
Fase 3: CI/CD Jenkins
- Crear jobs de Jenkins por proyecto
- Configurar credenciales (SSH, Docker Registry)
- Configurar shared libraries
- Probar pipelines en ambiente staging
Fase 4: Nginx y DNS
- Configurar upstreams por proyecto
- Crear virtual hosts
- Configurar DNS para subdominios
- Verificar SSL/TLS
Fase 5: Despliegue
- Desplegar trading-platform
- Desplegar erp-suite (por verticales)
- Desplegar platform-marketing-content
- Verificar health checks
- Configurar monitoreo
11. Referencias
- Inventario de Puertos:
/home/isem/workspace/core/orchestration/inventarios/DEVENV-PORTS-INVENTORY.yml - Inventario de Despliegue:
/home/isem/workspace/core/orchestration/inventarios/DEPLOYMENT-INVENTORY.yml - Gitea (72.60.226.4): http://72.60.226.4:3000
Gamilit (PM2)
- Servidor: 74.208.126.102
- Repositorio: https://github.com/rckrdmrd/gamilit-workspace.git
- ecosystem.config.js:
/home/isem/workspace/projects/gamilit/ecosystem.config.js - Deploy Script:
/home/isem/workspace/projects/gamilit/apps/devops/scripts/deploy.sh - Documentación:
/home/isem/workspace/projects/gamilit/README.md
Documento generado por DevEnv Agent - 2025-12-12 Versión: 1.0.0