Add FeWo Docker deployment and expand report themes

This commit is contained in:
2026-06-26 08:37:19 +00:00
parent 61a53e723e
commit 3a2f03d02e
7 changed files with 343 additions and 1 deletions
+12
View File
@@ -0,0 +1,12 @@
TZ=Europe/Berlin
REPORT_RENDERER_BIND_IP=127.0.0.1
REPORT_RENDERER_PORT=8090
RENDER_TIMEOUT_SECONDS=60
MAX_PAYLOAD_BYTES=2097152
GOTENBERG_IMAGE=gotenberg/gotenberg:8
GOTENBERG_PLATFORM=linux/amd64
N8N_NETWORK=docker-postgres-backend
+86
View File
@@ -0,0 +1,86 @@
name: monthly-report-kit
services:
report-renderer:
build:
context: ./source/renderer
container_name: monthly-report-renderer
restart: unless-stopped
environment:
TZ: ${TZ}
GOTENBERG_URL: http://gotenberg:3000
RENDER_TIMEOUT_SECONDS: ${RENDER_TIMEOUT_SECONDS}
MAX_PAYLOAD_BYTES: ${MAX_PAYLOAD_BYTES}
ports:
- "${REPORT_RENDERER_BIND_IP}:${REPORT_RENDERER_PORT}:8080"
depends_on:
gotenberg:
condition: service_healthy
healthcheck:
test:
- CMD
- python
- -c
- >-
import urllib.request;
urllib.request.urlopen(
'http://127.0.0.1:8080/health',
timeout=3
).read()
interval: 15s
timeout: 5s
retries: 10
start_period: 20s
networks:
report_internal:
n8n_backend:
aliases:
- report-renderer
gotenberg:
image: ${GOTENBERG_IMAGE}
platform: ${GOTENBERG_PLATFORM}
container_name: monthly-report-gotenberg
restart: unless-stopped
init: true
entrypoint:
- /usr/bin/gotenberg
command:
- --chromium-disable-javascript=true
- --api-timeout=90s
healthcheck:
test:
- CMD-SHELL
- >-
bash -c 'exec 3<>/dev/tcp/127.0.0.1/3000;
printf "HEAD /health HTTP/1.0\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n" >&3;
IFS= read -r status <&3;
case "$$status" in
*" 200 "*) exit 0 ;;
*) exit 1 ;;
esac'
interval: 5s
timeout: 5s
retries: 20
start_period: 20s
networks:
report_internal:
networks:
report_internal:
name: monthly-report-internal
internal: true
n8n_backend:
external: true
name: ${N8N_NETWORK}
+67
View File
@@ -0,0 +1,67 @@
#!/usr/bin/env bash
set -Eeuo pipefail
TARGET_DIR="${TARGET_DIR:-/srv/docker/monthly-report-kit}"
N8N_CONTAINER="${N8N_CONTAINER:-n8n}"
POSTGRES_CONTAINER="${POSTGRES_CONTAINER:-infra-postgres}"
POSTGRES_DATABASE="${POSTGRES_DATABASE:-fewomonitor}"
POSTGRES_ADMIN_USER="${POSTGRES_ADMIN_USER:-postgres}"
[[ -f "$TARGET_DIR/.env" ]] || {
echo "FEHLER: $TARGET_DIR/.env fehlt" >&2
exit 1
}
set -a
# shellcheck disable=SC1090
source "$TARGET_DIR/.env"
set +a
TEST_HOST="$REPORT_RENDERER_BIND_IP"
case "$TEST_HOST" in
0.0.0.0|"::"|"[::]")
TEST_HOST="127.0.0.1"
;;
esac
echo "1/5 Compose-Status"
(
cd "$TARGET_DIR"
docker compose ps
)
echo
echo "2/5 Gotenberg-Plattform"
docker image inspect "$GOTENBERG_IMAGE" \
--format 'Image: {{.Os}}/{{.Architecture}}'
echo
echo "3/5 Lokaler Renderer-Healthcheck"
curl -fsS \
"http://${TEST_HOST}:${REPORT_RENDERER_PORT}/health"
echo
echo
echo "4/5 Zugriff aus dem n8n-Container"
docker exec "$N8N_CONTAINER" node -e "
fetch('http://report-renderer:8080/health')
.then(async response => {
if (!response.ok) {
throw new Error('HTTP ' + response.status);
}
console.log(await response.text());
})
.catch(error => {
console.error(error);
process.exit(1);
});
"
echo
echo "5/5 Reporting-Tabellen"
docker exec "$POSTGRES_CONTAINER" \
psql \
-U "$POSTGRES_ADMIN_USER" \
-d "$POSTGRES_DATABASE" \
-c '\dt public.report_customer_config' \
-c '\dt public.monthly_report_runs'
+40
View File
@@ -0,0 +1,40 @@
#!/usr/bin/env bash
set -Eeuo pipefail
TARGET_DIR="${TARGET_DIR:-/srv/docker/monthly-report-kit}"
SOURCE_DIR="$TARGET_DIR/source"
REPO_REF="${REPO_REF:-main}"
[[ -d "$SOURCE_DIR/.git" ]] || {
echo "FEHLER: Kein Git-Repository unter $SOURCE_DIR" >&2
exit 1
}
[[ -f "$TARGET_DIR/.env" ]] || {
echo "FEHLER: $TARGET_DIR/.env fehlt" >&2
exit 1
}
set -a
# shellcheck disable=SC1090
source "$TARGET_DIR/.env"
set +a
: "${GOTENBERG_IMAGE:?GOTENBERG_IMAGE fehlt in .env}"
: "${GOTENBERG_PLATFORM:?GOTENBERG_PLATFORM fehlt in .env}"
git -C "$SOURCE_DIR" fetch --prune origin
git -C "$SOURCE_DIR" checkout "$REPO_REF"
git -C "$SOURCE_DIR" pull --ff-only origin "$REPO_REF"
cd "$TARGET_DIR"
docker compose config >/dev/null
docker pull \
--platform "$GOTENBERG_PLATFORM" \
"$GOTENBERG_IMAGE"
docker compose build --pull report-renderer
docker compose up -d --force-recreate
"$TARGET_DIR/scripts/test-monthly-report-kit.sh"
+109
View File
@@ -0,0 +1,109 @@
BEGIN;
CREATE TABLE IF NOT EXISTS public.report_customer_config (
customer_id VARCHAR(64) PRIMARY KEY,
customer_name VARCHAR(160) NOT NULL,
property_name VARCHAR(160) NOT NULL,
location VARCHAR(160) NOT NULL,
contact_line VARCHAR(180) NOT NULL DEFAULT '',
logo_data_uri TEXT,
logo_text VARCHAR(5) NOT NULL,
theme VARCHAR(40) NOT NULL DEFAULT 'modern',
primary_color CHAR(7) NOT NULL DEFAULT '#1F5D66',
secondary_color CHAR(7) NOT NULL DEFAULT '#2C7C82',
accent_color CHAR(7) NOT NULL DEFAULT '#DBA25D',
text_color CHAR(7) NOT NULL DEFAULT '#24343A',
muted_color CHAR(7) NOT NULL DEFAULT '#718087',
surface_color CHAR(7) NOT NULL DEFAULT '#F2F6F5',
font_family VARCHAR(100) NOT NULL DEFAULT 'Arial',
active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Upgrade-Pfad für die ursprüngliche Repo-Struktur.
ALTER TABLE public.report_customer_config
ALTER COLUMN theme TYPE VARCHAR(40);
ALTER TABLE public.report_customer_config
DROP CONSTRAINT IF EXISTS report_customer_config_theme_check;
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1
FROM pg_constraint
WHERE conname = 'report_customer_config_theme_not_empty'
AND conrelid = 'public.report_customer_config'::regclass
) THEN
ALTER TABLE public.report_customer_config
ADD CONSTRAINT report_customer_config_theme_not_empty
CHECK (length(trim(theme)) > 0) NOT VALID;
END IF;
END;
$$;
ALTER TABLE public.report_customer_config
VALIDATE CONSTRAINT report_customer_config_theme_not_empty;
CREATE TABLE IF NOT EXISTS public.monthly_report_runs (
id BIGSERIAL PRIMARY KEY,
customer_id VARCHAR(64) NOT NULL
REFERENCES public.report_customer_config(customer_id),
report_year INTEGER NOT NULL
CHECK (report_year BETWEEN 2020 AND 2100),
report_month INTEGER NOT NULL
CHECK (report_month BETWEEN 1 AND 12),
status VARCHAR(20) NOT NULL
CHECK (status IN ('queued', 'processing', 'completed', 'failed')),
report_version VARCHAR(20) NOT NULL,
prompt_version VARCHAR(20) NOT NULL,
template_version VARCHAR(20) NOT NULL,
claude_model VARCHAR(80),
storage_path TEXT,
checksum_sha256 CHAR(64),
error_message TEXT,
started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
completed_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE (customer_id, report_year, report_month, report_version)
);
CREATE INDEX IF NOT EXISTS idx_monthly_report_runs_status
ON public.monthly_report_runs(status, report_year, report_month);
CREATE INDEX IF NOT EXISTS idx_monthly_report_runs_customer_period
ON public.monthly_report_runs(customer_id, report_year, report_month);
CREATE OR REPLACE FUNCTION public.set_report_customer_config_updated_at()
RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$;
DROP TRIGGER IF EXISTS trg_report_customer_config_updated_at
ON public.report_customer_config;
CREATE TRIGGER trg_report_customer_config_updated_at
BEFORE UPDATE ON public.report_customer_config
FOR EACH ROW
EXECUTE FUNCTION public.set_report_customer_config_updated_at();
ALTER TABLE public.report_customer_config OWNER TO fewo;
ALTER TABLE public.monthly_report_runs OWNER TO fewo;
ALTER SEQUENCE IF EXISTS public.monthly_report_runs_id_seq OWNER TO fewo;
ALTER FUNCTION public.set_report_customer_config_updated_at() OWNER TO fewo;
GRANT SELECT, INSERT, UPDATE, DELETE
ON public.report_customer_config, public.monthly_report_runs
TO fewo;
GRANT USAGE, SELECT, UPDATE
ON SEQUENCE public.monthly_report_runs_id_seq
TO fewo;
COMMIT;
+27
View File
@@ -0,0 +1,27 @@
BEGIN;
ALTER TABLE public.report_customer_config
ALTER COLUMN theme TYPE VARCHAR(40);
ALTER TABLE public.report_customer_config
DROP CONSTRAINT IF EXISTS report_customer_config_theme_check;
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1
FROM pg_constraint
WHERE conname = 'report_customer_config_theme_not_empty'
AND conrelid = 'public.report_customer_config'::regclass
) THEN
ALTER TABLE public.report_customer_config
ADD CONSTRAINT report_customer_config_theme_not_empty
CHECK (length(trim(theme)) > 0) NOT VALID;
END IF;
END;
$$;
ALTER TABLE public.report_customer_config
VALIDATE CONSTRAINT report_customer_config_theme_not_empty;
COMMIT;
+2 -1
View File
@@ -6,7 +6,8 @@ CREATE TABLE IF NOT EXISTS report_customer_config (
contact_line VARCHAR(180) NOT NULL DEFAULT '',
logo_data_uri TEXT,
logo_text VARCHAR(5) NOT NULL,
theme VARCHAR(20) NOT NULL DEFAULT 'modern' CHECK (theme IN ('modern', 'classic')),
theme VARCHAR(40) NOT NULL DEFAULT 'modern'
CHECK (length(trim(theme)) > 0),
primary_color CHAR(7) NOT NULL DEFAULT '#1F5D66',
secondary_color CHAR(7) NOT NULL DEFAULT '#2C7C82',
accent_color CHAR(7) NOT NULL DEFAULT '#DBA25D',