665 lines
23 KiB
HTML
665 lines
23 KiB
HTML
<!DOCTYPE html>
|
||
<html>
|
||
<head>
|
||
<title>README.md</title>
|
||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
|
||
|
||
<style>
|
||
/* https://github.com/microsoft/vscode/blob/master/extensions/markdown-language-features/media/markdown.css */
|
||
/*---------------------------------------------------------------------------------------------
|
||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||
*--------------------------------------------------------------------------------------------*/
|
||
|
||
body {
|
||
font-family: var(--vscode-markdown-font-family, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif);
|
||
font-size: var(--vscode-markdown-font-size, 14px);
|
||
padding: 0 26px;
|
||
line-height: var(--vscode-markdown-line-height, 22px);
|
||
word-wrap: break-word;
|
||
}
|
||
|
||
#code-csp-warning {
|
||
position: fixed;
|
||
top: 0;
|
||
right: 0;
|
||
color: white;
|
||
margin: 16px;
|
||
text-align: center;
|
||
font-size: 12px;
|
||
font-family: sans-serif;
|
||
background-color:#444444;
|
||
cursor: pointer;
|
||
padding: 6px;
|
||
box-shadow: 1px 1px 1px rgba(0,0,0,.25);
|
||
}
|
||
|
||
#code-csp-warning:hover {
|
||
text-decoration: none;
|
||
background-color:#007acc;
|
||
box-shadow: 2px 2px 2px rgba(0,0,0,.25);
|
||
}
|
||
|
||
body.scrollBeyondLastLine {
|
||
margin-bottom: calc(100vh - 22px);
|
||
}
|
||
|
||
body.showEditorSelection .code-line {
|
||
position: relative;
|
||
}
|
||
|
||
body.showEditorSelection .code-active-line:before,
|
||
body.showEditorSelection .code-line:hover:before {
|
||
content: "";
|
||
display: block;
|
||
position: absolute;
|
||
top: 0;
|
||
left: -12px;
|
||
height: 100%;
|
||
}
|
||
|
||
body.showEditorSelection li.code-active-line:before,
|
||
body.showEditorSelection li.code-line:hover:before {
|
||
left: -30px;
|
||
}
|
||
|
||
.vscode-light.showEditorSelection .code-active-line:before {
|
||
border-left: 3px solid rgba(0, 0, 0, 0.15);
|
||
}
|
||
|
||
.vscode-light.showEditorSelection .code-line:hover:before {
|
||
border-left: 3px solid rgba(0, 0, 0, 0.40);
|
||
}
|
||
|
||
.vscode-light.showEditorSelection .code-line .code-line:hover:before {
|
||
border-left: none;
|
||
}
|
||
|
||
.vscode-dark.showEditorSelection .code-active-line:before {
|
||
border-left: 3px solid rgba(255, 255, 255, 0.4);
|
||
}
|
||
|
||
.vscode-dark.showEditorSelection .code-line:hover:before {
|
||
border-left: 3px solid rgba(255, 255, 255, 0.60);
|
||
}
|
||
|
||
.vscode-dark.showEditorSelection .code-line .code-line:hover:before {
|
||
border-left: none;
|
||
}
|
||
|
||
.vscode-high-contrast.showEditorSelection .code-active-line:before {
|
||
border-left: 3px solid rgba(255, 160, 0, 0.7);
|
||
}
|
||
|
||
.vscode-high-contrast.showEditorSelection .code-line:hover:before {
|
||
border-left: 3px solid rgba(255, 160, 0, 1);
|
||
}
|
||
|
||
.vscode-high-contrast.showEditorSelection .code-line .code-line:hover:before {
|
||
border-left: none;
|
||
}
|
||
|
||
img {
|
||
max-width: 100%;
|
||
max-height: 100%;
|
||
}
|
||
|
||
a {
|
||
text-decoration: none;
|
||
}
|
||
|
||
a:hover {
|
||
text-decoration: underline;
|
||
}
|
||
|
||
a:focus,
|
||
input:focus,
|
||
select:focus,
|
||
textarea:focus {
|
||
outline: 1px solid -webkit-focus-ring-color;
|
||
outline-offset: -1px;
|
||
}
|
||
|
||
hr {
|
||
border: 0;
|
||
height: 2px;
|
||
border-bottom: 2px solid;
|
||
}
|
||
|
||
h1 {
|
||
padding-bottom: 0.3em;
|
||
line-height: 1.2;
|
||
border-bottom-width: 1px;
|
||
border-bottom-style: solid;
|
||
}
|
||
|
||
h1, h2, h3 {
|
||
font-weight: normal;
|
||
}
|
||
|
||
table {
|
||
border-collapse: collapse;
|
||
}
|
||
|
||
table > thead > tr > th {
|
||
text-align: left;
|
||
border-bottom: 1px solid;
|
||
}
|
||
|
||
table > thead > tr > th,
|
||
table > thead > tr > td,
|
||
table > tbody > tr > th,
|
||
table > tbody > tr > td {
|
||
padding: 5px 10px;
|
||
}
|
||
|
||
table > tbody > tr + tr > td {
|
||
border-top: 1px solid;
|
||
}
|
||
|
||
blockquote {
|
||
margin: 0 7px 0 5px;
|
||
padding: 0 16px 0 10px;
|
||
border-left-width: 5px;
|
||
border-left-style: solid;
|
||
}
|
||
|
||
code {
|
||
font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Courier New", monospace, "Droid Sans Fallback";
|
||
font-size: 1em;
|
||
line-height: 1.357em;
|
||
}
|
||
|
||
body.wordWrap pre {
|
||
white-space: pre-wrap;
|
||
}
|
||
|
||
pre:not(.hljs),
|
||
pre.hljs code > div {
|
||
padding: 16px;
|
||
border-radius: 3px;
|
||
overflow: auto;
|
||
}
|
||
|
||
pre code {
|
||
color: var(--vscode-editor-foreground);
|
||
tab-size: 4;
|
||
}
|
||
|
||
/** Theming */
|
||
|
||
.vscode-light pre {
|
||
background-color: rgba(220, 220, 220, 0.4);
|
||
}
|
||
|
||
.vscode-dark pre {
|
||
background-color: rgba(10, 10, 10, 0.4);
|
||
}
|
||
|
||
.vscode-high-contrast pre {
|
||
background-color: rgb(0, 0, 0);
|
||
}
|
||
|
||
.vscode-high-contrast h1 {
|
||
border-color: rgb(0, 0, 0);
|
||
}
|
||
|
||
.vscode-light table > thead > tr > th {
|
||
border-color: rgba(0, 0, 0, 0.69);
|
||
}
|
||
|
||
.vscode-dark table > thead > tr > th {
|
||
border-color: rgba(255, 255, 255, 0.69);
|
||
}
|
||
|
||
.vscode-light h1,
|
||
.vscode-light hr,
|
||
.vscode-light table > tbody > tr + tr > td {
|
||
border-color: rgba(0, 0, 0, 0.18);
|
||
}
|
||
|
||
.vscode-dark h1,
|
||
.vscode-dark hr,
|
||
.vscode-dark table > tbody > tr + tr > td {
|
||
border-color: rgba(255, 255, 255, 0.18);
|
||
}
|
||
|
||
</style>
|
||
|
||
<style>
|
||
/* Tomorrow Theme */
|
||
/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */
|
||
/* Original theme - https://github.com/chriskempson/tomorrow-theme */
|
||
|
||
/* Tomorrow Comment */
|
||
.hljs-comment,
|
||
.hljs-quote {
|
||
color: #8e908c;
|
||
}
|
||
|
||
/* Tomorrow Red */
|
||
.hljs-variable,
|
||
.hljs-template-variable,
|
||
.hljs-tag,
|
||
.hljs-name,
|
||
.hljs-selector-id,
|
||
.hljs-selector-class,
|
||
.hljs-regexp,
|
||
.hljs-deletion {
|
||
color: #c82829;
|
||
}
|
||
|
||
/* Tomorrow Orange */
|
||
.hljs-number,
|
||
.hljs-built_in,
|
||
.hljs-builtin-name,
|
||
.hljs-literal,
|
||
.hljs-type,
|
||
.hljs-params,
|
||
.hljs-meta,
|
||
.hljs-link {
|
||
color: #f5871f;
|
||
}
|
||
|
||
/* Tomorrow Yellow */
|
||
.hljs-attribute {
|
||
color: #eab700;
|
||
}
|
||
|
||
/* Tomorrow Green */
|
||
.hljs-string,
|
||
.hljs-symbol,
|
||
.hljs-bullet,
|
||
.hljs-addition {
|
||
color: #718c00;
|
||
}
|
||
|
||
/* Tomorrow Blue */
|
||
.hljs-title,
|
||
.hljs-section {
|
||
color: #4271ae;
|
||
}
|
||
|
||
/* Tomorrow Purple */
|
||
.hljs-keyword,
|
||
.hljs-selector-tag {
|
||
color: #8959a8;
|
||
}
|
||
|
||
.hljs {
|
||
display: block;
|
||
overflow-x: auto;
|
||
color: #4d4d4c;
|
||
padding: 0.5em;
|
||
}
|
||
|
||
.hljs-emphasis {
|
||
font-style: italic;
|
||
}
|
||
|
||
.hljs-strong {
|
||
font-weight: bold;
|
||
}
|
||
</style>
|
||
|
||
<style>
|
||
/*
|
||
* Markdown PDF CSS
|
||
*/
|
||
|
||
body {
|
||
font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif, "Meiryo";
|
||
padding: 0 12px;
|
||
}
|
||
|
||
pre {
|
||
background-color: #f8f8f8;
|
||
border: 1px solid #cccccc;
|
||
border-radius: 3px;
|
||
overflow-x: auto;
|
||
white-space: pre-wrap;
|
||
overflow-wrap: break-word;
|
||
}
|
||
|
||
pre:not(.hljs) {
|
||
padding: 23px;
|
||
line-height: 19px;
|
||
}
|
||
|
||
blockquote {
|
||
background: rgba(127, 127, 127, 0.1);
|
||
border-color: rgba(0, 122, 204, 0.5);
|
||
}
|
||
|
||
.emoji {
|
||
height: 1.4em;
|
||
}
|
||
|
||
code {
|
||
font-size: 14px;
|
||
line-height: 19px;
|
||
}
|
||
|
||
/* for inline code */
|
||
:not(pre):not(.hljs) > code {
|
||
color: #C9AE75; /* Change the old color so it seems less like an error */
|
||
font-size: inherit;
|
||
}
|
||
|
||
/* Page Break : use <div class="page"/> to insert page break
|
||
-------------------------------------------------------- */
|
||
.page {
|
||
page-break-after: always;
|
||
}
|
||
|
||
</style>
|
||
|
||
<script src="https://unpkg.com/mermaid/dist/mermaid.min.js"></script>
|
||
</head>
|
||
<body>
|
||
<script>
|
||
mermaid.initialize({
|
||
startOnLoad: true,
|
||
theme: document.body.classList.contains('vscode-dark') || document.body.classList.contains('vscode-high-contrast')
|
||
? 'dark'
|
||
: 'default'
|
||
});
|
||
</script>
|
||
<h1 id="suitecoffee--sistema-de-gesti%C3%B3n-para-cafeter%C3%ADas-dockerizado-y-multi%E2%80%91servicio">SuiteCoffee — Sistema de gestión para cafeterías (Dockerizado y multi‑servicio)</h1>
|
||
<p>SuiteCoffee es un sistema modular pensado para la <strong>gestión de cafeterías</strong> (y negocios afines), con servicios Node.js para <strong>aplicación</strong> y <strong>autenticación</strong>, bases de datos <strong>PostgreSQL</strong> separadas para negocio y multi‑tenencia, y un <strong>stack Docker Compose</strong> que facilita levantar entornos de <strong>desarrollo</strong> y <strong>producción</strong>. Incluye herramientas auxiliares como <strong>Nginx Proxy Manager (NPM)</strong> y <strong>CloudBeaver</strong> para administrar bases de datos desde el navegador.</p>
|
||
<blockquote>
|
||
<p>Repositorio: https://gitea.mateosaldain.uy/msaldain/SuiteCoffee.git</p>
|
||
</blockquote>
|
||
<hr>
|
||
<h2 id="tabla-de-contenidos">Tabla de contenidos</h2>
|
||
<ul>
|
||
<li><a href="#arquitectura">Arquitectura</a></li>
|
||
<li><a href="#caracter%C3%ADsticas-principales">Características principales</a></li>
|
||
<li><a href="#requisitos">Requisitos</a></li>
|
||
<li><a href="#inicio-r%C3%A1pido">Inicio rápido</a></li>
|
||
<li><a href="#variables-de-entorno">Variables de entorno</a></li>
|
||
<li><a href="#endpoints">Endpoints</a></li>
|
||
<li><a href="#estructura-del-proyecto">Estructura del proyecto</a></li>
|
||
<li><a href="#herramientas-auxiliares-npm-y-cloudbeaver">Herramientas auxiliares (NPM y CloudBeaver)</a></li>
|
||
<li><a href="#backups-y-restauraci%C3%B3n-de-vol%C3%BAmenes">Backups y restauración de volúmenes</a></li>
|
||
<li><a href="#comandos-%C3%BAtiles">Comandos útiles</a></li>
|
||
<li><a href="#licencia">Licencia</a></li>
|
||
<li><a href="#sugerencias-de-mejora">Sugerencias de mejora</a></li>
|
||
</ul>
|
||
<hr>
|
||
<h2 id="arquitectura">Arquitectura</h2>
|
||
<p><strong>Servicios principales</strong></p>
|
||
<ul>
|
||
<li><strong>app</strong> (Node.js / Express): API de negocio y páginas simples para cargar y listar <em>roles, usuarios, categorías y productos</em>.</li>
|
||
<li><strong>auth</strong> (Node.js / Express + bcrypt): endpoints de <strong>registro</strong> e <strong>inicio de sesión</strong>.</li>
|
||
<li><strong>db</strong> (PostgreSQL 16): base de datos de la aplicación.</li>
|
||
<li><strong>tenants</strong> (PostgreSQL 16): base de datos separada para <strong>multi-tenencia</strong> (aislar clientes/tiendas).</li>
|
||
</ul>
|
||
<p><strong>Herramientas</strong></p>
|
||
<ul>
|
||
<li><strong>Nginx Proxy Manager (NPM)</strong>: reverse proxy y certificados (Let’s Encrypt) para exponer servicios.</li>
|
||
<li><strong>CloudBeaver (DBeaver)</strong>: administración de PostgreSQL vía web.</li>
|
||
</ul>
|
||
<p><strong>Redes & Volúmenes</strong></p>
|
||
<ul>
|
||
<li>Redes independientes por entorno (<code>suitecoffee_dev_net</code> / <code>suitecoffee_prod_net</code>).</li>
|
||
<li>Volúmenes gestionados por Compose para persistencia: <code>suitecoffee-db</code>, <code>tenants-db</code>, etc.</li>
|
||
</ul>
|
||
<h3 id="diagrama-alto-nivel">Diagrama (alto nivel)</h3>
|
||
<pre class="hljs"><code><div>@startuml
|
||
skinparam componentStyle rectangle
|
||
skinparam rectangle {
|
||
BorderColor #555
|
||
RoundCorner 10
|
||
}
|
||
actor Usuario
|
||
|
||
package "Entorno DEV/PROD" {
|
||
[app (Express)] as APP
|
||
[auth (Express + bcrypt)] as AUTH
|
||
database "db (PostgreSQL)" as DB
|
||
database "tenants (PostgreSQL)" as TENANTS
|
||
APP -down-> DB : Pool PG
|
||
APP -down-> TENANTS : Pool PG
|
||
AUTH -down-> DB : Pool PG (usuarios)
|
||
Usuario --> APP : UI / API
|
||
Usuario --> AUTH : Login/Registro
|
||
}
|
||
|
||
package "Herramientas" {
|
||
[Nginx Proxy Manager] as NPM
|
||
[CloudBeaver] as DBVR
|
||
NPM ..> APP : proxy
|
||
NPM ..> AUTH : proxy
|
||
DBVR ..> DB : admin
|
||
DBVR ..> TENANTS : admin
|
||
}
|
||
@enduml
|
||
</div></code></pre>
|
||
<hr>
|
||
<h2 id="caracter%C3%ADsticas-principales">Características principales</h2>
|
||
<ul>
|
||
<li><strong>API REST</strong> para entidades clave (roles, usuarios, categorías y productos).</li>
|
||
<li><strong>Autenticación básica</strong> (registro y login) con <strong>hash de contraseñas</strong> (bcrypt).</li>
|
||
<li><strong>Multi‑tenencia</strong> con base <code>tenants</code> separada para aislar clientes/tiendas.</li>
|
||
<li><strong>Docker Compose v2</strong> con entornos de <strong>desarrollo</strong> y <strong>producción</strong>.</li>
|
||
<li><strong>Herramientas integradas</strong> (NPM + CloudBeaver) en un <code>compose.tools.yaml</code> aparte.</li>
|
||
<li><strong>Scripts</strong> de <strong>backup/restauración de volúmenes</strong> y <strong>gestión de entornos</strong>.</li>
|
||
</ul>
|
||
<hr>
|
||
<h2 id="requisitos">Requisitos</h2>
|
||
<ul>
|
||
<li><strong>Docker</strong> y <strong>Docker Compose v2</strong> (recomendado).</li>
|
||
<li><strong>Python 3.9+</strong> (para scripts <code>suitecoffee.py</code>, backups y utilidades).</li>
|
||
<li><strong>Node.js 20+</strong> (sólo si vas a ejecutar servicios Node fuera de Docker).</li>
|
||
</ul>
|
||
<hr>
|
||
<h2 id="inicio-r%C3%A1pido">Inicio rápido</h2>
|
||
<h3 id="opci%C3%B3n-a--gestor-interactivo-recomendado">Opción A — Gestor interactivo (recomendado)</h3>
|
||
<ol>
|
||
<li>Clona el repo y entra al directorio:<pre class="hljs"><code><div>git <span class="hljs-built_in">clone</span> https://gitea.mateosaldain.uy/msaldain/SuiteCoffee.git
|
||
<span class="hljs-built_in">cd</span> SuiteCoffee
|
||
</div></code></pre>
|
||
</li>
|
||
<li>(Opcional) Crea/copía tus archivos <code>.env</code> para <strong>app</strong> y <strong>auth</strong> en <code>./services/<service>/.env.development</code> (ver sección de variables).</li>
|
||
<li>Ejecuta el gestor:<pre class="hljs"><code><div>python3 suitecoffee.py
|
||
</div></code></pre>
|
||
<ul>
|
||
<li>Verás un <strong>menú</strong> para levantar <strong>DESARROLLO</strong> o <strong>PRODUCCIÓN</strong>.</li>
|
||
<li>Desde ahí también puedes <strong>levantar/apagar</strong> las herramientas <strong>NPM</strong> y <strong>CloudBeaver</strong>.</li>
|
||
</ul>
|
||
</li>
|
||
<li>Accede:
|
||
<ul>
|
||
<li>App (dev): suele estar disponible via NPM o directamente dentro de la red, según tu configuración.</li>
|
||
<li>Páginas simples: <code>/roles</code>, <code>/usuarios</code>, <code>/categorias</code>, <code>/productos</code> (servidas por <code>app</code>).</li>
|
||
<li>Salud: <code>/health</code> en <code>app</code> y <code>auth</code>.</li>
|
||
</ul>
|
||
</li>
|
||
</ol>
|
||
<blockquote>
|
||
<p>Consejo: primero levanta <strong>desarrollo/producción</strong> y luego las <strong>herramientas</strong> para que existan las redes externas <code>suitecoffee_dev_net</code>/<code>suitecoffee_prod_net</code> que usa <code>compose.tools.yaml</code>.</p>
|
||
</blockquote>
|
||
<h3 id="opci%C3%B3n-b--comandos-docker-compose-avanzado">Opción B — Comandos Docker Compose (avanzado)</h3>
|
||
<ul>
|
||
<li>
|
||
<p><strong>Desarrollo</strong>:</p>
|
||
<pre class="hljs"><code><div>docker compose -f compose.yaml -f compose.dev.yaml --env-file ./services/app/.env.development --env-file ./services/auth/.env.development -p suitecoffee_dev up -d
|
||
</div></code></pre>
|
||
</li>
|
||
<li>
|
||
<p><strong>Producción</strong>:</p>
|
||
<pre class="hljs"><code><div>docker compose -f compose.yaml -f compose.prod.yaml --env-file ./services/app/.env.production --env-file ./services/auth/.env.production -p suitecoffee_prod up -d
|
||
</div></code></pre>
|
||
</li>
|
||
</ul>
|
||
<blockquote>
|
||
<p>Los puertos se <strong>exponen</strong> para herramientas (NPM UI <code>:81</code>, CloudBeaver <code>:8978</code>); los servicios <code>app</code> y <code>auth</code> se <strong>exponen dentro de la red</strong> y se publican externamente a través de NPM.</p>
|
||
</blockquote>
|
||
<hr>
|
||
<h2 id="variables-de-entorno">Variables de entorno</h2>
|
||
<p>Crea un archivo <code>.env.development</code> (y uno <code>.env.production</code>) en <strong>cada servicio</strong> (<code>./services/app</code> y <code>./services/auth</code>). Variables comunes:</p>
|
||
<pre class="hljs"><code><div># Servidor
|
||
PORT=4000 # puerto HTTP del servicio
|
||
NODE_ENV=development # development | production
|
||
|
||
# Base de datos
|
||
DB_HOST=db # nombre del servicio postgres (o host)
|
||
DB_LOCAL_PORT=5432 # puerto de PG al que conectarse
|
||
DB_USER=postgres
|
||
DB_PASS=postgres
|
||
DB_NAME=suitecoffee_db # para 'db' (aplicación)
|
||
TENANTS_DB_NAME=tenants_db # si el servicio necesita apuntar a 'tenants'
|
||
</div></code></pre>
|
||
<blockquote>
|
||
<p>Ajusta <code>DB_HOST</code> a <code>db</code> o <code>tenants</code> según corresponda. En desarrollo, los alias útiles son <code>dev-db</code> y <code>dev-tenants</code>; en producción: <code>prod-db</code> y <code>prod-tenants</code>.</p>
|
||
</blockquote>
|
||
<hr>
|
||
<h2 id="endpoints">Endpoints</h2>
|
||
<h3 id="servicio-app-negocio">Servicio <strong>app</strong> (negocio)</h3>
|
||
<ul>
|
||
<li><code>GET /health</code></li>
|
||
<li><code>GET /api/roles</code> — lista roles</li>
|
||
<li><code>POST /api/roles</code> — crea un rol</li>
|
||
<li><code>GET /api/usuarios</code> — lista usuarios</li>
|
||
<li><code>POST /api/usuarios</code> — crea un usuario</li>
|
||
<li><code>GET /api/categorias</code> — lista categorías</li>
|
||
<li><code>POST /api/categorias</code> — crea una categoría</li>
|
||
<li><code>GET /api/productos</code> — lista productos</li>
|
||
<li><code>POST /api/productos</code> — crea un producto</li>
|
||
<li>Páginas estáticas simples para probar: <code>/roles</code>, <code>/usuarios</code>, <code>/categorias</code>, <code>/productos</code></li>
|
||
</ul>
|
||
<h3 id="servicio-auth-autenticaci%C3%B3n">Servicio <strong>auth</strong> (autenticación)</h3>
|
||
<ul>
|
||
<li><code>GET /health</code></li>
|
||
<li><code>POST /register</code> — registro de usuario (password con <strong>bcrypt</strong>)</li>
|
||
<li><code>POST /auth/login</code> — inicio de sesión</li>
|
||
</ul>
|
||
<blockquote>
|
||
<p><strong>Nota</strong>: En esta etapa los endpoints son <strong>básicos</strong> y pensados para desarrollo/PoC. Ver la sección <em>Sugerencias de mejora</em> para próximos pasos (JWT, autorización, etc.).</p>
|
||
</blockquote>
|
||
<hr>
|
||
<h2 id="estructura-del-proyecto">Estructura del proyecto</h2>
|
||
<pre class="hljs"><code><div>SuiteCoffee/
|
||
├─ services/
|
||
│ ├─ app/
|
||
│ │ ├─ src/
|
||
│ │ │ ├─ index.js # API y páginas simples
|
||
│ │ │ └─ pages/ # roles.html, usuarios.html, categorias.html, productos.html
|
||
│ │ ├─ .env.development # variables (ejemplo)
|
||
│ │ └─ .env.production
|
||
│ └─ auth/
|
||
│ ├─ src/
|
||
│ │ └─ index.js # /register y /auth/login
|
||
│ ├─ .env.development
|
||
│ └─ .env.production
|
||
├─ compose.yaml # base (db, tenants)
|
||
├─ compose.dev.yaml # entorno desarrollo (app, auth, db, tenants)
|
||
├─ compose.prod.yaml # entorno producción (app, auth, db, tenants)
|
||
├─ compose.tools.yaml # herramientas (NPM, CloudBeaver) con redes externas
|
||
├─ suitecoffee.py # gestor interactivo (Docker Compose)
|
||
├─ backup_compose_volumes.py # backups de volúmenes Compose
|
||
└─ restore_compose_volumes.py# restauración de volúmenes Compose
|
||
</div></code></pre>
|
||
<hr>
|
||
<h2 id="herramientas-auxiliares-npm-y-cloudbeaver">Herramientas auxiliares (NPM y CloudBeaver)</h2>
|
||
<p>Los servicios de <strong>herramientas</strong> están separados para poder usarlos con <strong>ambos entornos</strong> (dev y prod) a la vez. Se levantan con <code>compose.tools.yaml</code> y se conectan a las <strong>redes externas</strong> <code>suitecoffee_dev_net</code> y <code>suitecoffee_prod_net</code>.</p>
|
||
<ul>
|
||
<li><strong>Nginx Proxy Manager (NPM)</strong><br>
|
||
Puertos: <code>80</code> (HTTP), <code>81</code> (UI). Volúmenes: <code>npm_data</code>, <code>npm_letsencrypt</code>.</li>
|
||
<li><strong>CloudBeaver</strong><br>
|
||
Puerto: <code>8978</code>. Volúmenes: <code>dbeaver_logs</code>, <code>dbeaver_workspace</code>.</li>
|
||
</ul>
|
||
<blockquote>
|
||
<p>Si es la primera vez, arranca un entorno (dev/prod) para que Compose cree las redes; luego levanta las herramientas:</p>
|
||
<pre class="hljs"><code><div>docker compose -f compose.tools.yaml --profile npm -p suitecoffee up -d
|
||
docker compose -f compose.tools.yaml --profile dbeaver -p suitecoffee up -d
|
||
</div></code></pre>
|
||
</blockquote>
|
||
<hr>
|
||
<h2 id="backups-y-restauraci%C3%B3n-de-vol%C3%BAmenes">Backups y restauración de volúmenes</h2>
|
||
<p>Este repo incluye dos utilidades:</p>
|
||
<ul>
|
||
<li><code>backup_compose_volumes.py</code> — detecta volúmenes de un proyecto de Compose (por <strong>labels</strong> y nombres) y los exporta a <code>tar.gz</code> usando un contenedor <code>alpine</code> temporal.</li>
|
||
<li><code>restore_compose_volumes.py</code> — permite restaurar esos <code>tar.gz</code> en volúmenes (útil para migraciones y pruebas).</li>
|
||
</ul>
|
||
<p><strong>Ejemplos básicos</strong></p>
|
||
<pre class="hljs"><code><div><span class="hljs-comment"># Listar ayuda</span>
|
||
python3 backup_compose_volumes.py --<span class="hljs-built_in">help</span>
|
||
|
||
<span class="hljs-comment"># Respaldar volúmenes asociados a "suitecoffee_dev" en ./backups</span>
|
||
python3 backup_compose_volumes.py --project suitecoffee_dev --output ./backups
|
||
|
||
<span class="hljs-comment"># Restaurar un archivo a un volumen</span>
|
||
python3 restore_compose_volumes.py --archive ./backups/suitecoffee_dev_suitecoffee-db-YYYYmmddHHMMSS.tar.gz --volume suitecoffee_dev_suitecoffee-db
|
||
</div></code></pre>
|
||
<blockquote>
|
||
<p>Consejo: si migraste manualmente y ves advertencias tipo “volume ... already exists but was not created by Docker Compose”, considera marcar el volumen como <code>external: true</code> en el YAML o recrearlo para que Compose lo etiquete correctamente.</p>
|
||
</blockquote>
|
||
<hr>
|
||
<h2 id="comandos-%C3%BAtiles">Comandos útiles</h2>
|
||
<pre class="hljs"><code><div><span class="hljs-comment"># Ver estado (menú interactivo)</span>
|
||
python3 suitecoffee.py
|
||
|
||
<span class="hljs-comment"># Levantar DEV/PROD por menú (con o sin --force-recreate)</span>
|
||
python3 suitecoffee.py
|
||
|
||
<span class="hljs-comment"># Levantar herramientas (también desde menú)</span>
|
||
docker compose -f compose.tools.yaml --profile npm -p suitecoffee up -d
|
||
docker compose -f compose.tools.yaml --profile dbeaver -p suitecoffee up -d
|
||
|
||
<span class="hljs-comment"># Inspeccionar servicios/volúmenes que Compose detecta desde los YAML</span>
|
||
docker compose -f compose.yaml -f compose.dev.yaml config --services
|
||
docker compose -f compose.yaml -f compose.dev.yaml config --format json | jq .volumes
|
||
</div></code></pre>
|
||
<hr>
|
||
<h2 id="licencia">Licencia</h2>
|
||
<ul>
|
||
<li><strong>ISC</strong> (ver <code>package.json</code>).</li>
|
||
</ul>
|
||
<hr>
|
||
<h2 id="sugerencias-de-mejora">Sugerencias de mejora</h2>
|
||
<ul>
|
||
<li><strong>Autenticación y seguridad</strong>
|
||
<ul>
|
||
<li>Emitir <strong>JWT</strong> en el login y proteger rutas (roles/autorización por perfil).</li>
|
||
<li>Configurar <strong>CORS</strong> por orígenes (en dev está abierto; en prod restringir).</li>
|
||
<li>Añadir <strong>rate‑limit</strong> y <strong>helmet</strong> en Express.</li>
|
||
</ul>
|
||
</li>
|
||
<li><strong>Esquema de datos y migraciones</strong>
|
||
<ul>
|
||
<li>Añadir migraciones automatizadas (p.ej. <strong>Prisma</strong>, <strong>Knex</strong>, <strong>Sequelize</strong> o SQL versionado) y seeds iniciales.</li>
|
||
<li>Clarificar el <strong>modelo multi‑tenant</strong>: por <strong>BD por tenant</strong> o <strong>schema por tenant</strong>; documentar estrategia.</li>
|
||
</ul>
|
||
</li>
|
||
<li><strong>Calidad & DX</strong>
|
||
<ul>
|
||
<li>Tests (unitarios e integración) y <strong>CI</strong> básico.</li>
|
||
<li>Validación de entrada (<strong>zod / joi</strong>), manejo de errores consistente y logs estructurados.</li>
|
||
</ul>
|
||
</li>
|
||
<li><strong>Docker/DevOps</strong>
|
||
<ul>
|
||
<li>Documentar variables <code>.env</code> completas por servicio.</li>
|
||
<li>Publicar imágenes de producción y usar <code>IMAGE:TAG</code> en <code>compose.prod.yaml</code> (evitar build en servidor).</li>
|
||
<li>Añadir <strong>healthchecks</strong> a <code>app</code>/<code>auth</code> (ya hay ejemplos comentados).</li>
|
||
</ul>
|
||
</li>
|
||
<li><strong>Frontend</strong>
|
||
<ul>
|
||
<li>Reemplazar páginas HTML de prueba por un <strong>frontend</strong> (React/Vite) o una UI admin mínima.</li>
|
||
</ul>
|
||
</li>
|
||
<li><strong>Pequeños fixes</strong>
|
||
<ul>
|
||
<li>En los HTML de ejemplo corregir las referencias a campos <code>id_rol</code>, <code>id_categoria</code>, etc.</li>
|
||
<li>Centralizar constantes (nombres de tablas/campos) y normalizar respuestas API.</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
<hr>
|
||
|
||
</body>
|
||
</html>
|