Subida a producción como Acana
This commit is contained in:
parent
46379e8881
commit
1783f630cf
11
Dockerfile
11
Dockerfile
@ -1,5 +1,6 @@
|
||||
# syntax=docker/dockerfile:1.7-labs
|
||||
|
||||
ARG COMPANY
|
||||
ARG NODE_IMAGE=node:24-bookworm-slim
|
||||
ARG PNPM_VERSION=10.20.0
|
||||
|
||||
@ -7,6 +8,10 @@ ARG PNPM_VERSION=10.20.0
|
||||
# 0) Base común
|
||||
########################
|
||||
FROM ${NODE_IMAGE} AS base
|
||||
|
||||
ARG COMPANY
|
||||
ENV COMPANY=${COMPANY}
|
||||
|
||||
ENV CI=1 \
|
||||
NODE_ENV=production
|
||||
RUN corepack enable && corepack prepare pnpm@${PNPM_VERSION} --activate
|
||||
@ -38,7 +43,7 @@ FROM base AS builder
|
||||
COPY --from=pruner /repo/out/full/ ./
|
||||
COPY --from=pruner /repo/out/pnpm-lock.yaml ./pnpm-lock.yaml
|
||||
|
||||
COPY --from=pruner /repo/modules/customer-invoices/src/api/application/use-cases/proformas/report-proforma/reporter/templates ./templates
|
||||
#COPY --from=pruner /repo/modules/customer-invoices/src/api/application/use-cases/proformas/report-proforma/reporter/templates ./templates
|
||||
|
||||
# Reutilizamos la store prefetch
|
||||
#COPY --from=installer /root/.local/share/pnpm/store /root/.local/share/pnpm/store
|
||||
@ -92,11 +97,11 @@ COPY --from=builder /repo/pnpm-lock.yaml ./pnpm-lock.yaml
|
||||
COPY --from=builder /repo/pnpm-workspace.yaml ./pnpm-workspace.yaml
|
||||
|
||||
COPY --from=builder /repo/apps/server/dist ./apps/server/dist
|
||||
COPY --from=builder /repo/apps/server/.env.production ./apps/server/dist/.env
|
||||
COPY --from=builder /repo/apps/server/.env.${COMPANY} ./apps/server/dist/.env
|
||||
COPY --from=builder /repo/apps/server/node_modules ./apps/server/node_modules
|
||||
COPY --from=builder /repo/apps/server/package.json ./apps/server/package.json
|
||||
|
||||
COPY --from=builder /repo/templates ./apps/server/dist/templates
|
||||
#COPY --from=builder /repo/templates ./apps/server/dist/templates
|
||||
|
||||
|
||||
# Salud del contenedor (ajusta puerto/endpoint)
|
||||
|
||||
33
apps/server/.env.acana
Normal file
33
apps/server/.env.acana
Normal file
@ -0,0 +1,33 @@
|
||||
COMPANY=acana
|
||||
DOMAIN=acana.factuges.app
|
||||
|
||||
#WEB_VERSION=v0.0.1-20251031-200910
|
||||
#API_IMAGE=factuges-server:rodax-v0.0.1
|
||||
API_PORT=3002
|
||||
|
||||
|
||||
|
||||
NODE_ENV=production
|
||||
HOST=0.0.0.0
|
||||
PORT=3002
|
||||
FRONTEND_URL=https://aana.factuges.app
|
||||
|
||||
DB_DIALECT=mysql
|
||||
DB_USER=acana
|
||||
DB_PASS=r@U8%GJ+2e/AWR
|
||||
DB_NAME=factuges_acana
|
||||
DB_ROOT_PASS=cQF#qRM*JU+tDyf
|
||||
DB_PORT=3306
|
||||
DB_LOGGING=false
|
||||
DB_SYNC_MODE=alter
|
||||
|
||||
APP_TIMEZONE=Europe/Madrid
|
||||
TRUST_PROXY=0
|
||||
|
||||
JWT_SECRET=supersecretkey
|
||||
JWT_ACCESS_EXPIRATION=1h
|
||||
JWT_REFRESH_EXPIRATION=7d
|
||||
|
||||
PUPPETEER_EXECUTABLE_PATH=/usr/bin/google-chrome
|
||||
|
||||
TEMPLATES_PATH=/opt/factuges/acana/templates
|
||||
@ -9,7 +9,7 @@ DB_HOST=localhost
|
||||
DB_PORT=3306
|
||||
DB_NAME=uecko_erp
|
||||
DB_USER=rodax
|
||||
DB_PASSWORD=rodax
|
||||
DB_PASS=rodax
|
||||
|
||||
DB_LOGGING=false
|
||||
DB_SYNC_MODE=alter
|
||||
|
||||
@ -23,7 +23,7 @@ DB_HOST=localhost
|
||||
DB_PORT=3306
|
||||
DB_NAME=uecko_erp
|
||||
DB_USER=rodax
|
||||
DB_PASSWORD=rodax
|
||||
DB_PASS=rodax
|
||||
|
||||
# Log de Sequelize (true|false)
|
||||
DB_LOGGING=false
|
||||
|
||||
@ -22,7 +22,7 @@ DB_HOST=localhost
|
||||
DB_PORT=3306
|
||||
DB_NAME=uecko_erp
|
||||
DB_USER=rodax
|
||||
DB_PASSWORD=rodax
|
||||
DB_PASS=rodax
|
||||
|
||||
DB_LOGGING=false
|
||||
DB_SYNC_MODE=alter
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@erp/factuges-server",
|
||||
"version": "0.0.13",
|
||||
"version": "0.0.14",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "tsup src/index.ts --config tsup.config.ts",
|
||||
@ -69,4 +69,4 @@
|
||||
"engines": {
|
||||
"node": ">=22"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,7 @@
|
||||
import { Sequelize } from "sequelize";
|
||||
|
||||
import { logger } from "../lib/logger";
|
||||
|
||||
import { ENV } from "./index";
|
||||
|
||||
/**
|
||||
@ -73,11 +75,11 @@ function buildSequelize(): Sequelize {
|
||||
if (!ENV.DB_DIALECT) {
|
||||
throw new Error("DB_DIALECT is required when DATABASE_URL is not provided");
|
||||
}
|
||||
if (!ENV.DB_NAME || !ENV.DB_USER) {
|
||||
if (!(ENV.DB_NAME && ENV.DB_USER)) {
|
||||
throw new Error("DB_NAME and DB_USER are required when DATABASE_URL is not provided");
|
||||
}
|
||||
|
||||
return new Sequelize(ENV.DB_NAME, ENV.DB_USER, ENV.DB_PASSWORD, {
|
||||
return new Sequelize(ENV.DB_NAME, ENV.DB_USER, ENV.DB_PASS, {
|
||||
host: ENV.DB_HOST,
|
||||
port: ENV.DB_PORT,
|
||||
dialect: ENV.DB_DIALECT,
|
||||
|
||||
@ -29,7 +29,7 @@ const DB_HOST = process.env.DB_HOST ?? "localhost";
|
||||
const DB_PORT = asNumber(process.env.DB_PORT, 3306);
|
||||
const DB_NAME = process.env.DB_NAME ?? "";
|
||||
const DB_USER = process.env.DB_USER ?? "";
|
||||
const DB_PASSWORD = process.env.DB_PASSWORD ?? "";
|
||||
const DB_PASS = process.env.DB_PASS ?? "";
|
||||
|
||||
const DB_LOGGING = asBoolean(process.env.DB_LOGGING, false);
|
||||
|
||||
@ -57,7 +57,7 @@ export const ENV = {
|
||||
DB_PORT,
|
||||
DB_NAME,
|
||||
DB_USER,
|
||||
DB_PASSWORD,
|
||||
DB_PASS,
|
||||
DB_LOGGING,
|
||||
DB_SYNC_MODE,
|
||||
APP_TIMEZONE,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Application, Request, Response } from "express";
|
||||
import type { Application, Request, Response } from "express";
|
||||
import { DateTime } from "luxon";
|
||||
/**
|
||||
|
||||
@ -8,9 +8,13 @@ Registra endpoints de liveness/readiness.
|
||||
|
||||
/__ready : 200 si ready=true, 503 en caso contrario.
|
||||
*/
|
||||
export function registerHealthRoutes(app: Application, getStatus: () => { ready: boolean }): void {
|
||||
export function registerHealthRoutes(
|
||||
app: Application,
|
||||
baseRoutePath: string,
|
||||
getStatus: () => { ready: boolean }
|
||||
): void {
|
||||
// Liveness probe: indica que el proceso responde
|
||||
app.get("/__health", (_req: Request, res: Response) => {
|
||||
app.get(`${baseRoutePath}/__health`, (_req: Request, res: Response) => {
|
||||
// Información mínima y no sensible
|
||||
res.status(200).json({
|
||||
status: "ok",
|
||||
@ -19,7 +23,7 @@ export function registerHealthRoutes(app: Application, getStatus: () => { ready:
|
||||
});
|
||||
|
||||
// Readiness probe: indica que el servidor está listo para tráfico
|
||||
app.get("/__ready", (_req: Request, res: Response) => {
|
||||
app.get(`${baseRoutePath}/__ready`, (_req: Request, res: Response) => {
|
||||
const { ready } = getStatus();
|
||||
if (ready) {
|
||||
return res.status(200).json({
|
||||
|
||||
@ -120,9 +120,6 @@ registerModules();
|
||||
|
||||
const app = createApp();
|
||||
|
||||
// ➕ Rutas de salud disponibles desde el inicio del proceso
|
||||
registerHealthRoutes(app, () => ({ ready: isReady }));
|
||||
|
||||
// Crea el servidor HTTP
|
||||
const server = http.createServer(app);
|
||||
|
||||
@ -235,6 +232,9 @@ process.on("uncaughtException", async (error: Error) => {
|
||||
// initStructure(sequelizeConn.connection);
|
||||
// insertUsers();
|
||||
|
||||
// ➕ Rutas de salud disponibles desde el inicio del proceso
|
||||
registerHealthRoutes(app, API_BASE_PATH, () => ({ ready: isReady }));
|
||||
|
||||
await initModules({
|
||||
app,
|
||||
database,
|
||||
|
||||
1
apps/web/.env.acana
Normal file
1
apps/web/.env.acana
Normal file
@ -0,0 +1 @@
|
||||
VITE_API_SERVER_URL=https://acana.factuges.app/api/v1
|
||||
1
apps/web/.env.rodax
Normal file
1
apps/web/.env.rodax
Normal file
@ -0,0 +1 @@
|
||||
VITE_API_SERVER_URL=https://factuges.rodax-software.local/api/v1
|
||||
@ -1,11 +1,13 @@
|
||||
{
|
||||
"name": "@erp/factuges-web",
|
||||
"private": true,
|
||||
"version": "0.0.13",
|
||||
"version": "0.0.14",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --host --clearScreen false",
|
||||
"build": "tsc && vite build",
|
||||
"build:rodax": "tsc && vite build --mode rodax",
|
||||
"build:acana": "tsc && vite build --mode acana",
|
||||
"preview": "vite preview",
|
||||
"clean": "rm -rf dist && rm -rf node_modules && rm -rf .turbo",
|
||||
"check:deps": "pnpm exec depcheck",
|
||||
@ -48,4 +50,4 @@
|
||||
"tw-animate-css": "^1.2.9",
|
||||
"vite-plugin-html": "^3.2.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,15 +1,13 @@
|
||||
{
|
||||
"name": "@erp/auth",
|
||||
"version": "0.0.13",
|
||||
"version": "0.0.14",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
|
||||
"scripts": {
|
||||
"typecheck": "tsc -p tsconfig.json --noEmit",
|
||||
"clean": "rimraf .turbo node_modules dist"
|
||||
},
|
||||
|
||||
"exports": {
|
||||
".": "./src/common/index.ts",
|
||||
"./api": "./src/api/index.ts",
|
||||
@ -39,4 +37,4 @@
|
||||
"react-router-dom": "^6.26.0",
|
||||
"react-secure-storage": "^1.3.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@erp/core",
|
||||
"version": "0.0.13",
|
||||
"version": "0.0.14",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
@ -54,4 +54,4 @@
|
||||
"sequelize": "^6.37.5",
|
||||
"zod": "^4.1.11"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@erp/customer-invoices",
|
||||
"version": "0.0.13",
|
||||
"version": "0.0.14",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
@ -63,4 +63,4 @@
|
||||
"sequelize": "^6.37.5",
|
||||
"zod": "^4.1.11"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -30,7 +30,6 @@ import {
|
||||
ItemAmount,
|
||||
ItemDiscount,
|
||||
ItemQuantity,
|
||||
ItemTaxes,
|
||||
} from "../../../../domain";
|
||||
|
||||
/**
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@erp/customers",
|
||||
"version": "0.0.13",
|
||||
"version": "0.0.14",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
@ -51,4 +51,4 @@
|
||||
"use-debounce": "^10.0.5",
|
||||
"zod": "^4.1.11"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,10 +1,9 @@
|
||||
{
|
||||
"name": "@erp/doc-numbering",
|
||||
"version": "0.0.13",
|
||||
"version": "0.0.14",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
|
||||
"scripts": {
|
||||
"typecheck": "tsc -p tsconfig.json --noEmit",
|
||||
"clean": "rimraf .turbo node_modules dist"
|
||||
@ -30,4 +29,4 @@
|
||||
"@repo/rdx-utils": "workspace:*",
|
||||
"@repo/rdx-logger": "workspace:*"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,20 +1,17 @@
|
||||
{
|
||||
"name": "@repo/rdx-criteria",
|
||||
"version": "0.0.13",
|
||||
"version": "0.0.14",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
|
||||
"scripts": {
|
||||
"typecheck": "tsc -p tsconfig.json --noEmit",
|
||||
"clean": "rimraf .turbo node_modules dist"
|
||||
},
|
||||
|
||||
"exports": {
|
||||
".": "./src/defaults.ts",
|
||||
"./server": "./src/index.ts"
|
||||
},
|
||||
|
||||
"devDependencies": {
|
||||
"@repo/typescript-config": "workspace:*",
|
||||
"rimraf": "^6.0.0",
|
||||
@ -23,4 +20,4 @@
|
||||
"dependencies": {
|
||||
"sequelize": "^6.37.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,19 +1,16 @@
|
||||
{
|
||||
"name": "@repo/rdx-ddd",
|
||||
"version": "0.0.13",
|
||||
"version": "0.0.14",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
|
||||
"scripts": {
|
||||
"typecheck": "tsc -p tsconfig.json --noEmit",
|
||||
"clean": "rimraf .turbo node_modules dist"
|
||||
},
|
||||
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
|
||||
"devDependencies": {
|
||||
"@repo/typescript-config": "workspace:*",
|
||||
"@types/node": "^22.15.12",
|
||||
@ -27,4 +24,4 @@
|
||||
"shallow-equal-object": "^1.1.1",
|
||||
"zod": "^4.1.11"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,19 +1,16 @@
|
||||
{
|
||||
"name": "@repo/rdx-logger",
|
||||
"version": "0.0.13",
|
||||
"version": "0.0.14",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
|
||||
"scripts": {
|
||||
"typecheck": "tsc -p tsconfig.json --noEmit",
|
||||
"clean": "rimraf .turbo node_modules dist"
|
||||
},
|
||||
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
|
||||
"devDependencies": {
|
||||
"typescript": "^5.9.3"
|
||||
},
|
||||
@ -22,4 +19,4 @@
|
||||
"winston": "^3.17.0",
|
||||
"winston-daily-rotate-file": "^5.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,19 +1,16 @@
|
||||
{
|
||||
"name": "@repo/rdx-utils",
|
||||
"version": "0.0.13",
|
||||
"version": "0.0.14",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
|
||||
"scripts": {
|
||||
"typecheck": "tsc -p tsconfig.json --noEmit",
|
||||
"clean": "rimraf .turbo node_modules dist"
|
||||
},
|
||||
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
|
||||
"devDependencies": {
|
||||
"@repo/typescript-config": "workspace:*",
|
||||
"@types/node": "^22.15.12",
|
||||
@ -23,4 +20,4 @@
|
||||
"joi": "^17.13.3",
|
||||
"uuid": "^11.0.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
16
scripts/Caddyfile
Normal file
16
scripts/Caddyfile
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
email soporte@rodax-software.com
|
||||
auto_https disable_redirects
|
||||
}
|
||||
|
||||
|
||||
https://presupuestos.uecko.com:13001 {
|
||||
reverse_proxy backend:3001
|
||||
encode gzip # Comprime las respuestas con gzip
|
||||
}
|
||||
|
||||
:443 {
|
||||
root * /srv
|
||||
file_server
|
||||
try_files {path} /index.html # Esto asegura que las rutas en React funcionen correctamente
|
||||
}
|
||||
@ -1,21 +1,20 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_VERSION="1.0.5"
|
||||
SCRIPT_VERSION="1.2.0"
|
||||
|
||||
# =====================================================
|
||||
# FACTUGES Build Script
|
||||
# -----------------------------------------------------
|
||||
# Build + Export de la API y/o
|
||||
# compilación de la Web (por compañía)
|
||||
# Build + Export de la API y/o WEB (por compañía)
|
||||
# =====================================================
|
||||
# Uso:
|
||||
# ./scripts/build-api.sh <company> [--api|web|all] [--load]
|
||||
# ./build_factuges.sh <company> [--api|web|all] [--load]
|
||||
#
|
||||
# Ejemplos:
|
||||
# ./scripts/build-api.sh acme --api --load # solo API + carga en Docker local
|
||||
# ./scripts/build-api.sh acme --web # solo web
|
||||
# ./scripts/build-api.sh acme # API + web (por defecto)
|
||||
# ./build_factuges.sh acme --api --load # solo API + carga en Docker local
|
||||
# ./build_factuges.sh acme --web # solo web
|
||||
# ./build_factuges.sh acme # API + web (por defecto)
|
||||
#
|
||||
# Funcionalidades:
|
||||
# - Detecta automáticamente el nombre, versión y puerto de la API
|
||||
@ -27,12 +26,26 @@ SCRIPT_VERSION="1.0.5"
|
||||
# =====================================================
|
||||
|
||||
# --- Configuración base ---
|
||||
COMPANY="${1:-}"
|
||||
COMPANY=""
|
||||
MODE="all" # api | web | all
|
||||
LOAD=false
|
||||
|
||||
# --- Validar que el primer argumento existe y no es un flag ---
|
||||
if [[ $# -eq 0 || "$1" == --* ]]; then
|
||||
echo "❌ ERROR: Falta el parámetro <company>"
|
||||
echo "Uso: ./build_factuges.sh <company> [--api|--web|--all] [--load]"
|
||||
echo "Ejemplos:"
|
||||
echo " ./build_factuges.sh acme --api"
|
||||
echo " ./build_factuges.sh acme --web"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
COMPANY="$1"
|
||||
|
||||
# --- Parseo de flags ---
|
||||
for arg in "${@:2}"; do
|
||||
shift # quitamos el <company>, ahora solo quedan flags
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--api) MODE="api" ;;
|
||||
--web) MODE="web" ;;
|
||||
@ -51,7 +64,12 @@ OUT_API_DIR="${PROJECT_DIR}/out/${COMPANY}/api"
|
||||
OUT_WEB_DIR="${PROJECT_DIR}/out/${COMPANY}/web"
|
||||
|
||||
if [[ -z "$COMPANY" ]]; then
|
||||
echo "❌ Error: debes indicar la compañía. Ejemplo: ./scripts/build-api.sh acme [--api|--web|--all] [--load]"
|
||||
echo "❌ Error: debes indicar la compañía. Ejemplo: ./build_factuges.sh acme [--api|--web|--all] [--load]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ $COMPANY =~ --.* ]]; then
|
||||
echo "❌ Error: debes indicar la compañía. Ejemplo: ./build_factuges.sh acme [--api|--web|--all] [--load]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@ -94,8 +112,6 @@ rm -rf "${OUT_API_DIR:?}/"*
|
||||
rm -rf "${OUT_WEB_DIR:?}/"*
|
||||
|
||||
|
||||
echo ""
|
||||
echo ""
|
||||
echo ""
|
||||
echo "-------------------------------------------------------"
|
||||
echo " FACTUGES Build Script v${SCRIPT_VERSION}"
|
||||
@ -120,7 +136,7 @@ if [[ "$MODE" == "web" || "$MODE" == "all" ]]; then
|
||||
|
||||
# Puedes pasar variables específicas por compañía
|
||||
# Ejemplo: VITE_COMPANY=acme VITE_API_BASE=https://acme.localhost/api
|
||||
VITE_COMPANY="${COMPANY}" pnpm build
|
||||
VITE_COMPANY="${COMPANY}" pnpm build:${COMPANY}
|
||||
|
||||
# Carpeta versionada
|
||||
VERSION_DIR="${OUT_WEB_DIR}/versions/v${WEB_VERSION}-${DATE}"
|
||||
@ -156,11 +172,14 @@ fi
|
||||
# 2️⃣ API
|
||||
# =====================================================
|
||||
if [[ "$MODE" == "api" || "$MODE" == "all" ]]; then
|
||||
# Recopilar plantillas
|
||||
${SCRIPT_DIR}/build-templates.sh
|
||||
|
||||
cd "${PROJECT_DIR}"
|
||||
echo "🐳 Construyendo imagen Docker..."
|
||||
|
||||
docker build --no-cache --debug -t "${IMAGE_TAG_V}" -t "${IMAGE_TAG_LATEST}" \
|
||||
--build-arg PORT="${PORT}" \
|
||||
--build-arg PORT="${PORT}" --build-arg COMPANY="${COMPANY}" \
|
||||
-f "${PROJECT_DIR}/Dockerfile" "${PROJECT_DIR}"
|
||||
|
||||
echo "✅ Imagen Docker construida correctamente"
|
||||
@ -199,9 +218,14 @@ EOF
|
||||
echo "📦 API manifest generado en ${OUT_API_DIR}/manifest-v${API_VERSION}-${DATE}.json"
|
||||
|
||||
if [[ "$LOAD" == true ]]; then
|
||||
echo "📥 Cargando imagen en Docker local..."
|
||||
docker load -i "${TAR_FILE_V}"
|
||||
echo "✅ Imagen cargada en Docker local"
|
||||
echo "📥 Cargando imagen en producción vps-2.rodax-software.com..."
|
||||
[[ "$MODE" == "web" || "$MODE" == "all" ]] && scp -r -P 49152 ${OUT_WEB_DIR} rodax@vps-2.rodax-software.com:/opt/factuges/${COMPANY}/
|
||||
[[ "$MODE" == "api" || "$MODE" == "all" ]] && scp -r -P 49152 ${OUT_API_DIR} rodax@vps-2.rodax-software.com:/opt/factuges/${COMPANY}/
|
||||
|
||||
[[ "$MODE" == "api" || "$MODE" == "all" ]] && RESULT=$(ssh -p 49152 rodax@vps-2.rodax-software.com "docker load -i /opt/factuges/${COMPANY}/api/${TAR_FILE_LATEST}")
|
||||
[[ "$MODE" == "api" || "$MODE" == "all" ]] && echo $RESULT
|
||||
#docker load -i "${TAR_FILE_V}"
|
||||
echo "✅ Imagen cargada en producción"
|
||||
fi
|
||||
fi
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Fail fast
|
||||
set -euo pipefail
|
||||
|
||||
echo "-------------------------------------------------------"
|
||||
echo "[build-templates] Recopilando plantillas del proyecto..."
|
||||
|
||||
# Root directory (dir where the script lives, then go up)
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
|
||||
@ -28,4 +29,5 @@ for module in "$SOURCE_DIR"/*; do
|
||||
fi
|
||||
done
|
||||
|
||||
echo "[build-templates] Completed."
|
||||
echo "✅ [build-templates] Terminado."
|
||||
echo "-------------------------------------------------------"
|
||||
78
scripts/docker-compose.caddymanager.yml
Normal file
78
scripts/docker-compose.caddymanager.yml
Normal file
@ -0,0 +1,78 @@
|
||||
services:
|
||||
# MongoDB database for persistent storage (optional - SQLite is used by default)
|
||||
mongodb:
|
||||
image: mongo:8.0
|
||||
container_name: caddymanager-mongodb
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- MONGO_INITDB_ROOT_USERNAME=mongoadmin
|
||||
- MONGO_INITDB_ROOT_PASSWORD=someSecretPassword # Change for production!
|
||||
ports:
|
||||
- "27017:27017" # Expose for local dev, remove for production
|
||||
volumes:
|
||||
- mongodb_data:/data/db
|
||||
networks:
|
||||
- caddymanager
|
||||
profiles:
|
||||
- mongodb # Use 'docker-compose --profile mongodb up' to include MongoDB
|
||||
|
||||
# Backend API server
|
||||
backend:
|
||||
image: caddymanager/caddymanager-backend:latest
|
||||
container_name: caddymanager-backend
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- PORT=3000
|
||||
# Database Engine Configuration (defaults to SQLite)
|
||||
- DB_ENGINE=sqlite # Options: 'sqlite' or 'mongodb'
|
||||
# SQLite Configuration (used when DB_ENGINE=sqlite)
|
||||
- SQLITE_DB_PATH=/app/data/caddymanager.sqlite
|
||||
# MongoDB Configuration (used when DB_ENGINE=mongodb)
|
||||
- MONGODB_URI=mongodb://mongoadmin:someSecretPassword@mongodb:27017/caddymanager?authSource=admin
|
||||
- CORS_ORIGIN=http://localhost:80
|
||||
- LOG_LEVEL=debug
|
||||
- CADDY_SANDBOX_URL=http://localhost:2019
|
||||
- PING_INTERVAL=30000
|
||||
- PING_TIMEOUT=2000
|
||||
- AUDIT_LOG_MAX_SIZE_MB=100
|
||||
- AUDIT_LOG_RETENTION_DAYS=90
|
||||
- METRICS_HISTORY_MAX=1000 # Optional: max number of in-memory metric history snapshots to keep
|
||||
- JWT_SECRET=your_jwt_secret_key_here # Change for production!
|
||||
- JWT_EXPIRATION=24h
|
||||
# Backend is now only accessible through frontend proxy
|
||||
volumes:
|
||||
- sqlite_data:/app/data # SQLite database storage
|
||||
networks:
|
||||
- caddymanager
|
||||
|
||||
# Frontend web UI
|
||||
frontend:
|
||||
image: caddymanager/caddymanager-frontend:latest
|
||||
container_name: caddymanager-frontend
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- backend
|
||||
environment:
|
||||
- BACKEND_HOST=backend:3000
|
||||
- APP_NAME=Caddy Manager
|
||||
- DARK_MODE=true
|
||||
ports:
|
||||
- "80:80" # Expose web UI
|
||||
networks:
|
||||
- caddymanager
|
||||
|
||||
networks:
|
||||
caddymanager:
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
mongodb_data: # Only used when MongoDB profile is active
|
||||
sqlite_data: # SQLite database storage
|
||||
|
||||
# Notes:
|
||||
# - SQLite is the default database engine - no additional setup required!
|
||||
# - To use MongoDB instead, set DB_ENGINE=mongodb and start with: docker-compose --profile mongodb up
|
||||
# - For production, use strong passwords and consider secrets management.
|
||||
# - The backend uses SQLite by default, storing data in a persistent volume.
|
||||
# - The frontend proxies all /api/* requests to the backend service.
|
||||
# - Backend is not directly exposed - all API access goes through the frontend proxy.
|
||||
115
scripts/docker-compose.old
Normal file
115
scripts/docker-compose.old
Normal file
@ -0,0 +1,115 @@
|
||||
|
||||
# ======================================================
|
||||
# STACK POR COMPAÑÍA
|
||||
# - API Node.js
|
||||
# - Web React (Nginx)
|
||||
# - MariaDB
|
||||
# - Integrado con Traefik
|
||||
# ======================================================
|
||||
|
||||
|
||||
services:
|
||||
# --- Base de datos MariaDB ---
|
||||
db:
|
||||
image: mariadb:lts-noble
|
||||
container_name: factuges_${COMPANY}_db
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASS}
|
||||
MARIADB_USER: ${DB_USER}
|
||||
MARIADB_PASSWORD: ${DB_PASS}
|
||||
MARIADB_DATABASE: ${DB_NAME}
|
||||
volumes:
|
||||
- /opt/factuges/${COMPANY}/volumes/db_data:/var/lib/mysql
|
||||
networks:
|
||||
- internal
|
||||
- edge
|
||||
ports:
|
||||
- 3306:3306
|
||||
healthcheck:
|
||||
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
|
||||
interval: 20s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
|
||||
phpmyadmin:
|
||||
image: phpmyadmin/phpmyadmin
|
||||
container_name: factuges_${COMPANY}_phpmyadmin
|
||||
restart: always
|
||||
environment:
|
||||
PMA_HOST: db
|
||||
PMA_USER: ${DB_USER}
|
||||
PMA_PASSWORD: ${DB_PASS}
|
||||
PMA_VERBOSES: "FactuGES Rodax"
|
||||
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASS}
|
||||
UPLOAD_LIMIT: 64M
|
||||
networks:
|
||||
- internal
|
||||
- edge
|
||||
depends_on:
|
||||
- db
|
||||
ports:
|
||||
- 8080:80
|
||||
labels:
|
||||
traefik.enable: "true"
|
||||
traefik.http.routers.factuges_rodax_phpmyadmin.rule: Host(`phpmyadmin.${DOMAIN}`)
|
||||
traefik.http.routers.factuges_rodax_phpmyadmin.entrypoints: web
|
||||
traefik.http.services.factuges_rodax_phpmyadmin.loadbalancer.server.port: "80"
|
||||
|
||||
# --- API (imagen versionada generada por build-factuges.sh) ---
|
||||
api:
|
||||
image: ${API_IMAGE}
|
||||
container_name: factuges_${COMPANY}_api
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
NODE_ENV: production
|
||||
COMPANY: ${COMPANY}
|
||||
PORT: ${SERVER_PORT:-3002}
|
||||
DB_DIALECT: "mysql"
|
||||
DB_HOST: "db"
|
||||
DB_PORT: ${DB_PORT}
|
||||
DB_NAME: ${DB_NAME}
|
||||
DB_USER: ${DB_USER}
|
||||
DB_PASSWORD: ${DB_PASS}
|
||||
FRONTEND_URL: ${FRONTEND_URL}
|
||||
networks:
|
||||
- internal
|
||||
- edge
|
||||
ports:
|
||||
- ${SERVER_PORT:-3002}:${SERVER_PORT:-3002}
|
||||
labels:
|
||||
traefik.enable: "true"
|
||||
traefik.http.routers.factuges_rodax_api.rule: Host(`${DOMAIN}`) && PathPrefix(`/api`)
|
||||
traefik.http.routers.factuges_rodax_api.entrypoints: web
|
||||
traefik.http.services.factuges_rodax_api.loadbalancer.server.port: "${API_PORT:-3002}"
|
||||
|
||||
# --- Web estática (React compilado por build-factuges.sh) ---
|
||||
web:
|
||||
image: nginx:1.27-alpine
|
||||
container_name: factuges_${COMPANY}_web
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- api
|
||||
volumes:
|
||||
- /opt/factuges/${COMPANY}/web/versions/${WEB_VERSION}/dist:/usr/share/nginx/html:ro
|
||||
networks:
|
||||
- internal
|
||||
- edge
|
||||
labels:
|
||||
traefik.enable: "true"
|
||||
traefik.http.routers.factuges_rodax_web.rule: Host(`${DOMAIN}`)
|
||||
traefik.http.routers.factuges_rodax_web.entrypoints: web
|
||||
traefik.http.services.factuges_rodax_web.loadbalancer.server.port: "80"
|
||||
|
||||
networks:
|
||||
edge:
|
||||
external: true # red pública manejada por Traefik
|
||||
internal:
|
||||
driver: bridge # red privada de la compañía
|
||||
|
||||
volumes:
|
||||
db_data:
|
||||
|
||||
133
scripts/docker-compose.yml
Normal file
133
scripts/docker-compose.yml
Normal file
@ -0,0 +1,133 @@
|
||||
|
||||
# ======================================================
|
||||
# STACK POR COMPAÑÍA
|
||||
# - API Node.js
|
||||
# - Web React (Nginx)
|
||||
# - MariaDB
|
||||
# - Integrado con Traefik
|
||||
# ======================================================
|
||||
|
||||
|
||||
services:
|
||||
# --- Base de datos MariaDB ---
|
||||
db:
|
||||
image: mariadb:lts-noble
|
||||
container_name: factuges_${COMPANY}_db
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASS}
|
||||
MARIADB_USER: ${DB_USER}
|
||||
MARIADB_PASSWORD: ${DB_PASS}
|
||||
MARIADB_DATABASE: ${DB_NAME}
|
||||
volumes:
|
||||
- /opt/factuges/${COMPANY}/volumes/db_data:/var/lib/mysql
|
||||
networks:
|
||||
- internal
|
||||
- edge
|
||||
ports:
|
||||
- 3306:3306
|
||||
healthcheck:
|
||||
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
|
||||
interval: 20s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
|
||||
phpmyadmin:
|
||||
image: phpmyadmin/phpmyadmin
|
||||
container_name: factuges_${COMPANY}_phpmyadmin
|
||||
restart: always
|
||||
environment:
|
||||
PMA_HOST: db
|
||||
PMA_USER: ${DB_USER}
|
||||
PMA_PASSWORD: ${DB_PASS}
|
||||
PMA_VERBOSES: "FactuGES Rodax"
|
||||
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASS}
|
||||
UPLOAD_LIMIT: 64M
|
||||
networks:
|
||||
- internal
|
||||
- edge
|
||||
depends_on:
|
||||
- db
|
||||
ports:
|
||||
- 8080:80
|
||||
labels:
|
||||
traefik.enable: "true"
|
||||
# Router
|
||||
traefik.http.routers.${COMPANY}-phpmyadmin.rule: Host(`${PMA_DOMAIN}`)
|
||||
traefik.http.routers.${COMPANY}-phpmyadmin.entrypoints: websecure
|
||||
traefik.http.routers.${COMPANY}-phpmyadmin.tls.certresolver: cfresolver
|
||||
# Servicio
|
||||
traefik.http.services.${COMPANY}-phpmyadmin.loadbalancer.server.port: "80"
|
||||
# Middleware: whitelist por IP
|
||||
traefik.http.routers.${COMPANY}-phpmyadmin.middlewares: "${COMPANY}-phpmyadmin-ipwhitelist@docker"
|
||||
traefik.http.middlewares.${COMPANY}-phpmyadmin-ipwhitelist.ipwhitelist.sourcerange: "79.116.183.41/32"
|
||||
|
||||
# --- API (imagen versionada generada por build-factuges.sh) ---
|
||||
api:
|
||||
image: ${API_IMAGE}
|
||||
container_name: factuges_${COMPANY}_api
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
NODE_ENV: production
|
||||
COMPANY: ${COMPANY}
|
||||
PORT: ${SERVER_PORT:-3002}
|
||||
DB_DIALECT: "mysql"
|
||||
DB_HOST: "db"
|
||||
DB_PORT: ${DB_PORT}
|
||||
DB_NAME: ${DB_NAME}
|
||||
DB_USER: ${DB_USER}
|
||||
DB_PASSWORD: ${DB_PASS}
|
||||
FRONTEND_URL: ${FRONTEND_URL}
|
||||
networks:
|
||||
- internal
|
||||
- edge
|
||||
ports:
|
||||
- ${SERVER_PORT:-3002}:${SERVER_PORT:-3002}
|
||||
labels:
|
||||
traefik.enable: "true"
|
||||
# Router
|
||||
traefik.http.routers.${COMPANY}-api.rule: Host(`${API_DOMAIN}`)
|
||||
traefik.http.routers.${COMPANY}-api.entrypoints: websecure
|
||||
traefik.http.routers.${COMPANY}-api.tls.certresolver: cfresolver
|
||||
# Servicio
|
||||
traefik.http.services.${COMPANY}-api.loadbalancer.server.port: "${SERVER_PORT:-3002}"
|
||||
|
||||
# --- Web estática (React compilado por build-factuges.sh) ---
|
||||
web:
|
||||
container_name: caddy
|
||||
image: caddy:alpine
|
||||
volumes:
|
||||
- /opt/factuges/${COMPANY}/Caddyfile:/etc/caddy/Caddyfile # Monta el archivo de configuración
|
||||
- caddy_data:/data # Almacena los certificados en este volumen
|
||||
- caddy_config:/config # Configuración de Caddy
|
||||
- /opt/factuges/${COMPANY}/web/latest/dist/:/srv
|
||||
ports:
|
||||
- 81:80 # Puerto HTTP (Caddy lo redirige automáticamente a HTTPS)
|
||||
- 444:443 # Puerto HTTPS
|
||||
- 13001:13001 # reverse proxy al backend
|
||||
networks:
|
||||
- internal
|
||||
- edge
|
||||
restart: on-failure
|
||||
depends_on:
|
||||
- api
|
||||
labels:
|
||||
traefik.enable: "true"
|
||||
traefik.http.routers.factuges_rodax_web.rule: Host(`${DOMAIN}`)
|
||||
traefik.http.routers.factuges_rodax_web.entrypoints: web
|
||||
traefik.http.services.factuges_rodax_web.loadbalancer.server.port: "444"
|
||||
|
||||
networks:
|
||||
edge:
|
||||
external: true # red pública manejada por Traefik
|
||||
internal:
|
||||
driver: bridge # red privada de la compañía
|
||||
|
||||
volumes:
|
||||
db_data:
|
||||
caddy_data:
|
||||
caddy_config:
|
||||
|
||||
14
scripts/stack.env
Normal file
14
scripts/stack.env
Normal file
@ -0,0 +1,14 @@
|
||||
COMPANY=rodax
|
||||
DOMAIN=rodax.factuges.rodax-software.local
|
||||
FRONTEND_URL=rodax.factuges.rodax-software.local
|
||||
WEB_VERSION=v0.0.4-latest
|
||||
API_IMAGE=factuges-server:rodax-latest
|
||||
SERVER_PORT=3002
|
||||
DB_HOST=db
|
||||
DB_DIALECT=mysql
|
||||
DB_PORT=3306
|
||||
DB_USER=rodax_usr
|
||||
DB_PASS=supersecret
|
||||
DB_NAME=rodax_db
|
||||
DB_ROOT_PASS=verysecret
|
||||
TRAEFIK_ENTRYPOINT=web
|
||||
Loading…
Reference in New Issue
Block a user