Actualizaciones de archivo.

Modificaciones pequeñas
This commit is contained in:
Mateo Saldain 2025-08-16 03:25:24 +00:00
parent f9bf5f4824
commit 97db600b1f
9 changed files with 124 additions and 134 deletions

View File

@ -17,12 +17,15 @@ services:
- "81:81" # UI de administración NPM - "81:81" # UI de administración NPM
- "443:443" # HTTPS público - "443:443" # HTTPS público
volumes: volumes:
- npm_data:/data # config + DB (SQLite) - dev-npm_data:/data # config + DB (SQLite)
- npm_letsencrypt:/etc/letsencrypt - dev-npm_letsencrypt:/etc/letsencrypt
networks: networks:
- suitecoffee-net - suitecoffee-net
suitecoffee-app: suitecoffee-app:
image: node:20-bookworm
container_name: suitecoffee-app container_name: suitecoffee-app
depends_on: depends_on:
suitecoffee-db: suitecoffee-db:
@ -31,9 +34,11 @@ services:
condition: service_healthy condition: service_healthy
ports: ports:
- 3000:3000 - 3000:3000
working_dir: /app
user: "${UID:-1000}:${GID:-1000}"
volumes: volumes:
- ./services/app:/app - ./services/app:/app:rw
- /app/node_modules - ./node_modules:/app/node_modules
env_file: env_file:
- ./services/app/.env.development - ./services/app/.env.development
environment: environment:
@ -50,16 +55,22 @@ services:
networks: networks:
- suitecoffee-net - suitecoffee-net
suitecoffee-auth: suitecoffee-auth:
image: node:20-bookworm
container_name: suitecoffee-auth container_name: suitecoffee-auth
depends_on: depends_on:
suitecoffee-db: suitecoffee-db:
condition: service_healthy condition: service_healthy
ports: ports:
- 4000:4000 - 4000:4000
working_dir: /app
user: "${UID:-1000}:${GID:-1000}"
volumes: volumes:
- ./services/auth:/app - ./services/app:/app:rw
- /app/node_modules - ./node_modules:/app/node_modules
env_file: env_file:
- ./services/auth/.env.development - ./services/auth/.env.development
environment: environment:
@ -83,9 +94,9 @@ services:
POSTGRES_USER: ${DB_USER} POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASS} POSTGRES_PASSWORD: ${DB_PASS}
ports: ports:
- 54321:5432 - ${DB_LOCAL_PORT}:${DB_DOCKER_PORT}
volumes: volumes:
- suitecoffee-data:/var/lib/postgresql/data - dev-suitecoffee-data:/var/lib/postgresql/data
restart: unless-stopped restart: unless-stopped
healthcheck: healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d ${DB_NAME}"] test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d ${DB_NAME}"]
@ -104,9 +115,9 @@ services:
POSTGRES_USER: ${TENANTS_DB_USER} POSTGRES_USER: ${TENANTS_DB_USER}
POSTGRES_PASSWORD: ${TENANTS_DB_PASS} POSTGRES_PASSWORD: ${TENANTS_DB_PASS}
volumes: volumes:
- tenants-data:/var/lib/postgresql/data - dev-tenants-data:/var/lib/postgresql/data
ports: ports:
- 54322:5432 - ${TENANTS_DB_LOCAL_PORT}:${TENANTS_DB_DOCKER_PORT}
restart: unless-stopped restart: unless-stopped
healthcheck: healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${TENANTS_DB_USER} -d ${TENANTS_DB_NAME}"] test: ["CMD-SHELL", "pg_isready -U ${TENANTS_DB_USER} -d ${TENANTS_DB_NAME}"]
@ -129,8 +140,8 @@ services:
ports: ports:
- 8978:8978 - 8978:8978
volumes: volumes:
- dbeaver_logs:/opt/cloudbeaver/logs - dev-dbeaver_logs:/opt/cloudbeaver/logs
- dbeaver_workspace:/opt/cloudbeaver/workspace - dev-dbeaver_workspace:/opt/cloudbeaver/workspace
networks: networks:
- suitecoffee-net - suitecoffee-net
@ -149,12 +160,12 @@ services:
# - suitecoffee-net # - suitecoffee-net
volumes: volumes:
tenants-data: dev-tenants-data:
suitecoffee-data: dev-suitecoffee-data:
npm_data: dev-npm_data:
npm_letsencrypt: dev-npm_letsencrypt:
dbeaver_logs: dev-dbeaver_logs:
dbeaver_workspace: dev-dbeaver_workspace:
networks: networks:
suitecoffee-net: suitecoffee-net:

View File

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

View File

@ -1,13 +0,0 @@
{
"watch": ["src"],
"ext": "js,json",
"ignore": [
"node_modules/**/node_modules",
"node_modules/",
".git"
],
"env": {
"NODE_ENV": "development"
},
"exec": "node ./src/index.js"
}

View File

@ -9,7 +9,7 @@
"version": "1.0.0", "version": "1.0.0",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"chalk": "^5.3.0", "chalk": "^5.5.0",
"cors": "^2.8.5", "cors": "^2.8.5",
"dotenv": "^17.2.1", "dotenv": "^17.2.1",
"express": "^5.1.0", "express": "^5.1.0",

View File

@ -1,10 +1,10 @@
{ {
"name": "app", "name": "suitecoffee_aplication_service",
"version": "1.0.0", "version": "1.0.0",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"start": "NODE_ENV=production node ./src/index.js", "start": "NODE_ENV=production node ./src/index.js",
"dev": "npx nodemon", "dev": "NODE_ENV=development npx nodemon ./src/index.js",
"test": "NODE_ENV=stage node ./src/index.js" "test": "NODE_ENV=stage node ./src/index.js"
}, },
"author": "Mateo Saldain", "author": "Mateo Saldain",
@ -15,7 +15,7 @@
"nodemon": "^3.1.10" "nodemon": "^3.1.10"
}, },
"dependencies": { "dependencies": {
"chalk": "^5.3.0", "chalk": "^5.5.0",
"cors": "^2.8.5", "cors": "^2.8.5",
"dotenv": "^17.2.1", "dotenv": "^17.2.1",
"express": "^5.1.0", "express": "^5.1.0",

View File

@ -1,13 +0,0 @@
{
"watch": ["src"],
"ext": "js,json",
"ignore": [
"node_modules/**/node_modules",
"node_modules/",
".git"
],
"env": {
"NODE_ENV": "development"
},
"exec": "node ./src/index.js"
}

View File

@ -10,7 +10,7 @@
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"bcrypt": "^5.1.1", "bcrypt": "^5.1.1",
"chalk": "^5.3.0", "chalk": "^5.5.0",
"cors": "^2.8.5", "cors": "^2.8.5",
"dotenv": "^17.2.1", "dotenv": "^17.2.1",
"express": "^5.1.0", "express": "^5.1.0",

View File

@ -1,10 +1,10 @@
{ {
"name": "auth", "name": "suitecoffee_authentication_service",
"version": "1.0.0", "version": "1.0.0",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"start": "NODE_ENV=production node ./src/index.js", "start": "NODE_ENV=production node ./src/index.js",
"dev": "npx nodemon", "dev": "NODE_ENV=development npx nodemon ./src/index.js",
"test": "NODE_ENV=stage node ./src/index.js" "test": "NODE_ENV=stage node ./src/index.js"
}, },
"author": "Mateo Saldain", "author": "Mateo Saldain",
@ -16,7 +16,7 @@
}, },
"dependencies": { "dependencies": {
"bcrypt": "^5.1.1", "bcrypt": "^5.1.1",
"chalk": "^5.3.0", "chalk": "^5.5.0",
"cors": "^2.8.5", "cors": "^2.8.5",
"dotenv": "^17.2.1", "dotenv": "^17.2.1",
"express": "^5.1.0", "express": "^5.1.0",

View File

@ -1,10 +1,12 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="es"> <html lang="es">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>SuiteCoffee - Autenticación</title> <title>SuiteCoffee - Autenticación</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head> </head>
<body class="bg-light d-flex justify-content-center align-items-center vh-100"> <body class="bg-light d-flex justify-content-center align-items-center vh-100">
<div class="card shadow p-4" style="width: 100%; max-width: 400px;"> <div class="card shadow p-4" style="width: 100%; max-width: 400px;">
@ -51,101 +53,103 @@
<div class="text-center mt-3"> <div class="text-center mt-3">
<button class="btn btn-link btn-sm" id="toggle-mode">¿No tienes cuenta? Regístrate</button> <button class="btn btn-link btn-sm" id="toggle-mode">¿No tienes cuenta? Regístrate</button>
</div> </div>
<p>Hola!</p>
</div> </div>
<script> <script>
const form = document.getElementById('formulario'); const form = document.getElementById('formulario');
const mensaje = document.getElementById('mensaje'); const mensaje = document.getElementById('mensaje');
const toggleModeBtn = document.getElementById('toggle-mode'); const toggleModeBtn = document.getElementById('toggle-mode');
const registroExtra = document.getElementById('registro-extra'); const registroExtra = document.getElementById('registro-extra');
const formTitle = document.getElementById('form-title'); const formTitle = document.getElementById('form-title');
const btnSubmit = document.getElementById('btn-submit'); const btnSubmit = document.getElementById('btn-submit');
let modoRegistro = false; let modoRegistro = false;
toggleModeBtn.addEventListener('click', () => { toggleModeBtn.addEventListener('click', () => {
modoRegistro = !modoRegistro; modoRegistro = !modoRegistro;
registroExtra.style.display = modoRegistro ? 'block' : 'none'; registroExtra.style.display = modoRegistro ? 'block' : 'none';
formTitle.textContent = modoRegistro ? 'Registrar Cuenta' : 'Iniciar Sesión'; formTitle.textContent = modoRegistro ? 'Registrar Cuenta' : 'Iniciar Sesión';
btnSubmit.textContent = modoRegistro ? 'Registrarse' : 'Entrar'; btnSubmit.textContent = modoRegistro ? 'Registrarse' : 'Entrar';
toggleModeBtn.textContent = modoRegistro ? '¿Ya tienes cuenta? Inicia sesión' : '¿No tienes cuenta? Regístrate'; toggleModeBtn.textContent = modoRegistro ? '¿Ya tienes cuenta? Inicia sesión' : '¿No tienes cuenta? Regístrate';
if (modoRegistro) { if (modoRegistro) {
cargarPlanes(); // ✅ ahora sí se ejecutará correctamente cargarPlanes(); // ✅ ahora sí se ejecutará correctamente
} }
}); });
form.addEventListener('submit', async (e) => { form.addEventListener('submit', async (e) => {
e.preventDefault(); e.preventDefault();
mensaje.classList.add('d-none'); mensaje.classList.add('d-none');
const datos = { const datos = {
correo: document.getElementById('correo').value, correo: document.getElementById('correo').value,
clave_acceso: document.getElementById('clave').value clave_acceso: document.getElementById('clave').value
}; };
if (modoRegistro) { if (modoRegistro) {
Object.assign(datos, { Object.assign(datos, {
nombre_empresa: document.getElementById('nombre_empresa').value, nombre_empresa: document.getElementById('nombre_empresa').value,
rut: document.getElementById('rut').value, rut: document.getElementById('rut').value,
telefono: document.getElementById('telefono').value, telefono: document.getElementById('telefono').value,
direccion: document.getElementById('direccion').value, direccion: document.getElementById('direccion').value,
logo: document.getElementById('logo').value, logo: document.getElementById('logo').value,
plan_id: document.getElementById('plan_id').value plan_id: document.getElementById('plan_id').value
}); });
}
try {
const url = modoRegistro ? '/api/registro' : '/api/login';
const res = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(datos)
});
const resultado = await res.json();
if (!res.ok) {
throw new Error(resultado.error || 'Error inesperado');
} }
mensaje.className = 'alert alert-success'; try {
mensaje.textContent = resultado.message || (modoRegistro ? 'Registro exitoso' : 'Inicio exitoso'); const url = modoRegistro ? '/api/registro' : '/api/login';
mensaje.classList.remove('d-none'); const res = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(datos)
});
if (!modoRegistro) { const resultado = await res.json();
// Redirigir a dashboard, por ejemplo
// window.location.href = `/dashboard?tenant=${resultado.uuid}`; if (!res.ok) {
throw new Error(resultado.error || 'Error inesperado');
}
mensaje.className = 'alert alert-success';
mensaje.textContent = resultado.message || (modoRegistro ? 'Registro exitoso' : 'Inicio exitoso');
mensaje.classList.remove('d-none');
if (!modoRegistro) {
// Redirigir a dashboard, por ejemplo
// window.location.href = `/dashboard?tenant=${resultado.uuid}`;
}
} catch (err) {
mensaje.className = 'alert alert-danger';
mensaje.textContent = err.message;
mensaje.classList.remove('d-none');
}
});
// ✅ Ahora la función está declarada correctamente
async function cargarPlanes() {
const select = document.getElementById('plan_id');
select.innerHTML = '<option value="">Cargando planes...</option>';
try {
const res = await fetch('/planes');
const planes = await res.json();
select.innerHTML = '<option value="">Seleccione un plan</option>';
planes.forEach(plan => {
const opt = document.createElement('option');
opt.value = plan.id;
opt.textContent = plan.nombre.charAt(0).toUpperCase() + plan.nombre.slice(1);
select.appendChild(opt);
});
} catch (err) {
select.innerHTML = '<option value="">Error al cargar planes</option>';
console.error('Error cargando planes:', err);
} }
} catch (err) {
mensaje.className = 'alert alert-danger';
mensaje.textContent = err.message;
mensaje.classList.remove('d-none');
} }
});
// ✅ Ahora la función está declarada correctamente
async function cargarPlanes() {
const select = document.getElementById('plan_id');
select.innerHTML = '<option value="">Cargando planes...</option>';
try {
const res = await fetch('/planes');
const planes = await res.json();
select.innerHTML = '<option value="">Seleccione un plan</option>';
planes.forEach(plan => {
const opt = document.createElement('option');
opt.value = plan.id;
opt.textContent = plan.nombre.charAt(0).toUpperCase() + plan.nombre.slice(1);
select.appendChild(opt);
});
} catch (err) {
select.innerHTML = '<option value="">Error al cargar planes</option>';
console.error('Error cargando planes:', err);
}
}
</script> </script>
</body> </body>
</html>
</html>