Dockers
This commit is contained in:
parent
d6c5d067cb
commit
cecba33ebb
2
.gitignore
vendored
2
.gitignore
vendored
@ -9,7 +9,7 @@ dist-ssr
|
||||
.cache
|
||||
server/dist
|
||||
public/dist
|
||||
|
||||
out
|
||||
|
||||
apps/**/false/*
|
||||
false
|
||||
|
||||
94
Dockerfile
Normal file
94
Dockerfile
Normal file
@ -0,0 +1,94 @@
|
||||
# syntax=docker/dockerfile:1.7-labs
|
||||
|
||||
ARG NODE_IMAGE=node:22-alpine
|
||||
ARG PNPM_VERSION=10.20.0
|
||||
|
||||
########################
|
||||
# 0) Base común
|
||||
########################
|
||||
FROM ${NODE_IMAGE} AS base
|
||||
ENV CI=1 \
|
||||
NODE_ENV=production
|
||||
RUN corepack enable && corepack prepare pnpm@${PNPM_VERSION} --activate
|
||||
|
||||
WORKDIR /repo
|
||||
|
||||
########################
|
||||
# 1) Turborepo prune (reduce contexto)
|
||||
########################
|
||||
FROM base AS pruner
|
||||
|
||||
# Copiar archivos raíz necesarios
|
||||
COPY turbo.json package.json pnpm-workspace.yaml pnpm-lock.yaml ./
|
||||
|
||||
# Copiar los directorios relevantes del monorepo
|
||||
COPY packages ./packages
|
||||
COPY apps ./apps
|
||||
COPY modules ./modules
|
||||
|
||||
# Ejecuta prune para @erp/factuges-server
|
||||
RUN npx --yes turbo@2.5.8 prune @erp/factuges-server --docker
|
||||
|
||||
########################
|
||||
# 2) Instalar deps de la parte pruned (usando pnpm fetch para cache)
|
||||
########################
|
||||
#FROM base AS installer
|
||||
|
||||
# Traemos lockfile y pnpm-store desde la etapa prune
|
||||
#COPY --from=pruner /repo/out/json/ ./
|
||||
#COPY --from=pruner /repo/out/pnpm-lock.yaml ./pnpm-lock.yaml
|
||||
|
||||
# Prefetch a store global (rápido y cacheable)
|
||||
#RUN pnpm fetch
|
||||
#RUN pnpm install --frozen-lockfile
|
||||
|
||||
########################
|
||||
# 3) Builder: código + link deps y build
|
||||
########################
|
||||
FROM base AS builder
|
||||
|
||||
# Copiamos todo lo “pruned” (json, lock, full)
|
||||
COPY --from=pruner /repo/out/full/ ./
|
||||
COPY --from=pruner /repo/out/pnpm-lock.yaml ./pnpm-lock.yaml
|
||||
|
||||
# Reutilizamos la store prefetch
|
||||
#COPY --from=installer /root/.local/share/pnpm/store /root/.local/share/pnpm/store
|
||||
|
||||
# Instalamos solo lo necesario para prod de los workspaces
|
||||
RUN pnpm install --frozen-lockfile --prefer-offline
|
||||
|
||||
# IMPORTANTE: build de paquetes buildables (utils, logger, etc.) primero
|
||||
# Turborepo asegura orden con "dependsOn". Un único comando:
|
||||
RUN pnpm -w turbo run build --filter=@erp/factuges-server...
|
||||
|
||||
########################
|
||||
# 4) Runner minimal
|
||||
########################
|
||||
#FROM ${NODE_IMAGE} AS runner
|
||||
FROM base AS runner
|
||||
ENV NODE_ENV=production TZ=UTC
|
||||
|
||||
# Don't run production as root
|
||||
RUN addgroup --system --gid 1001 expressjs
|
||||
RUN adduser --system --uid 1001 expressjs
|
||||
USER expressjs
|
||||
|
||||
#COPY --from=builder /repo .
|
||||
COPY --from=builder /repo/node_modules ./node_modules
|
||||
COPY --from=builder /repo/packages ./packages
|
||||
COPY --from=builder /repo/package.json ./package.json
|
||||
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/node_modules ./apps/server/node_modules
|
||||
COPY --from=builder /repo/apps/server/package.json ./apps/server/package.json
|
||||
|
||||
|
||||
# Salud del contenedor (ajusta puerto/endpoint)
|
||||
#HEALTHCHECK --interval=20s --timeout=3s --retries=5 \
|
||||
# CMD node -e "fetch('http://127.0.0.1:'+(process.env.PORT||3002)+'/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"
|
||||
|
||||
EXPOSE 3002
|
||||
CMD ["pnpm","exec", "node", "--env-file=apps/server/dist/.env", "apps/server/dist/index.js"]
|
||||
@ -1,7 +1,7 @@
|
||||
NODE_ENV=development
|
||||
NODE_ENV=production
|
||||
HOST=0.0.0.0
|
||||
PORT=3002
|
||||
FRONTEND_URL=http://localhost:5173
|
||||
FRONTEND_URL=http://factuges.rodax-software.local
|
||||
|
||||
|
||||
DB_DIALECT=mysql
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
# server/Dockerfile
|
||||
FROM node:22.13.1
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
COPY package*.json ./
|
||||
RUN npm install --production
|
||||
|
||||
COPY . .
|
||||
|
||||
EXPOSE 5000
|
||||
|
||||
CMD ["npm", "start"]
|
||||
@ -1,10 +1,7 @@
|
||||
{
|
||||
"name": "@erp/server",
|
||||
"version": "0.0.1",
|
||||
"name": "@erp/factuges-server",
|
||||
"version": "0.0.3",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "tsup src/index.ts --config tsup.config.ts",
|
||||
"dev": "tsx watch src/index.ts",
|
||||
|
||||
@ -2,13 +2,13 @@ import { DateTime } from "luxon";
|
||||
import http from "node:http";
|
||||
import os from "node:os";
|
||||
import { z } from "zod/v4";
|
||||
import { createApp } from "./app";
|
||||
import { ENV } from "./config";
|
||||
import { tryConnectToDatabase } from "./config/database";
|
||||
import { registerHealthRoutes } from "./health";
|
||||
import { listRoutes, logger } from "./lib";
|
||||
import { initModules } from "./lib/modules";
|
||||
import { registerModules } from "./register-modules";
|
||||
import { createApp } from "./app.ts";
|
||||
import { tryConnectToDatabase } from "./config/database.ts";
|
||||
import { ENV } from "./config/index.ts";
|
||||
import { registerHealthRoutes } from "./health.ts";
|
||||
import { listRoutes, logger } from "./lib/index.ts";
|
||||
import { initModules } from "./lib/modules/index.ts";
|
||||
import { registerModules } from "./register-modules.ts";
|
||||
|
||||
const API_BASE_PATH = "/api/v1";
|
||||
|
||||
@ -208,9 +208,23 @@ process.on("uncaughtException", async (error: Error) => {
|
||||
const now = DateTime.now();
|
||||
logger.info(`Time: ${now.toLocaleString(DateTime.DATETIME_FULL)} ${now.zoneName}`);
|
||||
logger.info(`Launched in: ${now.diff(currentState.launchedAt).toMillis()} ms`);
|
||||
logger.info(`Environment: ${currentState.environment}`);
|
||||
logger.info(`Process PID: ${process.pid}`);
|
||||
|
||||
// Mostrar variables de entorno
|
||||
logger.info(`Environment: ${currentState.environment}`);
|
||||
logger.info(`HOST: ${ENV.HOST}`);
|
||||
logger.info(`PORT: ${ENV.PORT}`);
|
||||
logger.info(`API_BASE_PATH: ${API_BASE_PATH}`);
|
||||
|
||||
logger.info(`FRONTEND_URL: ${ENV.FRONTEND_URL}`);
|
||||
|
||||
logger.info(`DB_HOST: ${ENV.DB_HOST}`);
|
||||
logger.info(`DB_PORT: ${ENV.DB_PORT}`);
|
||||
logger.info(`DB_NAME: ${ENV.DB_NAME}`);
|
||||
logger.info(`DB_USER: ${ENV.DB_USER}`);
|
||||
logger.info(`DB_LOGGING: ${ENV.DB_LOGGING}`);
|
||||
logger.info(`DB_SYNC_MODE: ${ENV.DB_SYNC_MODE}`);
|
||||
|
||||
const database = await tryConnectToDatabase();
|
||||
|
||||
// Lógica de inicialización de DB, si procede:
|
||||
@ -219,7 +233,7 @@ process.on("uncaughtException", async (error: Error) => {
|
||||
|
||||
await initModules({ app, database, baseRoutePath: API_BASE_PATH, logger });
|
||||
|
||||
// ✅ El servidor ya está listo para recibir tráfico
|
||||
// El servidor ya está listo para recibir tráfico
|
||||
isReady = true;
|
||||
logger.info("✅ Server is READY (readiness=true)");
|
||||
logger.info(`startup_duration_ms=${DateTime.now().diff(currentState.launchedAt).toMillis()}`);
|
||||
|
||||
@ -2,13 +2,19 @@
|
||||
"extends": "@repo/typescript-config/express.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"noEmit": true,
|
||||
"allowImportingTsExtensions": true,
|
||||
"lib": ["ES2022"],
|
||||
"baseUrl": "./src",
|
||||
"paths": {
|
||||
"@/*": ["*"]
|
||||
},
|
||||
"outDir": "dist"
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
|
||||
"removeComments": true
|
||||
},
|
||||
//"files": ["src/index.ts"], // Esta opción compila sólo los archivos listados (y sus dependencias importadas).
|
||||
"include": ["src"],
|
||||
"exclude": ["src/**/__tests__/*", "src/**/*.mock.*", "src/**/*.test.*", "node_modules", "dist"]
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { defineConfig } from "tsup";
|
||||
|
||||
/**
|
||||
@ -10,23 +12,62 @@ import { defineConfig } from "tsup";
|
||||
export default defineConfig({
|
||||
entry: ["src/index.ts"], // punto de entrada principal
|
||||
format: ["esm"], // ESM nativo (Node 18+)
|
||||
target: "node18", // objetivo de compilación
|
||||
sourcemap: true,
|
||||
target: "node22", // objetivo de compilación
|
||||
bundle: true, // genera un único bundle, evita imports rotos
|
||||
sourcemap: false,
|
||||
clean: true,
|
||||
minify: false,
|
||||
treeshake: true,
|
||||
dts: false, // opcional, genera .d.ts
|
||||
outDir: "dist",
|
||||
platform: "node",
|
||||
|
||||
// Paquetes de npm a mantener como externos (no se incluyen)
|
||||
external: ["express", "zod", "react", "react-dom", "date-fns"],
|
||||
// Paquetes de npm a mantener como externos (no se incluyen en el "bundle")
|
||||
external: [],
|
||||
|
||||
// Paquetes internos buildless que se deben incluir en el bundle
|
||||
noExternal: [
|
||||
"@erp/auth",
|
||||
"@erp/core",
|
||||
"@erp/customers",
|
||||
"@erp/customer-invoices",
|
||||
"@erp/verifactu",
|
||||
"@repo/rdx-logger",
|
||||
noExternal: ["@repo", "@erp"],
|
||||
|
||||
esbuildOptions(options) {
|
||||
// Permite resolver imports sin extensión (.ts, .js, .mjs, etc.)
|
||||
options.resolveExtensions = [".ts", ".js", ".mjs", ".json"];
|
||||
|
||||
// Corrige la extensión de salida
|
||||
options.outExtension = { ".js": ".js" };
|
||||
|
||||
// Permite usar require en contexto ESM (Node >=18)
|
||||
options.banner = {
|
||||
js: `
|
||||
import { createRequire } from "module";
|
||||
const require = createRequire(import.meta.url);
|
||||
`,
|
||||
};
|
||||
},
|
||||
|
||||
// Plugin: fuerza a que los imports relativos se traten como locales, no "external"
|
||||
esbuildPlugins: [
|
||||
{
|
||||
name: "fix-local-imports",
|
||||
setup(build) {
|
||||
build.onResolve({ filter: /^\.{1,2}\// }, (args) => {
|
||||
const abs = path.resolve(args.resolveDir, args.path);
|
||||
|
||||
// Si es un directorio, intenta resolver a index.(ts|js)
|
||||
if (fs.existsSync(abs) && fs.statSync(abs).isDirectory()) {
|
||||
const indexTs = path.join(abs, "index.ts");
|
||||
const indexJs = path.join(abs, "index.js");
|
||||
if (fs.existsSync(indexTs)) return { path: indexTs, external: false };
|
||||
if (fs.existsSync(indexJs)) return { path: indexJs, external: false };
|
||||
}
|
||||
|
||||
// Si es un fichero con extensión válida
|
||||
const withExts = [".ts", ".js", ".mjs"];
|
||||
for (const ext of withExts) {
|
||||
if (fs.existsSync(abs + ext)) return { path: abs + ext, external: false };
|
||||
}
|
||||
|
||||
return { path: abs, external: false };
|
||||
});
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@erp/web",
|
||||
"name": "@erp/factuges-web",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.3",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --host --clearScreen false",
|
||||
|
||||
@ -1,31 +0,0 @@
|
||||
name: factuges
|
||||
|
||||
services:
|
||||
mariadb:
|
||||
image: mariadb:latest
|
||||
container_name: mariadb
|
||||
restart: always
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: rootpass
|
||||
MYSQL_DATABASE: factuges_db
|
||||
MYSQL_USER: factuges_usr
|
||||
MYSQL_PASSWORD: factuges_pass
|
||||
volumes:
|
||||
- mariadb_data:/var/lib/mysql
|
||||
ports:
|
||||
- "3306:3306"
|
||||
|
||||
phpmyadmin:
|
||||
image: phpmyadmin/phpmyadmin
|
||||
container_name: phpmyadmin
|
||||
restart: always
|
||||
environment:
|
||||
PMA_HOST: mariadb
|
||||
MYSQL_ROOT_PASSWORD: rootpass
|
||||
ports:
|
||||
- "8080:80"
|
||||
depends_on:
|
||||
- mariadb
|
||||
|
||||
volumes:
|
||||
mariadb_data:
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@erp/auth",
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.3",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@erp/core",
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.3",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@erp/customer-invoices",
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.3",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
|
||||
@ -10,7 +10,10 @@ export enum INVOICE_STATUS {
|
||||
SENT = "sent", // <- Proforma
|
||||
APPROVED = "approved", // <- Proforma
|
||||
REJECTED = "rejected", // <- Proforma
|
||||
ISSUED = "issued", // <- Factura y enviada a Veri*Factu
|
||||
|
||||
// issued <- (si is_proforma === true) => Es una proforma (histórica)
|
||||
// issued <- (si is_proforma === false) => Factura y enviada a Veri*Factu
|
||||
ISSUED = "issued",
|
||||
}
|
||||
export class CustomerInvoiceStatus extends ValueObject<ICustomerInvoiceStatusProps> {
|
||||
private static readonly ALLOWED_STATUSES = ["draft", "sent", "approved", "rejected", "issued"];
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@erp/customers",
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.3",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@erp/doc-numbering",
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.3",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@erp/verifactu",
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.3",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@repo/rdx-criteria",
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.3",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@repo/rdx-ddd",
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.3",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@repo/rdx-logger",
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.3",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@repo/rdx-utils",
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.3",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
|
||||
219
scripts/build-api.sh
Executable file
219
scripts/build-api.sh
Executable file
@ -0,0 +1,219 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_VERSION="1.0.3"
|
||||
|
||||
# =====================================================
|
||||
# FACTUGES Build Script
|
||||
# -----------------------------------------------------
|
||||
# Build + Export de la API y/o
|
||||
# compilación de la Web (por compañía)
|
||||
# =====================================================
|
||||
# Uso:
|
||||
# ./scripts/build-api.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)
|
||||
#
|
||||
# Funcionalidades:
|
||||
# - Detecta automáticamente el nombre, versión y puerto de la API
|
||||
# - Compila la web React (Vite)
|
||||
# - Copia los estáticos a out/<company>/web/dist
|
||||
# - Genera imagen Docker etiquetada por compañía + versión + latest
|
||||
# - Guarda la imagen .tar en out/<company>/api/
|
||||
# - Carga la imagen en Docker (opcional)
|
||||
# =====================================================
|
||||
|
||||
# --- Configuración base ---
|
||||
COMPANY="${1:-}"
|
||||
MODE="all" # api | web | all
|
||||
LOAD=false
|
||||
|
||||
# --- Parseo de flags ---
|
||||
for arg in "${@:2}"; do
|
||||
case "$arg" in
|
||||
--api) MODE="api" ;;
|
||||
--web) MODE="web" ;;
|
||||
--all) MODE="all" ;;
|
||||
--load) LOAD=true ;;
|
||||
*) echo "⚠️ Argumento desconocido: $arg" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# --- Paths base ---
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_DIR="$(realpath "${SCRIPT_DIR}/..")"
|
||||
SERVER_DIR="${PROJECT_DIR}/apps/server"
|
||||
WEB_DIR="${PROJECT_DIR}/apps/web"
|
||||
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]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- Verificaciones mínimas ---
|
||||
[[ -d "$SERVER_DIR" ]] || { echo "❌ Falta ${SERVER_DIR}"; exit 1; }
|
||||
[[ -d "$WEB_DIR" ]] || { echo "❌ Falta ${WEB_DIR}"; exit 1; }
|
||||
|
||||
|
||||
# --- Detectar nombre y versión de la API ---
|
||||
IMAGE_NAME=$(node -p "require('${SERVER_DIR}/package.json').name.replace(/^@.*\\//, '')" 2>/dev/null || echo "api")
|
||||
API_VERSION=$(node -p "require('${SERVER_DIR}/package.json').version" 2>/dev/null || echo "0.0.0")
|
||||
|
||||
# --- Detectar versión de la web ---
|
||||
WEB_VERSION=$(node -p "require('${WEB_DIR}/package.json').version" 2>/dev/null || echo "0.0.0")
|
||||
|
||||
# --- Detectar puerto ---
|
||||
PORT=""
|
||||
if [[ -f "${SERVER_DIR}/.env" ]]; then
|
||||
PORT=$(grep -E '^PORT=' "${SERVER_DIR}/.env" | cut -d'=' -f2 | tr -d '"')
|
||||
fi
|
||||
|
||||
if [[ -z "$PORT" && -f "${PROJECT_DIR}/Dockerfile" ]]; then
|
||||
PORT=$(grep -E '^EXPOSE ' "${PROJECT_DIR}/Dockerfile" | awk '{print $2}' | head -n1)
|
||||
fi
|
||||
|
||||
PORT="${PORT:-3002}" # valor por defecto
|
||||
|
||||
# --- 3. Etiquetas e información ---
|
||||
DATE=$(date +'%Y%m%d-%H%M%S')
|
||||
ISO_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
||||
USER_NAME=$(whoami)
|
||||
GIT_HASH=$(git -C "$PROJECT_DIR" rev-parse --short HEAD 2>/dev/null || echo "unknown")
|
||||
GIT_BRANCH=$(git -C "$PROJECT_DIR" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown")
|
||||
|
||||
IMAGE_TAG_V="${IMAGE_NAME}:${COMPANY}-v${API_VERSION}"
|
||||
IMAGE_TAG_LATEST="${IMAGE_NAME}:${COMPANY}-latest"
|
||||
|
||||
mkdir -p "$OUT_API_DIR" "$OUT_WEB_DIR"
|
||||
rm -rf "${OUT_API_DIR:?}/"*
|
||||
rm -rf "${OUT_WEB_DIR:?}/"*
|
||||
|
||||
|
||||
echo ""
|
||||
echo ""
|
||||
echo ""
|
||||
echo "-------------------------------------------------------"
|
||||
echo " FACTUGES Build Script v${SCRIPT_VERSION}"
|
||||
echo " Construyendo entorno para compañía '${COMPANY}'"
|
||||
echo " Proyecto: ${IMAGE_NAME}"
|
||||
echo " Versión API: ${API_VERSION}"
|
||||
echo " Versión Web: ${WEB_VERSION}"
|
||||
echo " Puerto API: ${PORT}"
|
||||
echo " Etiquetas: ${IMAGE_TAG_V}, ${IMAGE_TAG_LATEST}"
|
||||
echo " API out: ${OUT_API_DIR}"
|
||||
echo " WEB out: ${OUT_WEB_DIR}"
|
||||
echo " Modo: ${MODE}"
|
||||
echo " Cargar: ${LOAD}"
|
||||
echo "-------------------------------------------------------"
|
||||
|
||||
# =====================================================
|
||||
# 1️⃣ WEB
|
||||
# =====================================================
|
||||
if [[ "$MODE" == "web" || "$MODE" == "all" ]]; then
|
||||
echo "🌐 Compilando web con Vite..."
|
||||
cd "${WEB_DIR}"
|
||||
|
||||
# Puedes pasar variables específicas por compañía
|
||||
# Ejemplo: VITE_COMPANY=acme VITE_API_BASE=https://acme.localhost/api
|
||||
VITE_COMPANY="${COMPANY}" pnpm build
|
||||
|
||||
# Carpeta versionada
|
||||
VERSION_DIR="${OUT_WEB_DIR}/versions/v${WEB_VERSION}-${DATE}"
|
||||
mkdir -p "${VERSION_DIR}/dist"
|
||||
rm -rf "${VERSION_DIR}/dist"/*
|
||||
cp -r "${WEB_DIR}/dist/"* "${VERSION_DIR}/dist/"
|
||||
|
||||
# Enlace simbólico 'latest' → versión recién compilada
|
||||
ln -sfn "${VERSION_DIR}" "${OUT_WEB_DIR}/latest"
|
||||
|
||||
# Manifest JSON de la web
|
||||
cat > "${VERSION_DIR}/manifest.json" <<EOF
|
||||
{
|
||||
"type": "web",
|
||||
"company": "${COMPANY}",
|
||||
"version": "${WEB_VERSION}",
|
||||
"buildTime": "${ISO_DATE}",
|
||||
"user": "${USER_NAME}",
|
||||
"git": {
|
||||
"branch": "${GIT_BRANCH}",
|
||||
"commit": "${GIT_HASH}"
|
||||
},
|
||||
"source": "${PROJECT_DIR}/apps/web",
|
||||
"dist": "${VERSION_DIR}/dist"
|
||||
}
|
||||
EOF
|
||||
|
||||
echo "✅ Web v${WEB_VERSION} compilada y copiada a ${VERSION_DIR}"
|
||||
echo " 🔗 Enlace 'latest' actualizado a ${OUT_WEB_DIR}/latest"
|
||||
fi
|
||||
|
||||
# =====================================================
|
||||
# 2️⃣ API
|
||||
# =====================================================
|
||||
if [[ "$MODE" == "api" || "$MODE" == "all" ]]; then
|
||||
cd "${PROJECT_DIR}"
|
||||
echo "🐳 Construyendo imagen Docker..."
|
||||
|
||||
docker build --no-cache --debug -t "${IMAGE_TAG_V}" -t "${IMAGE_TAG_LATEST}" \
|
||||
--build-arg PORT="${PORT}" \
|
||||
-f "${PROJECT_DIR}/Dockerfile" "${PROJECT_DIR}"
|
||||
|
||||
echo "✅ Imagen Docker construida correctamente"
|
||||
|
||||
TAR_FILE_V="${OUT_API_DIR}/${IMAGE_NAME}-${COMPANY}-v${API_VERSION}-${DATE}.tar"
|
||||
TAR_FILE_LATEST="${OUT_API_DIR}/${IMAGE_NAME}-${COMPANY}-latest.tar"
|
||||
|
||||
docker save "${IMAGE_TAG_V}" "${IMAGE_TAG_LATEST}" -o "${TAR_FILE_V}"
|
||||
cp -f "${TAR_FILE_V}" "${TAR_FILE_LATEST}"
|
||||
|
||||
echo "📦 Imagen guardada:"
|
||||
echo " ${TAR_FILE_V}"
|
||||
echo " ${TAR_FILE_LATEST}"
|
||||
|
||||
# Manifest JSON de la API
|
||||
cat > "${OUT_API_DIR}/${IMAGE_NAME}-${COMPANY}-v${API_VERSION}-${DATE}-manifest.json" <<EOF
|
||||
{
|
||||
"type": "api",
|
||||
"company": "${COMPANY}",
|
||||
"image": "${IMAGE_TAG_V}",
|
||||
"version": "${API_VERSION}",
|
||||
"buildTime": "${ISO_DATE}",
|
||||
"user": "${USER_NAME}",
|
||||
"git": {
|
||||
"branch": "${GIT_BRANCH}",
|
||||
"commit": "${GIT_HASH}"
|
||||
},
|
||||
"port": "${PORT}",
|
||||
"files": {
|
||||
"versioned": "${TAR_FILE_V}",
|
||||
"latest": "${TAR_FILE_LATEST}"
|
||||
}
|
||||
}
|
||||
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"
|
||||
fi
|
||||
fi
|
||||
|
||||
# =====================================================
|
||||
# 3️⃣ Resumen
|
||||
# =====================================================
|
||||
echo "-------------------------------------------------------"
|
||||
echo "🎯 Resultado final para '${COMPANY}'"
|
||||
[[ "$MODE" == "web" || "$MODE" == "all" ]] && echo " 🌐 Web: v${WEB_VERSION} → ${OUT_WEB_DIR}/versions/v${WEB_VERSION}-${DATE}"
|
||||
[[ "$MODE" == "api" || "$MODE" == "all" ]] && echo " 🐳 API: v${API_VERSION} → ${OUT_API_DIR}"
|
||||
echo "🧩 Script version: ${SCRIPT_VERSION} - FIN"
|
||||
echo "-------------------------------------------------------"
|
||||
echo ""
|
||||
echo ""
|
||||
echo ""
|
||||
@ -1,4 +0,0 @@
|
||||
{
|
||||
"extends": "@repo/typescript-config/root.json",
|
||||
"include": ["apps", "modules", "packages"]
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user