Compare commits
2 Commits
220cc4c3a4
...
1783f630cf
| Author | SHA1 | Date | |
|---|---|---|---|
| 1783f630cf | |||
| 46379e8881 |
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 @@
|
||||
import { PageHeader, SimpleSearchInput } from "@erp/core/components";
|
||||
import { ErrorAlert } from "@erp/customers/components";
|
||||
import { AppContent, AppHeader, BackHistoryButton } from "@repo/rdx-ui/components";
|
||||
import { AppContent, AppHeader, BackHistoryButton, LogoVerifactu } from "@repo/rdx-ui/components";
|
||||
import {
|
||||
Alert,
|
||||
AlertDescription,
|
||||
@ -12,7 +12,7 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@repo/shadcn-ui/components";
|
||||
import { FilterIcon, LockIcon, PlusIcon } from "lucide-react";
|
||||
import { FilterIcon, PlusIcon } from "lucide-react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import { useTranslation } from "../../../../i18n";
|
||||
@ -63,6 +63,7 @@ export const IssuedInvoiceListPage = () => {
|
||||
rightSlot={
|
||||
<Button
|
||||
aria-label={t("pages.issued_invoices.create.title")}
|
||||
className="hidden"
|
||||
onClick={() => navigate("/issued-invoices/create")}
|
||||
>
|
||||
<PlusIcon aria-hidden className="mr-2 size-4" />
|
||||
@ -73,14 +74,16 @@ export const IssuedInvoiceListPage = () => {
|
||||
/>
|
||||
</AppHeader>
|
||||
<AppContent>
|
||||
<Alert className="bg-green-50 text-green-800">
|
||||
<LockIcon />
|
||||
<AlertTitle className="font-semibold text-green-800">¡Atención!</AlertTitle>
|
||||
<AlertDescription className="text-green-800">
|
||||
Las facturas de esta pantalla son de solo lectura. Se generan automáticamente al emitir
|
||||
una proforma aprobada y no se pueden modificar ni eliminar. Solo puedes descargar el PDF
|
||||
de cada factura.
|
||||
</AlertDescription>
|
||||
<Alert className="bg-green-50 text-green-800 flex items-center justify-between gap-6">
|
||||
<div>
|
||||
<AlertTitle className="font-semibold text-green-800">¡Atención!</AlertTitle>
|
||||
<AlertDescription className="text-green-800">
|
||||
Las facturas de esta pantalla son de solo lectura. Se generan automáticamente al
|
||||
emitir una proforma aprobada y no se pueden modificar ni eliminar. Solo puedes
|
||||
descargar el PDF de cada factura.
|
||||
</AlertDescription>
|
||||
</div>
|
||||
<LogoVerifactu className="text-green-800 w-50" color="" />
|
||||
</Alert>
|
||||
|
||||
{/* Search and filters */}
|
||||
|
||||
@ -228,7 +228,7 @@ export function useProformasGridColumns(
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-1">
|
||||
{!isIssued && (
|
||||
{!isIssued && actionHandlers.onEditClick && (
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
@ -248,7 +248,7 @@ export function useProformasGridColumns(
|
||||
)}
|
||||
|
||||
{/* Cambiar estado */}
|
||||
{!isIssued && availableTransitions.length && (
|
||||
{!isIssued && availableTransitions.length && actionHandlers.onChangeStatusClick && (
|
||||
<TooltipProvider key={availableTransitions[0]}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
@ -272,7 +272,7 @@ export function useProformasGridColumns(
|
||||
)}
|
||||
|
||||
{/* Emitir factura: solo si approved */}
|
||||
{!isIssued && proforma.status === "approved" && (
|
||||
{!isIssued && proforma.status === "approved" && actionHandlers.onIssueClick && (
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
@ -291,7 +291,7 @@ export function useProformasGridColumns(
|
||||
)}
|
||||
|
||||
{/* Eliminar */}
|
||||
{!isIssued && (
|
||||
{!isIssued && actionHandlers.onDeleteClick && (
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
|
||||
@ -37,10 +37,10 @@ export const ProformaListPage = () => {
|
||||
} = useProformaListPageController();
|
||||
|
||||
const columns = useProformasGridColumns({
|
||||
onEditClick: (proforma) => navigate(`/proformas/${proforma.id}/edit`),
|
||||
onIssueClick: handleIssueProforma,
|
||||
onDeleteClick: handleDeleteProforma,
|
||||
onChangeStatusClick: handleChangeStatusProforma,
|
||||
//onEditClick: (proforma) => navigate(`/proformas/${proforma.id}/edit`),
|
||||
//onIssueClick: handleIssueProforma,
|
||||
//onDeleteClick: handleDeleteProforma,
|
||||
//onChangeStatusClick: handleChangeStatusProforma,
|
||||
});
|
||||
|
||||
if (listCtrl.isError || !listCtrl.data) {
|
||||
@ -63,6 +63,7 @@ export const ProformaListPage = () => {
|
||||
rightSlot={
|
||||
<Button
|
||||
aria-label={t("pages.proformas.create.title")}
|
||||
className="hidden"
|
||||
onClick={() => navigate("/proformas/create")}
|
||||
>
|
||||
<PlusIcon aria-hidden className="mr-2 size-4" />
|
||||
|
||||
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -8,9 +8,9 @@ export * from "./full-screen-modal.tsx";
|
||||
export * from "./grid/index.ts";
|
||||
export * from "./layout/index.tsx";
|
||||
export * from "./loading-overlay/index.tsx";
|
||||
export * from "./logo-verifactu.tsx";
|
||||
export * from "./lookup-dialog/index.tsx";
|
||||
export * from "./multi-select.tsx";
|
||||
export * from "./multiple-selector.tsx";
|
||||
export * from "./scroll-to-top.tsx";
|
||||
export * from "./tailwind-indicator.tsx";
|
||||
|
||||
|
||||
85
packages/rdx-ui/src/components/logo-verifactu.tsx
Normal file
85
packages/rdx-ui/src/components/logo-verifactu.tsx
Normal file
@ -0,0 +1,85 @@
|
||||
export const LogoVerifactu = ({ color = "black", width = "100%", ...props }) => {
|
||||
return (
|
||||
<div {...props}>
|
||||
<svg
|
||||
{...props}
|
||||
height="auto"
|
||||
preserveAspectRatio="xMidYMid meet"
|
||||
style={{
|
||||
color,
|
||||
shapeRendering: "geometricPrecision",
|
||||
textRendering: "geometricPrecision",
|
||||
fillRule: "evenodd",
|
||||
clipRule: "evenodd",
|
||||
}}
|
||||
version="1.1"
|
||||
viewBox="0 0 947 250"
|
||||
width={width}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlnsXlink="http://www.w3.org/1999/xlink"
|
||||
>
|
||||
<title id="verifactu-title">Verifactu</title>
|
||||
<g>
|
||||
<path
|
||||
d="M 206.5,5.5 C 230.199,4.70303 238.699,15.703 232,38.5C 216.138,54.0286 200.638,69.8619 185.5,86C 237.643,77.4718 261.81,99.6385 258,152.5C 257.39,155.827 256.89,159.161 256.5,162.5C 227.5,162.5 198.5,162.5 169.5,162.5C 171.043,186.571 183.376,195.738 206.5,190C 213.036,186.43 217.869,181.263 221,174.5C 231.076,176.597 241.243,178.43 251.5,180C 245.366,197.128 233.366,208.795 215.5,215C 189.259,222.983 166.759,217.149 148,197.5C 138.556,183.943 134.556,168.943 136,152.5C 123.745,172.681 110.745,192.348 97,211.5C 88.7085,222.232 77.8752,226.732 64.5,225C 58.8865,223.718 54.7199,220.551 52,215.5C 48.3418,200.527 43.0085,186.193 36,172.5C 27.0419,161.206 17.7086,150.206 8,139.5C 3.51095,114.656 13.6776,104.489 38.5,109C 45.9811,112.493 52.4811,117.326 58,123.5C 65.3893,133.223 72.3893,143.223 79,153.5C 106.485,107.327 139.319,65.1599 177.5,27C 186.223,18.4277 195.889,11.2611 206.5,5.5 Z M 212.5,13.5 C 224.657,15.807 228.49,22.807 224,34.5C 200.259,56.9058 177.926,80.5725 157,105.5C 134.752,138.328 112.752,171.328 91,204.5C 85.6618,211.249 78.8285,215.582 70.5,217.5C 64.7571,216.922 60.9238,213.922 59,208.5C 55.6365,193.742 50.3031,179.742 43,166.5C 34.7461,155.575 25.7461,145.242 16,135.5C 13.7729,119.405 20.6062,113.572 36.5,118C 45.6208,123.454 53.1208,130.62 59,139.5C 65.4174,150.003 72.0841,160.337 79,170.5C 108.373,119.772 143.873,73.6053 185.5,32C 189.833,28.3333 194.167,24.6667 198.5,21C 202.826,17.6733 207.492,15.1733 212.5,13.5 Z M 198.5,109.5 C 211.684,107.835 220.518,113.168 225,125.5C 226.16,130.434 226.66,135.434 226.5,140.5C 208.83,140.667 191.164,140.5 173.5,140C 175.923,124.764 184.256,114.597 198.5,109.5 Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</g>
|
||||
<g>
|
||||
<path
|
||||
d="M 472.5,35.5 C 487.189,34.3737 501.522,36.0404 515.5,40.5C 516.631,42.0745 516.798,43.7412 516,45.5C 514.293,52.6665 512.126,59.6665 509.5,66.5C 501.517,62.8817 493.184,61.715 484.5,63C 482.622,64.5429 481.122,66.3762 480,68.5C 478.622,74.4002 477.122,80.2336 475.5,86C 483.493,86.4997 491.493,86.6664 499.5,86.5C 498.475,94.9601 496.808,103.293 494.5,111.5C 486.089,111.875 477.922,112.875 470,114.5C 463.004,147.481 456.171,180.481 449.5,213.5C 437.495,213.667 425.495,213.5 413.5,213C 415.943,203.954 417.943,194.787 419.5,185.5C 433.193,180.63 441.36,170.964 444,156.5C 445.846,143.204 442.013,131.871 432.5,122.5C 433.802,119.292 434.469,115.959 434.5,112.5C 428.5,112.5 422.5,112.5 416.5,112.5C 417.674,104.127 419.174,95.7934 421,87.5C 427.41,86.5223 433.91,86.189 440.5,86.5C 442.415,74.1716 445.582,62.1716 450,50.5C 455.548,42.4327 463.048,37.4327 472.5,35.5 Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</g>
|
||||
<g>
|
||||
<path
|
||||
d="M 377.5,37.5 C 388.833,37.5 400.167,37.5 411.5,37.5C 409.758,48.5415 407.758,59.5415 405.5,70.5C 393.833,70.5 382.167,70.5 370.5,70.5C 371.944,59.2795 374.277,48.2795 377.5,37.5 Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</g>
|
||||
<g>
|
||||
<path
|
||||
d="M 809.5,42.5 C 810.097,42.7352 810.43,43.2352 810.5,44C 807.306,58.1367 804.306,72.3033 801.5,86.5C 808.532,86.1697 815.532,86.503 822.5,87.5C 820.512,95.7878 818.678,104.121 817,112.5C 809.921,113.48 802.755,113.813 795.5,113.5C 790.772,136.641 786.105,159.808 781.5,183C 781.723,186.888 783.723,189.221 787.5,190C 792.887,190.784 798.22,190.618 803.5,189.5C 803.833,190.167 804.167,190.833 804.5,191.5C 802.671,199.815 801.004,208.148 799.5,216.5C 786.346,219.617 773.346,219.117 760.5,215C 752.836,212.002 748.003,206.502 746,198.5C 745.336,191.459 745.669,184.459 747,177.5C 751.251,156.242 755.751,135.075 760.5,114C 754.804,113.83 749.138,113.33 743.5,112.5C 745.488,104.212 747.322,95.8789 749,87.5C 754.741,86.5251 760.575,86.1918 766.5,86.5C 767.342,79.7889 768.509,73.1223 770,66.5C 783.338,58.6368 796.505,50.6368 809.5,42.5 Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</g>
|
||||
<g>
|
||||
<path
|
||||
d="M 345.5,83.5 C 351.785,83.8505 357.785,85.3505 363.5,88C 359.038,97.0908 354.705,106.257 350.5,115.5C 331.112,113.281 316.945,120.947 308,138.5C 299.736,163.652 293.569,189.318 289.5,215.5C 277.482,215.832 265.482,215.499 253.5,214.5C 262.395,172.525 271.229,130.525 280,88.5C 290.912,86.5357 302.078,85.8691 313.5,86.5C 311.998,93.4994 310.832,100.499 310,107.5C 316.251,99.2469 323.751,92.4136 332.5,87C 336.86,85.4171 341.194,84.2505 345.5,83.5 Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</g>
|
||||
<g>
|
||||
<path
|
||||
d="M 689.5,83.5 C 710.119,82.2301 726.286,89.8967 738,106.5C 741.176,113.046 743.343,119.879 744.5,127C 733.292,128.536 722.125,130.036 711,131.5C 706.259,111.048 694.425,105.548 675.5,115C 667.414,121.312 661.914,129.479 659,139.5C 655.484,150.594 654.15,161.927 655,173.5C 658.469,186.973 666.969,192.14 680.5,189C 690.603,184.797 697.269,177.464 700.5,167C 711.87,167.949 723.203,169.449 734.5,171.5C 724.935,201.207 704.435,216.874 673,218.5C 657.24,218.945 643.907,213.611 633,202.5C 634.482,206.476 636.315,210.476 638.5,214.5C 617.174,215.666 595.841,215.833 574.5,215C 571.625,210.083 568.625,205.249 565.5,200.5C 544.709,212.446 522.376,218.613 498.5,219C 495.585,215.172 492.919,211.172 490.5,207C 514.037,203.906 536.037,196.239 556.5,184C 538.086,151.836 519.419,119.836 500.5,88C 522.167,87.3333 543.833,87.3333 565.5,88C 577.207,108.582 589.04,129.082 601,149.5C 614.482,135.207 623.815,118.54 629,99.5C 631.667,102.833 634.333,106.167 637,109.5C 650.868,93.4829 668.368,84.8162 689.5,83.5 Z M 621.5,183.5 C 617.357,177.898 613.69,171.898 610.5,165.5C 613.333,161.833 616.167,158.167 619,154.5C 619.187,164.238 620.021,173.905 621.5,183.5 Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</g>
|
||||
<g>
|
||||
<path
|
||||
d="M 366.5,86.5 C 378.167,86.5 389.833,86.5 401.5,86.5C 400.117,95.5811 398.284,104.581 396,113.5C 372.948,123.27 364.615,140.27 371,164.5C 373.608,170.716 377.441,176.049 382.5,180.5C 380.008,191.469 377.674,202.469 375.5,213.5C 363.833,213.5 352.167,213.5 340.5,213.5C 348.91,171.117 357.577,128.784 366.5,86.5 Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</g>
|
||||
<g>
|
||||
<path
|
||||
d="M 826.5,86.5 C 838.167,86.5 849.833,86.5 861.5,86.5C 855.183,117.103 849.183,147.769 843.5,178.5C 843.785,187.452 848.452,192.118 857.5,192.5C 873.88,189.032 885.047,179.366 891,163.5C 897.077,137.949 902.577,112.282 907.5,86.5C 919.167,86.5 930.833,86.5 942.5,86.5C 934.091,129.544 925.425,172.544 916.5,215.5C 905.5,215.5 894.5,215.5 883.5,215.5C 884.416,210.102 885.25,204.769 886,199.5C 867.641,217.05 846.474,221.883 822.5,214C 812.227,207.123 807.227,197.456 807.5,185C 812.742,151.956 819.075,119.123 826.5,86.5 Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</g>
|
||||
<g>
|
||||
<path
|
||||
d="M 408.5,115.5 C 414.114,114.291 417.114,116.457 417.5,122C 415.066,130.535 412.066,138.868 408.5,147C 416.511,143.159 424.511,138.992 432.5,134.5C 438.775,136.543 440.109,140.376 436.5,146C 427.591,148.382 418.591,150.382 409.5,152C 417.875,156.381 425.709,161.548 433,167.5C 434.067,176.531 430.567,178.698 422.5,174C 416.972,167.773 411.806,161.273 407,154.5C 406.667,164.167 406.333,173.833 406,183.5C 403.217,187.823 399.717,188.49 395.5,185.5C 395.167,184.167 394.833,182.833 394.5,181.5C 397.064,172.314 399.897,163.314 403,154.5C 396.375,159.564 389.208,163.731 381.5,167C 373.915,168.421 371.415,165.588 374,158.5C 374.903,157.299 376.069,156.465 377.5,156C 385.53,154.273 393.53,152.44 401.5,150.5C 393.742,145.872 386.242,140.872 379,135.5C 377.167,128 380,125.167 387.5,127C 393.375,133.207 398.875,139.707 404,146.5C 405.141,137.543 405.808,128.543 406,119.5C 406.662,118.016 407.496,116.683 408.5,115.5 Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</g>
|
||||
<g>
|
||||
<path
|
||||
d="M 509.5,126.5 C 510.496,126.414 511.329,126.748 512,127.5C 514.167,131.833 516.333,136.167 518.5,140.5C 501.734,156.692 491.234,176.359 487,199.5C 483.166,194.503 479.833,189.17 477,183.5C 476.333,181.833 476.333,180.167 477,178.5C 484.052,158.714 494.885,141.381 509.5,126.5 Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -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