Ahora Suitecoffee puede correr dentro de contenedores docker. Mediante docker compose sin necesidad de exponer puertos, únicamente se expone un puerto 80 del servicio 'gateway' que utiliza una imagen nginx:alpine.

el comando para levantar el servicio con el entorno de desarrollo (utilizando las variables desarrollo + docker.compose.override.yml) es:

docker compose -f docker-compose.yml -f docker-compose.override.yml --env-file .env.development up -d

Para desactivarlo:

docker compose -f docker-compose.yml -f docker-compose.override.yml --env-file .env.development down
This commit is contained in:
Mateo Saldain 2025-08-15 14:30:35 +00:00
parent abaf43b8d6
commit 3d3ef3f002
12 changed files with 1676 additions and 44 deletions

View File

@ -2,73 +2,117 @@
# Docker Comose para entorno de desarrollo o development.
services:
gateway:
image: nginx:alpine
container_name: suitecoffee-gateway
depends_on:
suitecoffee-app:
condition: service_healthy
suitecoffee-auth:
condition: service_healthy
ports:
- "80:80" # único puerto público (agregá 443 si después sumás TLS)
volumes:
- ./gateway/nginx.conf:/etc/nginx/nginx.conf:ro
networks:
- suitecoffee-net
restart: unless-stopped
suitecoffee-app:
container_name: suitecoffee-app
depends_on:
- suitecoffee-db
- suitecoffee-tenants
suitecoffee-db:
condition: service_healthy
suitecoffee-tenants:
condition: service_healthy
build:
context: ./services/app
dockerfile: Dockerfile.development
volumes:
- ./services/app:/app
ports:
- ${APP_LOCAL_PORT}:${APP_DOCKER_PORT}
# env_file:
# -
env_file:
- ./services/app/.env.development
environment:
- NODE_ENV=${NODE_ENV}
command: npm run dev
healthcheck:
# IMPORTANTE: asegurate de tener curl instalado en la imagen de app (ver nota abajo)
test: ["CMD-SHELL", "curl -fsS http://localhost:${APP_DOCKER_PORT}/health || exit 1"]
interval: 10s
timeout: 3s
retries: 10
start_period: 20s
restart: unless-stopped
networks:
- suitecoffee-net
suitecoffee-auth:
container_name: suitecoffee-auth
depends_on:
- suitecoffee-auth-db
suitecoffee-db:
condition: service_healthy
build:
context: ./services/auth
dockerfile: Dockerfile.development
volumes:
- ./services/auth:/app
ports:
- ${AUTH_LOCAL_PORT}:${AUTH_DOCKER_PORT}
# env_file:
# -
env_file:
- ./services/auth/.env.development
environment:
- NODE_ENV=${NODE_ENV}
command: npm run dev
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "curl -fsS http://localhost:${AUTH_DOCKER_PORT:-4000}/health || exit 1"]
interval: 10s
timeout: 3s
retries: 10
start_period: 15s
networks:
- suitecoffee-net
suitecoffee-db:
image: postgres:16
container_name: suitecoffee-db
# env_file:
# -
environment:
POSTGRES_DB: ${DB_NAME}
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASS}
ports:
- ${DB_LOCAL_PORT}:${DB_DOCKER_PORT}
volumes:
- suitecoffee-data:/var/lib/postgresql/data
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d ${DB_NAME}"]
interval: 5s
timeout: 3s
retries: 20
start_period: 10s
networks:
- suitecoffee-net
suitecoffee-tenants:
suitecoffee-tenants:
image: postgres:16
container_name: suitecoffee-tenants
# env_file:
# -
environment:
POSTGRES_DB: ${TENANTS_DB_NAME}
POSTGRES_USER: ${TENANTS_DB_USER}
POSTGRES_PASSWORD: ${TENANTS_DB_PASS}
ports:
- ${TENANTS_DB_LOCAL_PORT}:${TENANTS_DB_DOCKER_PORT}
volumes:
- tenants-data:/var/lib/postgresql/data
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${TENANTS_DB_USER} -d ${TENANTS_DB_NAME}"]
interval: 5s
timeout: 3s
retries: 20
start_period: 10s
networks:
- suitecoffee-net
volumes:
tenants-data:
suitecoffee-data:
suitecoffee-data:
networks:
suitecoffee-net:
driver: bridge

49
gateway/nginx.conf Normal file
View File

@ -0,0 +1,49 @@
worker_processes 1;
events { worker_connections 1024; }
http {
# Logs básicos
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
# Ajustes útiles para proxys
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;
proxy_set_header Connection "";
# Soporte WebSocket (si lo usás en dev)
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
server_name _;
# Frontend / App principal (Next/React/Express, etc.)
# / -> app
location / {
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_pass http://suitecoffee-app:4000; # usa el puerto interno de app
}
# API de app (si preferís separar por path)
location /api/ {
proxy_pass http://suitecoffee-app:3000;
}
# Servicio de autenticación (por ejemplo /auth/*)
# location /auth/ {
# proxy_pass http://suitecoffee-auth:4000;
# }
# Opcional: servir estáticos si la app build genera /public
# location /static/ { alias /usr/share/nginx/html/static/; }
}
}

1482
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

5
package.json Normal file
View File

@ -0,0 +1,5 @@
{
"dependencies": {
"app": "file:services/app"
}
}

View File

@ -9,8 +9,9 @@ ARG PORT=3000
COPY package*.json ./
# Instala dependencias
RUN npm i express pg dotenv cors
RUN npm i --save-dev nodemon
RUN apt-get update
RUN npm i
RUN apt-get install -y curl && rm -rf /var/lib/apt/lists/*
# Copia el resto de la app
COPY . .

View File

@ -9,6 +9,7 @@
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"chalk": "^5.3.0",
"cors": "^2.8.5",
"dotenv": "^17.2.1",
"express": "^5.1.0",
@ -145,6 +146,18 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/chalk": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.5.0.tgz",
"integrity": "sha512-1tm8DTaJhPBG3bIkVeZt1iZM9GfSX2lzOeDVZH9R9ffRHpmHvxZ/QhgQH/aDTkswQVt+YHdXAdS/In/30OjCbg==",
"license": "MIT",
"engines": {
"node": "^12.17.0 || ^14.13 || >=16.0.0"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/chokidar": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",

View File

@ -19,7 +19,8 @@
"dotenv": "^17.2.1",
"express": "^5.1.0",
"express-ejs-layouts": "^2.5.1",
"pg": "^8.16.3"
"pg": "^8.16.3",
"chalk": "^5.3.0"
},
"keywords": [],
"description": ""

View File

@ -1,4 +1,5 @@
// app/src/index.js
import chalk from 'chalk'; // Colores!
import express from 'express';
import expressLayouts from 'express-ejs-layouts';
import cors from 'cors';
@ -17,13 +18,13 @@ import dotenv, { config } from 'dotenv';
try {
if (process.env.NODE_ENV === 'development') {
dotenv.config({ path: path.resolve(__dirname, '../.env.development' )});
console.log("Activando entorno de -> development");
console.log(`Activando entorno de ->${chalk.green(` DEVELOPMENT `)}`);
} else if (process.env.NODE_ENV === 'stage') {
dotenv.config({ path: path.resolve(__dirname, '../.env.test' )});
console.log("Activando entorno de -> testing");
console.log(`Activando entorno de ->->${chalk.yellow(` TESTING `)}`);
} else if (process.env.NODE_ENV === 'production') {
dotenv.config({ path: path.resolve(__dirname, '../.env' )});
console.log("Activando entorno de -> producción");
console.log(`Activando entorno de ->->${chalk.red(` PRODUCTION `)}`);
}
} catch (error) {
console.log("A ocurrido un error al seleccionar el entorno. \nError: " + error);
@ -51,16 +52,17 @@ async function verificarConexion() {
try {
const client = await pool.connect();
const res = await client.query('SELECT NOW() AS hora');
console.log('Conexión con la base de datos fue exitosa.');
console.log(`\nConexión con la base de datos ${chalk.green(process.env.DB_NAME)} fue exitosa.`);
console.log('Fecha y hora actual de la base de datos:', res.rows[0].hora);
client.release(); // liberar el cliente de nuevo al pool
} catch (error) {
console.error('Error al conectar con la base de datos al iniciar:', error.message);
console.error(`Troubleshooting:\n1. Compruebe que las bases de datos se iniciaron correctamente.\n2. Verifique las credenciales y puertos de acceso a la base de datos.\n3. Si está conectandose a una base de datos externa a localhost, verifique las reglas del firewal de entrada y salida de ambos dispositivos.`);
}
}
// === Servir páginas estáticas ===
app.use('/pages', express.static(path.join(__dirname, 'pages')));
@ -231,10 +233,21 @@ async function q(text, params) {
});
// Colores personalizados
let primaryColor = chalk.hex('#'+`${process.env.COL_PRI}`);
let secondaryColor = chalk.hex('#'+`${process.env.COL_SEC}`);
// let backgroundColor = chalk.hex('#'+`${process.env.COL_BG}`);
app.use(expressLayouts);
// Iniciar servidor
app.listen( process.env.PORT, () => {
console.log(`Servidor corriendo en http://localhost:${process.env.PORT}`);
console.log('Estableciendo conexión con la db...');
console.log(`Servidor de ${chalk.red('aplicación')} de ${secondaryColor('SuiteCoffee')} corriendo en ${chalk.yellow(`http://localhost:${process.env.PORT}\n`)}` );
console.log(chalk.grey(`Comprobando accesibilidad a la db ${chalk.green(process.env.DB_NAME)} del host ${chalk.white(`${process.env.DB_HOST}`)} ...`));
verificarConexion();
});
app.get("/health", async (req, res) => {
// Podés chequear DB aquí. 200 = healthy; 503 = not ready.
res.status(200).json({ status: "ok" });
});

View File

@ -3,20 +3,21 @@ FROM node:20.17
# Definir variables de entorno con valores predeterminados
ARG NODE_ENV=development
ARG PORT=3000
ARG PORT=4000
# Copia archivos de configuración primero para aprovechar el cache
COPY package*.json ./
# Instala dependencias
RUN npm i express pg dotenv cors
RUN npm i --save-dev nodemon
RUN apt-get update
RUN npm i
RUN apt-get install -y curl && rm -rf /var/lib/apt/lists/*
# Copia el resto de la app
COPY . .
# Expone el puerto
EXPOSE 3000
EXPOSE 4000
# Usa nodemon para hot reload si lo tenés
CMD ["npm", "run", "dev"]

View File

@ -10,6 +10,7 @@
"license": "ISC",
"dependencies": {
"bcrypt": "^5.1.1",
"chalk": "^5.3.0",
"cors": "^2.8.5",
"dotenv": "^17.2.1",
"express": "^5.1.0",
@ -236,6 +237,18 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/chalk": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.5.0.tgz",
"integrity": "sha512-1tm8DTaJhPBG3bIkVeZt1iZM9GfSX2lzOeDVZH9R9ffRHpmHvxZ/QhgQH/aDTkswQVt+YHdXAdS/In/30OjCbg==",
"license": "MIT",
"engines": {
"node": "^12.17.0 || ^14.13 || >=16.0.0"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/chokidar": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",

View File

@ -20,7 +20,8 @@
"express": "^5.1.0",
"express-ejs-layouts": "^2.5.1",
"pg": "^8.16.3",
"bcrypt": "^5.1.1"
"bcrypt": "^5.1.1",
"chalk": "^5.3.0"
},
"keywords": [],
"description": ""

View File

@ -1,4 +1,5 @@
// auth/src/index.js
import chalk from 'chalk'; // Colores!
import express from 'express';
import expressLayouts from 'express-ejs-layouts';
import cors from 'cors';
@ -18,13 +19,13 @@ import dotenv, { config } from 'dotenv';
try {
if (process.env.NODE_ENV === 'development') {
dotenv.config({ path: path.resolve(__dirname, '../.env.development' )});
console.log("Activando entorno de -> development");
console.log(`Activando entorno de ->${chalk.green(` DEVELOPMENT `)}`);
} else if (process.env.NODE_ENV === 'stage') {
dotenv.config({ path: path.resolve(__dirname, '../.env.test' )});
console.log("Activando entorno de -> testing");
console.log(`Activando entorno de ->->${chalk.yellow(` TESTING `)}`);
} else if (process.env.NODE_ENV === 'production') {
dotenv.config({ path: path.resolve(__dirname, '../.env' )});
console.log("Activando entorno de -> producción");
console.log(`Activando entorno de ->->${chalk.red(` PRODUCTION `)}`);
}
} catch (error) {
console.log("A ocurrido un error al seleccionar el entorno. \nError: " + error);
@ -52,11 +53,12 @@ async function verificarConexion() {
try {
const client = await pool.connect();
const res = await client.query('SELECT NOW() AS hora');
console.log('Conexión con la base de datos fue exitosa.');
console.log(`\nConexión con la base de datos ${chalk.green(process.env.DB_NAME)} fue exitosa.`);
console.log('Fecha y hora actual de la base de datos:', res.rows[0].hora);
client.release(); // liberar el cliente de nuevo al pool
} catch (error) {
console.error('Error al conectar con la base de datos al iniciar:', error.message);
console.error(`Troubleshooting:\n1. Compruebe que las bases de datos se iniciaron correctamente.\n2. Verifique las credenciales y puertos de acceso a la base de datos.\n3. Si está conectandose a una base de datos externa a localhost, verifique las reglas del firewal de entrada y salida de ambos dispositivos.`);
}
}
@ -180,14 +182,21 @@ app.post('/login', async (req, res) => {
});
// Colores personalizados
let primaryColor = chalk.hex('#'+`${process.env.COL_PRI}`);
let secondaryColor = chalk.hex('#'+`${process.env.COL_SEC}`);
// let backgroundColor = chalk.hex('#'+`${process.env.COL_BG}`);
app.use(expressLayouts);
// Iniciar servidor
app.listen( process.env.PORT, () => {
console.log(`Servidor corriendo en http://localhost:${process.env.PORT}`);
console.log('Estableciendo conexión con la db...');
console.log(`Servidor de ${chalk.yellow('autenticación')} de ${secondaryColor('SuiteCoffee')} corriendo en ${chalk.yellow(`http://localhost:${process.env.PORT}\n`)}` );
console.log(chalk.grey(`Comprobando accesibilidad a la db ${chalk.green(process.env.DB_NAME)} del host ${chalk.white(`${process.env.DB_HOST}`)} ...`));
verificarConexion();
});
app.get("/health", async (req, res) => {
// Podés chequear DB aquí. 200 = healthy; 503 = not ready.
res.status(200).json({ status: "ok" });
});