Compare commits

..

2 Commits

Author SHA1 Message Date
aac0529bb6 fix(infra): replace legacy klz fallback references with etib across docker compose and test scripts
All checks were successful
Build & Deploy / 🔍 Prepare (push) Successful in 19s
Build & Deploy / 🧪 QA (push) Successful in 1m25s
Build & Deploy / 🏗️ Build (push) Successful in 2m44s
Build & Deploy / 🚀 Deploy (push) Successful in 24s
Build & Deploy / 🧪 Post-Deploy Verification (push) Successful in 51s
Build & Deploy / 🔔 Notify (push) Successful in 3s
2026-08-17 18:31:29 +02:00
5006163ddf fix(ci): update proxy matcher and traefik public router for image optimizer loopback
All checks were successful
Build & Deploy / 🔍 Prepare (push) Successful in 19s
Build & Deploy / 🧪 QA (push) Successful in 1m31s
Build & Deploy / 🏗️ Build (push) Successful in 2m34s
Build & Deploy / 🚀 Deploy (push) Successful in 25s
Build & Deploy / 🧪 Post-Deploy Verification (push) Successful in 50s
Build & Deploy / 🔔 Notify (push) Successful in 3s
2026-08-17 18:22:45 +02:00
18 changed files with 140 additions and 78 deletions

View File

@@ -57,9 +57,10 @@ services:
sh -c "pnpm install --no-frozen-lockfile && pnpm next dev --webpack --hostname 0.0.0.0 --port 3001" sh -c "pnpm install --no-frozen-lockfile && pnpm next dev --webpack --hostname 0.0.0.0 --port 3001"
labels: labels:
- "traefik.enable=true" - "traefik.enable=true"
- "traefik.http.services.${PROJECT_NAME:-klz}-app-svc.loadbalancer.server.port=3001" - "traefik.http.services.${PROJECT_NAME:-etib}-app-svc.loadbalancer.server.port=3001"
- "traefik.docker.network=infra" - "traefik.docker.network=infra"
networks: networks:
default: default:
name: ${COMPOSE_PROJECT_NAME:-etib}-dev-internal name: ${COMPOSE_PROJECT_NAME:-etib}-dev-internal

View File

@@ -15,46 +15,47 @@ services:
labels: labels:
- "traefik.enable=true" - "traefik.enable=true"
# HTTP ⇒ HTTPS redirect # HTTP ⇒ HTTPS redirect
- "traefik.http.routers.${PROJECT_NAME:-klz}-web.rule=${TRAEFIK_HOST_RULE:-Host(`${TRAEFIK_HOST:-e-tib.com}`) || Host(`staging.${TRAEFIK_HOST:-e-tib.com}`) || Host(`testing.${TRAEFIK_HOST:-e-tib.com}`)}" - "traefik.http.routers.${PROJECT_NAME:-etib}-web.rule=${TRAEFIK_HOST_RULE:-Host(`${TRAEFIK_HOST:-e-tib.com}`) || Host(`staging.${TRAEFIK_HOST:-e-tib.com}`) || Host(`testing.${TRAEFIK_HOST:-e-tib.com}`)}"
- "traefik.http.routers.${PROJECT_NAME:-klz}-web.entrypoints=web" - "traefik.http.routers.${PROJECT_NAME:-etib}-web.entrypoints=web"
- "traefik.http.routers.${PROJECT_NAME:-klz}-web.middlewares=redirect-https" - "traefik.http.routers.${PROJECT_NAME:-etib}-web.middlewares=redirect-https"
# HTTPS router (Standard) # HTTPS router (Standard)
- "traefik.http.routers.${PROJECT_NAME:-klz}.rule=${TRAEFIK_HOST_RULE:-Host(`${TRAEFIK_HOST:-e-tib.com}`) || Host(`staging.${TRAEFIK_HOST:-e-tib.com}`) || Host(`testing.${TRAEFIK_HOST:-e-tib.com}`)}" - "traefik.http.routers.${PROJECT_NAME:-etib}.rule=${TRAEFIK_HOST_RULE:-Host(`${TRAEFIK_HOST:-e-tib.com}`) || Host(`staging.${TRAEFIK_HOST:-e-tib.com}`) || Host(`testing.${TRAEFIK_HOST:-e-tib.com}`)}"
- "traefik.http.routers.${PROJECT_NAME:-klz}.entrypoints=${TRAEFIK_ENTRYPOINT:-web}" - "traefik.http.routers.${PROJECT_NAME:-etib}.entrypoints=${TRAEFIK_ENTRYPOINT:-web}"
- "traefik.http.routers.${PROJECT_NAME:-klz}.tls.certresolver=${TRAEFIK_CERT_RESOLVER:-}" - "traefik.http.routers.${PROJECT_NAME:-etib}.tls.certresolver=${TRAEFIK_CERT_RESOLVER:-}"
- "traefik.http.routers.${PROJECT_NAME:-klz}.tls=${TRAEFIK_TLS:-false}" - "traefik.http.routers.${PROJECT_NAME:-etib}.tls=${TRAEFIK_TLS:-false}"
- "traefik.http.routers.${PROJECT_NAME:-klz}.service=${PROJECT_NAME:-klz}-app-svc" - "traefik.http.routers.${PROJECT_NAME:-etib}.service=${PROJECT_NAME:-etib}-app-svc"
- "traefik.http.routers.${PROJECT_NAME:-klz}.middlewares=${AUTH_MIDDLEWARE:-etib-ratelimit,etib-forward,etib-compress}" - "traefik.http.routers.${PROJECT_NAME:-etib}.middlewares=${AUTH_MIDDLEWARE:-etib-ratelimit,etib-forward,etib-compress}"
# Public Router paths that bypass Gatekeeper auth (health, SEO, static assets, OG images) # Public Router paths that bypass Gatekeeper auth (health, SEO, static assets, OG images)
- "traefik.http.routers.${PROJECT_NAME:-klz}-public.rule=(${TRAEFIK_HOST_RULE:-Host(`${TRAEFIK_HOST:-e-tib.com}`) || Host(`staging.${TRAEFIK_HOST:-e-tib.com}`) || Host(`testing.${TRAEFIK_HOST:-e-tib.com}`)}) && PathRegexp(`^/([a-z]{2}/)?(health|login|gatekeeper|uploads|media|assets|robots\\.txt|manifest\\.webmanifest|sitemap(-[0-9]+)?\\.xml|(.*/)?api/og(/.*)?|(.*/)?opengraph-image.*)`)" - "traefik.http.routers.${PROJECT_NAME:-etib}-public.rule=(${TRAEFIK_HOST_RULE:-Host(`${TRAEFIK_HOST:-e-tib.com}`) || Host(`staging.${TRAEFIK_HOST:-e-tib.com}`) || Host(`testing.${TRAEFIK_HOST:-e-tib.com}`)}) && PathRegexp(`^/([a-z]{2}/)?(health|login|gatekeeper|uploads|media|assets|_next|robots\\.txt|manifest\\.webmanifest|sitemap(-[0-9]+)?\\.xml|(.*/)?api/og(/.*)?|(.*/)?opengraph-image.*)`)"
- "traefik.http.routers.${PROJECT_NAME:-klz}-public.entrypoints=${TRAEFIK_ENTRYPOINT:-web}"
- "traefik.http.routers.${PROJECT_NAME:-klz}-public.tls.certresolver=${TRAEFIK_CERT_RESOLVER:-}"
- "traefik.http.routers.${PROJECT_NAME:-klz}-public.tls=${TRAEFIK_TLS:-false}"
- "traefik.http.routers.${PROJECT_NAME:-klz}-public.service=${PROJECT_NAME:-klz}-app-svc"
- "traefik.http.routers.${PROJECT_NAME:-klz}-public.priority=2000"
- "traefik.http.services.${PROJECT_NAME:-klz}-app-svc.loadbalancer.server.port=3000" - "traefik.http.routers.${PROJECT_NAME:-etib}-public.entrypoints=${TRAEFIK_ENTRYPOINT:-web}"
- "traefik.http.routers.${PROJECT_NAME:-etib}-public.tls.certresolver=${TRAEFIK_CERT_RESOLVER:-}"
- "traefik.http.routers.${PROJECT_NAME:-etib}-public.tls=${TRAEFIK_TLS:-false}"
- "traefik.http.routers.${PROJECT_NAME:-etib}-public.service=${PROJECT_NAME:-etib}-app-svc"
- "traefik.http.routers.${PROJECT_NAME:-etib}-public.priority=2000"
- "traefik.http.services.${PROJECT_NAME:-etib}-app-svc.loadbalancer.server.port=3000"
- "traefik.docker.network=infra" - "traefik.docker.network=infra"
# Middlewares # Middlewares
- "traefik.http.middlewares.${PROJECT_NAME:-klz}-compress.compress=true" - "traefik.http.middlewares.${PROJECT_NAME:-etib}-compress.compress=true"
- "traefik.http.middlewares.${PROJECT_NAME:-klz}-ratelimit.ratelimit.average=100" - "traefik.http.middlewares.${PROJECT_NAME:-etib}-ratelimit.ratelimit.average=100"
- "traefik.http.middlewares.${PROJECT_NAME:-klz}-ratelimit.ratelimit.burst=50" - "traefik.http.middlewares.${PROJECT_NAME:-etib}-ratelimit.ratelimit.burst=50"
- "traefik.http.middlewares.${PROJECT_NAME:-klz}-forward.headers.customrequestheaders.X-Forwarded-Proto=https" - "traefik.http.middlewares.${PROJECT_NAME:-etib}-forward.headers.customrequestheaders.X-Forwarded-Proto=https"
- "traefik.http.middlewares.${PROJECT_NAME:-klz}-forward.headers.customrequestheaders.X-Forwarded-Ssl=on" - "traefik.http.middlewares.${PROJECT_NAME:-etib}-forward.headers.customrequestheaders.X-Forwarded-Ssl=on"
# Login redirect the app's middleware sends users to /login but login lives at /gatekeeper/login # Login redirect the app's middleware sends users to /login but login lives at /gatekeeper/login
- "traefik.http.middlewares.${PROJECT_NAME:-klz}-loginredirect.redirectregex.regex=^https?://([^/]+)/([a-z]{2}/)?login(.*)" - "traefik.http.middlewares.${PROJECT_NAME:-etib}-loginredirect.redirectregex.regex=^https?://([^/]+)/([a-z]{2}/)?login(.*)"
- "traefik.http.middlewares.${PROJECT_NAME:-klz}-loginredirect.redirectregex.replacement=https://$${1}/gatekeeper/login$${3}" - "traefik.http.middlewares.${PROJECT_NAME:-etib}-loginredirect.redirectregex.replacement=https://$${1}/gatekeeper/login$${3}"
- "traefik.http.middlewares.${PROJECT_NAME:-klz}-loginredirect.redirectregex.permanent=false" - "traefik.http.middlewares.${PROJECT_NAME:-etib}-loginredirect.redirectregex.permanent=false"
- "traefik.http.routers.${PROJECT_NAME:-klz}-loginredir.rule=(${TRAEFIK_HOST_RULE:-Host(`${TRAEFIK_HOST:-e-tib.com}`) || Host(`staging.${TRAEFIK_HOST:-e-tib.com}`) || Host(`testing.${TRAEFIK_HOST:-e-tib.com}`)}) && PathRegexp(`^/([a-z]{2}/)?login`)" - "traefik.http.routers.${PROJECT_NAME:-etib}-loginredir.rule=(${TRAEFIK_HOST_RULE:-Host(`${TRAEFIK_HOST:-e-tib.com}`) || Host(`staging.${TRAEFIK_HOST:-e-tib.com}`) || Host(`testing.${TRAEFIK_HOST:-e-tib.com}`)}) && PathRegexp(`^/([a-z]{2}/)?login`)"
- "traefik.http.routers.${PROJECT_NAME:-klz}-loginredir.entrypoints=${TRAEFIK_ENTRYPOINT:-web}" - "traefik.http.routers.${PROJECT_NAME:-etib}-loginredir.entrypoints=${TRAEFIK_ENTRYPOINT:-web}"
- "traefik.http.routers.${PROJECT_NAME:-klz}-loginredir.tls.certresolver=${TRAEFIK_CERT_RESOLVER:-}" - "traefik.http.routers.${PROJECT_NAME:-etib}-loginredir.tls.certresolver=${TRAEFIK_CERT_RESOLVER:-}"
- "traefik.http.routers.${PROJECT_NAME:-klz}-loginredir.tls=${TRAEFIK_TLS:-false}" - "traefik.http.routers.${PROJECT_NAME:-etib}-loginredir.tls=${TRAEFIK_TLS:-false}"
- "traefik.http.routers.${PROJECT_NAME:-klz}-loginredir.middlewares=${PROJECT_NAME:-klz}-loginredirect" - "traefik.http.routers.${PROJECT_NAME:-etib}-loginredir.middlewares=${PROJECT_NAME:-etib}-loginredirect"
- "traefik.http.routers.${PROJECT_NAME:-klz}-loginredir.service=${PROJECT_NAME:-klz}-app-svc" - "traefik.http.routers.${PROJECT_NAME:-etib}-loginredir.service=${PROJECT_NAME:-etib}-app-svc"
- "traefik.http.routers.${PROJECT_NAME:-klz}-loginredir.priority=2002" - "traefik.http.routers.${PROJECT_NAME:-etib}-loginredir.priority=2002"
etib-gatekeeper: etib-gatekeeper:
profiles: [ "gatekeeper" ] profiles: [ "gatekeeper" ]
@@ -64,7 +65,7 @@ services:
default: default:
infra: infra:
aliases: aliases:
- ${PROJECT_NAME:-klz}-gatekeeper - ${PROJECT_NAME:-etib}-gatekeeper
env_file: env_file:
- ${ENV_FILE:-.env} - ${ENV_FILE:-.env}
environment: environment:
@@ -73,19 +74,20 @@ services:
NODE_ENV: production NODE_ENV: production
labels: labels:
- "traefik.enable=true" - "traefik.enable=true"
- "traefik.http.services.${PROJECT_NAME:-klz}-gatekeeper-svc.loadbalancer.server.port=3000" - "traefik.http.services.${PROJECT_NAME:-etib}-gatekeeper-svc.loadbalancer.server.port=3000"
- "traefik.http.middlewares.${PROJECT_NAME:-klz}-auth.forwardauth.address=http://${PROJECT_NAME:-klz}-gatekeeper:3000/gatekeeper/api/verify" - "traefik.http.middlewares.${PROJECT_NAME:-etib}-auth.forwardauth.address=http://${PROJECT_NAME:-etib}-gatekeeper:3000/gatekeeper/api/verify"
- "traefik.http.middlewares.${PROJECT_NAME:-klz}-auth.forwardauth.trustForwardHeader=true" - "traefik.http.middlewares.${PROJECT_NAME:-etib}-auth.forwardauth.trustForwardHeader=true"
- "traefik.http.middlewares.${PROJECT_NAME:-klz}-auth.forwardauth.authResponseHeaders=X-Auth-User" - "traefik.http.middlewares.${PROJECT_NAME:-etib}-auth.forwardauth.authResponseHeaders=X-Auth-User"
- "traefik.docker.network=infra" - "traefik.docker.network=infra"
# Gatekeeper Public Router (Login/Auth UI) — basePath mode on main domain # Gatekeeper Public Router (Login/Auth UI) — basePath mode on main domain
- "traefik.http.routers.${PROJECT_NAME:-klz}-gatekeeper.rule=(${TRAEFIK_HOST_RULE:-Host(`${TRAEFIK_HOST:-e-tib.com}`) || Host(`staging.${TRAEFIK_HOST:-e-tib.com}`) || Host(`testing.${TRAEFIK_HOST:-e-tib.com}`)}) && PathPrefix(`/gatekeeper`)" - "traefik.http.routers.${PROJECT_NAME:-etib}-gatekeeper.rule=(${TRAEFIK_HOST_RULE:-Host(`${TRAEFIK_HOST:-e-tib.com}`) || Host(`staging.${TRAEFIK_HOST:-e-tib.com}`) || Host(`testing.${TRAEFIK_HOST:-e-tib.com}`)}) && PathPrefix(`/gatekeeper`)"
- "traefik.http.routers.${PROJECT_NAME:-klz}-gatekeeper.entrypoints=${TRAEFIK_ENTRYPOINT:-web}" - "traefik.http.routers.${PROJECT_NAME:-etib}-gatekeeper.entrypoints=${TRAEFIK_ENTRYPOINT:-web}"
- "traefik.http.routers.${PROJECT_NAME:-klz}-gatekeeper.tls.certresolver=${TRAEFIK_CERT_RESOLVER:-}" - "traefik.http.routers.${PROJECT_NAME:-etib}-gatekeeper.tls.certresolver=${TRAEFIK_CERT_RESOLVER:-}"
- "traefik.http.routers.${PROJECT_NAME:-klz}-gatekeeper.tls=${TRAEFIK_TLS:-false}" - "traefik.http.routers.${PROJECT_NAME:-etib}-gatekeeper.tls=${TRAEFIK_TLS:-false}"
- "traefik.http.routers.${PROJECT_NAME:-klz}-gatekeeper.service=${PROJECT_NAME:-klz}-gatekeeper-svc" - "traefik.http.routers.${PROJECT_NAME:-etib}-gatekeeper.service=${PROJECT_NAME:-etib}-gatekeeper-svc"
- "traefik.http.routers.${PROJECT_NAME:-klz}-gatekeeper.priority=2001" - "traefik.http.routers.${PROJECT_NAME:-etib}-gatekeeper.priority=2001"
etib-db: etib-db:
image: postgres:15-alpine image: postgres:15-alpine

View File

@@ -1,6 +1,7 @@
{ {
"name": "e-tib-nextjs", "name": "e-tib-nextjs",
"version": "2.4.56", "version": "2.4.58",
"type": "module", "type": "module",
"private": true, "private": true,
"packageManager": "pnpm@10.18.3", "packageManager": "pnpm@10.18.3",

View File

@@ -7,11 +7,12 @@ export default createMiddleware({
}); });
export const config = { export const config = {
// Match all pathnames except for // Match all pathnames except for:
// - /api (API routes) // - /api (API routes)
// - /stats (Analytics proxy) // - /stats (Analytics proxy)
// - /_next (Next.js internals) // - /_next (Next.js internals & image optimizer)
// - /_static (inside /public) // - /assets, /_static (static files in /public)
// - all root files inside /public (e.g. /favicon.ico) // - all files with an extension (e.g. /favicon.ico, .JPG, .png, .webp, .svg)
matcher: ['/((?!api|stats|_next|assets|_static|_vercel|[\\w-]+\\.\\w+).*)'] matcher: ['/((?!api|stats|_next|assets|_static|_vercel|.*\\..*).*)']
}; };

View File

@@ -31,22 +31,23 @@ fi
get_media_path() { get_media_path() {
case "$1" in case "$1" in
local) echo "$LOCAL_MEDIA_DIR" ;; local) echo "$LOCAL_MEDIA_DIR" ;;
testing) echo "/var/lib/docker/volumes/klz-testing_klz_media_data/_data" ;; testing) echo "/var/lib/docker/volumes/etib-testing_etib_media_data/_data" ;;
staging) echo "/var/lib/docker/volumes/klz-staging_klz_media_data/_data" ;; staging) echo "/var/lib/docker/volumes/etib-staging_etib_media_data/_data" ;;
prod|production) echo "/var/lib/docker/volumes/klz-cablescom_klz_media_data/_data" ;; prod|production) echo "/var/lib/docker/volumes/e-tibcom_etib_media_data/_data" ;;
*) echo "❌ Unknown environment: $1"; exit 1 ;; *) echo "❌ Unknown environment: $1"; exit 1 ;;
esac esac
} }
get_app_container() { get_app_container() {
case "$1" in case "$1" in
testing) echo "klz-testing-klz-app-1" ;; testing) echo "etib-testing-etib-app-1" ;;
staging) echo "klz-staging-klz-app-1" ;; staging) echo "etib-staging-etib-app-1" ;;
prod|production) echo "klz-cablescom-klz-app-1" ;; prod|production) echo "e-tibcom-etib-app-1" ;;
*) echo "" ;; *) echo "" ;;
esac esac
} }
SRC_PATH=$(get_media_path "$SOURCE_ENV") SRC_PATH=$(get_media_path "$SOURCE_ENV")
TGT_PATH=$(get_media_path "$TARGET_ENV") TGT_PATH=$(get_media_path "$TARGET_ENV")
TGT_CONTAINER=$(get_app_container "$TARGET_ENV") TGT_CONTAINER=$(get_app_container "$TARGET_ENV")

View File

@@ -15,7 +15,8 @@ export NEXT_PUBLIC_CI=true
export CI=true export CI=true
docker network create infra 2>/dev/null || true docker network create infra 2>/dev/null || true
docker volume create klz_db_data 2>/dev/null || true docker volume create etib_db_data 2>/dev/null || true
# 2. Start infra services (DB, CMS, Gatekeeper) # 2. Start infra services (DB, CMS, Gatekeeper)
echo "📦 Starting infrastructure services..." echo "📦 Starting infrastructure services..."

View File

@@ -7,7 +7,8 @@ const targetUrl =
process.env.NEXT_PUBLIC_BASE_URL || process.env.NEXT_PUBLIC_BASE_URL ||
'http://localhost:3000'; 'http://localhost:3000';
const limit = process.env.ASSET_CHECK_LIMIT ? parseInt(process.env.ASSET_CHECK_LIMIT) : 20; const limit = process.env.ASSET_CHECK_LIMIT ? parseInt(process.env.ASSET_CHECK_LIMIT) : 20;
const gatekeeperPassword = process.env.GATEKEEPER_PASSWORD || 'klz2026'; const gatekeeperPassword = process.env.GATEKEEPER_PASSWORD || 'etib2026';
const authCookieName = process.env.AUTH_COOKIE_NAME || 'etib_gatekeeper_session';
async function main() { async function main() {
console.log(`\n🚀 Starting Strict Asset Integrity Check for: ${targetUrl}`); console.log(`\n🚀 Starting Strict Asset Integrity Check for: ${targetUrl}`);
@@ -20,9 +21,10 @@ async function main() {
try { try {
console.log(`📥 Fetching sitemap from ${sitemapUrl}...`); console.log(`📥 Fetching sitemap from ${sitemapUrl}...`);
const response = await axios.get(sitemapUrl, { const response = await axios.get(sitemapUrl, {
headers: { Cookie: `klz_gatekeeper_session=${gatekeeperPassword}` }, headers: { Cookie: `${authCookieName}=${gatekeeperPassword}` },
}); });
const $ = cheerio.load(response.data, { xmlMode: true }); const $ = cheerio.load(response.data, { xmlMode: true });
urls = $('url loc') urls = $('url loc')
.map((i, el) => $(el).text()) .map((i, el) => $(el).text())
@@ -64,7 +66,7 @@ async function main() {
// Inject Gatekeeper session bypassing auth screens // Inject Gatekeeper session bypassing auth screens
await page.setCookie({ await page.setCookie({
name: 'klz_gatekeeper_session', name: authCookieName,
value: gatekeeperPassword, value: gatekeeperPassword,
domain: new URL(targetUrl).hostname, domain: new URL(targetUrl).hostname,
path: '/', path: '/',

View File

@@ -3,7 +3,8 @@ import axios from 'axios';
import * as cheerio from 'cheerio'; import * as cheerio from 'cheerio';
const targetUrl = process.argv[2] || process.env.NEXT_PUBLIC_BASE_URL || 'http://localhost:3000'; const targetUrl = process.argv[2] || process.env.NEXT_PUBLIC_BASE_URL || 'http://localhost:3000';
const gatekeeperPassword = process.env.GATEKEEPER_PASSWORD || 'klz2026'; const gatekeeperPassword = process.env.GATEKEEPER_PASSWORD || 'etib2026';
const authCookieName = process.env.AUTH_COOKIE_NAME || 'etib_gatekeeper_session';
// Utility for hardcoded delays // Utility for hardcoded delays
const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
@@ -20,7 +21,7 @@ async function main() {
while (Date.now() - startTime < maxWaitMs) { while (Date.now() - startTime < maxWaitMs) {
try { try {
const resp = await axios.get(targetUrl, { const resp = await axios.get(targetUrl, {
headers: { Cookie: `klz_gatekeeper_session=${gatekeeperPassword}` }, headers: { Cookie: `${authCookieName}=${gatekeeperPassword}` },
timeout: 5000, timeout: 5000,
validateStatus: (s) => s === 200, validateStatus: (s) => s === 200,
}); });
@@ -50,7 +51,7 @@ async function main() {
try { try {
console.log(`📥 Fetching sitemap from ${sitemapUrl}...`); console.log(`📥 Fetching sitemap from ${sitemapUrl}...`);
const response = await axios.get(sitemapUrl, { const response = await axios.get(sitemapUrl, {
headers: { Cookie: `klz_gatekeeper_session=${gatekeeperPassword}` }, headers: { Cookie: `${authCookieName}=${gatekeeperPassword}` },
}); });
const $ = cheerio.load(response.data, { xmlMode: true }); const $ = cheerio.load(response.data, { xmlMode: true });

View File

@@ -5,7 +5,8 @@ import * as path from 'path';
import { execSync } from 'child_process'; import { execSync } from 'child_process';
const targetUrl = process.argv[2] || process.env.NEXT_PUBLIC_BASE_URL || 'http://localhost:3000'; const targetUrl = process.argv[2] || process.env.NEXT_PUBLIC_BASE_URL || 'http://localhost:3000';
const gatekeeperPassword = process.env.GATEKEEPER_PASSWORD || 'klz2026'; const gatekeeperPassword = process.env.GATEKEEPER_PASSWORD || 'etib2026';
const authCookieName = process.env.AUTH_COOKIE_NAME || 'etib_gatekeeper_session';
async function main() { async function main() {
console.log(`\n🚀 Starting HTML Validation for: ${targetUrl}`); console.log(`\n🚀 Starting HTML Validation for: ${targetUrl}`);
@@ -16,10 +17,11 @@ async function main() {
console.log(`📥 Fetching sitemap from ${sitemapUrl}...`); console.log(`📥 Fetching sitemap from ${sitemapUrl}...`);
const response = await axios.get(sitemapUrl, { const response = await axios.get(sitemapUrl, {
headers: { Cookie: `klz_gatekeeper_session=${gatekeeperPassword}` }, headers: { Cookie: `${authCookieName}=${gatekeeperPassword}` },
validateStatus: (status) => status < 400, validateStatus: (status) => status < 400,
}); });
const $ = cheerio.load(response.data, { xmlMode: true }); const $ = cheerio.load(response.data, { xmlMode: true });
let urls = $('url loc') let urls = $('url loc')
.map((i, el) => $(el).text()) .map((i, el) => $(el).text())
@@ -47,10 +49,11 @@ async function main() {
const u = urls[i]; const u = urls[i];
try { try {
const res = await axios.get(u, { const res = await axios.get(u, {
headers: { Cookie: `klz_gatekeeper_session=${gatekeeperPassword}` }, headers: { Cookie: `${authCookieName}=${gatekeeperPassword}` },
validateStatus: (status) => status < 400, validateStatus: (status) => status < 400,
}); });
// Generate a safe filename that retains URL information // Generate a safe filename that retains URL information
const urlStr = new URL(u); const urlStr = new URL(u);
const safePath = (urlStr.pathname + urlStr.search).replace(/[^a-zA-Z0-9]/g, '_'); const safePath = (urlStr.pathname + urlStr.search).replace(/[^a-zA-Z0-9]/g, '_');

View File

@@ -2,7 +2,8 @@ import axios from 'axios';
import * as cheerio from 'cheerio'; import * as cheerio from 'cheerio';
const targetUrl = process.argv[2] || process.env.NEXT_PUBLIC_BASE_URL || 'http://localhost:3000'; const targetUrl = process.argv[2] || process.env.NEXT_PUBLIC_BASE_URL || 'http://localhost:3000';
const gatekeeperPassword = process.env.GATEKEEPER_PASSWORD || 'klz2026'; const gatekeeperPassword = process.env.GATEKEEPER_PASSWORD || 'etib2026';
const authCookieName = process.env.AUTH_COOKIE_NAME || 'etib_gatekeeper_session';
async function main() { async function main() {
console.log(`\n🚀 Starting HTTP Sitemap Validation for: ${targetUrl}\n`); console.log(`\n🚀 Starting HTTP Sitemap Validation for: ${targetUrl}\n`);
@@ -12,10 +13,11 @@ async function main() {
console.log(`📥 Fetching sitemap from ${sitemapUrl}...`); console.log(`📥 Fetching sitemap from ${sitemapUrl}...`);
const response = await axios.get(sitemapUrl, { const response = await axios.get(sitemapUrl, {
headers: { Cookie: `klz_gatekeeper_session=${gatekeeperPassword}` }, headers: { Cookie: `${authCookieName}=${gatekeeperPassword}` },
validateStatus: (status) => status < 400, validateStatus: (status) => status < 400,
}); });
const $ = cheerio.load(response.data, { xmlMode: true }); const $ = cheerio.load(response.data, { xmlMode: true });
let urls = $('url loc') let urls = $('url loc')
.map((i, el) => $(el).text()) .map((i, el) => $(el).text())
@@ -42,10 +44,11 @@ async function main() {
const u = urls[i]; const u = urls[i];
try { try {
const res = await axios.get(u, { const res = await axios.get(u, {
headers: { Cookie: `klz_gatekeeper_session=${gatekeeperPassword}` }, headers: { Cookie: `${authCookieName}=${gatekeeperPassword}` },
validateStatus: null, // Don't throw on error status validateStatus: null, // Don't throw on error status
}); });
if (res.status >= 400) { if (res.status >= 400) {
console.error(`❌ ERROR ${res.status}: ${res.statusText} -> ${u}`); console.error(`❌ ERROR ${res.status}: ${res.statusText} -> ${u}`);
hasErrors = true; hasErrors = true;

View File

@@ -12,7 +12,8 @@ import * as cheerio from 'cheerio';
*/ */
const targetUrl = process.argv[2] || process.env.NEXT_PUBLIC_BASE_URL || 'http://localhost:3000'; const targetUrl = process.argv[2] || process.env.NEXT_PUBLIC_BASE_URL || 'http://localhost:3000';
const gatekeeperPassword = process.env.GATEKEEPER_PASSWORD || 'klz2026'; const gatekeeperPassword = process.env.GATEKEEPER_PASSWORD || 'etib2026';
const authCookieName = process.env.AUTH_COOKIE_NAME || 'etib_gatekeeper_session';
// Expected slug translations: German key → English value // Expected slug translations: German key → English value
const SLUG_MAP: Record<string, string> = { const SLUG_MAP: Record<string, string> = {
@@ -32,7 +33,8 @@ const REVERSE_SLUG_MAP: Record<string, string> = Object.fromEntries(
Object.entries(SLUG_MAP).map(([de, en]) => [en, de]), Object.entries(SLUG_MAP).map(([de, en]) => [en, de]),
); );
const headers = { Cookie: `klz_gatekeeper_session=${gatekeeperPassword}` }; const headers = { Cookie: `${authCookieName}=${gatekeeperPassword}` };
function getExpectedTranslation( function getExpectedTranslation(
sourcePath: string, sourcePath: string,

View File

@@ -1,7 +1,8 @@
import axios from 'axios'; import axios from 'axios';
const targetUrl = process.argv[2] || process.env.NEXT_PUBLIC_BASE_URL || 'http://localhost:3000'; const targetUrl = process.argv[2] || process.env.NEXT_PUBLIC_BASE_URL || 'http://localhost:3000';
const gatekeeperPassword = process.env.GATEKEEPER_PASSWORD || 'klz2026'; const gatekeeperPassword = process.env.GATEKEEPER_PASSWORD || 'etib2026';
const authCookieName = process.env.AUTH_COOKIE_NAME || 'etib_gatekeeper_session';
const requiredHeaders = [ const requiredHeaders = [
'strict-transport-security', 'strict-transport-security',
@@ -15,10 +16,11 @@ async function main() {
console.log(`\n🛡 Starting Security Headers Scan for: ${targetUrl}\n`); console.log(`\n🛡 Starting Security Headers Scan for: ${targetUrl}\n`);
try { try {
const response = await axios.head(targetUrl, { const response = await axios.head(targetUrl, {
headers: { Cookie: `klz_gatekeeper_session=${gatekeeperPassword}` }, headers: { Cookie: `${authCookieName}=${gatekeeperPassword}` },
validateStatus: () => true, validateStatus: () => true,
}); });
const headers = response.headers; const headers = response.headers;
let allPassed = true; let allPassed = true;

View File

@@ -7,9 +7,10 @@ module.exports = async (browser, context) => {
// Using LHCI_URL or TARGET_URL if available // Using LHCI_URL or TARGET_URL if available
const targetUrl = const targetUrl =
process.env.LHCI_URL || process.env.TARGET_URL || 'https://testing.e-tib.com'; process.env.LHCI_URL || process.env.TARGET_URL || 'https://testing.e-tib.com';
const gatekeeperPassword = process.env.GATEKEEPER_PASSWORD || 'klz2026'; const gatekeeperPassword = process.env.GATEKEEPER_PASSWORD || 'etib2026';
const authCookieName = process.env.AUTH_COOKIE_NAME || 'etib_gatekeeper_session'; const authCookieName = process.env.AUTH_COOKIE_NAME || 'etib_gatekeeper_session';
console.log(`🔑 LHCI Auth: Setting gatekeeper cookie (${authCookieName}) for ${new URL(targetUrl).hostname}...`); console.log(`🔑 LHCI Auth: Setting gatekeeper cookie (${authCookieName}) for ${new URL(targetUrl).hostname}...`);
await page.setCookie({ await page.setCookie({

View File

@@ -18,9 +18,10 @@ const targetUrl =
process.env.LHCI_URL || process.env.LHCI_URL ||
'http://localhost:3000'; 'http://localhost:3000';
const limit = process.env.PAGESPEED_LIMIT ? parseInt(process.env.PAGESPEED_LIMIT) : 20; // Default limit to avoid infinite runs const limit = process.env.PAGESPEED_LIMIT ? parseInt(process.env.PAGESPEED_LIMIT) : 20; // Default limit to avoid infinite runs
const gatekeeperPassword = process.env.GATEKEEPER_PASSWORD || 'klz2026'; const gatekeeperPassword = process.env.GATEKEEPER_PASSWORD || 'etib2026';
const authCookieName = process.env.AUTH_COOKIE_NAME || 'etib_gatekeeper_session'; const authCookieName = process.env.AUTH_COOKIE_NAME || 'etib_gatekeeper_session';
async function main() { async function main() {
console.log(`\n🚀 Starting PageSpeed test for: ${targetUrl}`); console.log(`\n🚀 Starting PageSpeed test for: ${targetUrl}`);
console.log(`📊 Limit: ${limit} pages\n`); console.log(`📊 Limit: ${limit} pages\n`);

View File

@@ -1,7 +1,8 @@
import puppeteer from 'puppeteer'; import puppeteer from 'puppeteer';
const targetUrl = process.argv[2] || process.env.NEXT_PUBLIC_BASE_URL || 'http://localhost:3000'; const targetUrl = process.argv[2] || process.env.NEXT_PUBLIC_BASE_URL || 'http://localhost:3000';
const gatekeeperPassword = process.env.GATEKEEPER_PASSWORD || 'klz2026'; const gatekeeperPassword = process.env.GATEKEEPER_PASSWORD || 'etib2026';
async function run() { async function run() {
console.log(`🚀 Smoke Test: ${targetUrl}`); console.log(`🚀 Smoke Test: ${targetUrl}`);

View File

@@ -14,7 +14,8 @@ import * as path from 'path';
const targetUrl = process.argv[2] || process.env.NEXT_PUBLIC_BASE_URL || 'http://localhost:3000'; const targetUrl = process.argv[2] || process.env.NEXT_PUBLIC_BASE_URL || 'http://localhost:3000';
const limit = process.env.PAGESPEED_LIMIT ? parseInt(process.env.PAGESPEED_LIMIT) : 20; const limit = process.env.PAGESPEED_LIMIT ? parseInt(process.env.PAGESPEED_LIMIT) : 20;
const gatekeeperPassword = process.env.GATEKEEPER_PASSWORD || 'klz2026'; const gatekeeperPassword = process.env.GATEKEEPER_PASSWORD || 'etib2026';
const authCookieName = process.env.AUTH_COOKIE_NAME || 'etib_gatekeeper_session';
async function main() { async function main() {
console.log(`\n🚀 Starting WCAG Audit for: ${targetUrl}`); console.log(`\n🚀 Starting WCAG Audit for: ${targetUrl}`);
@@ -27,11 +28,12 @@ async function main() {
const response = await axios.get(sitemapUrl, { const response = await axios.get(sitemapUrl, {
headers: { headers: {
Cookie: `klz_gatekeeper_session=${gatekeeperPassword}`, Cookie: `${authCookieName}=${gatekeeperPassword}`,
}, },
validateStatus: (status) => status < 400, validateStatus: (status) => status < 400,
}); });
const $ = cheerio.load(response.data, { xmlMode: true }); const $ = cheerio.load(response.data, { xmlMode: true });
let urls = $('url loc') let urls = $('url loc')
.map((i, el) => $(el).text()) .map((i, el) => $(el).text())
@@ -92,8 +94,9 @@ async function main() {
], ],
}, },
headers: { headers: {
Cookie: `klz_gatekeeper_session=${gatekeeperPassword}`, Cookie: `${authCookieName}=${gatekeeperPassword}`,
}, },
timeout: 60000, // Increase timeout for slower pages timeout: 60000, // Increase timeout for slower pages
}, },
urls: urls, urls: urls,

View File

@@ -0,0 +1,15 @@
import { describe, it, expect } from 'vitest';
import fs from 'node:fs';
import path from 'node:path';
describe('Docker Configuration Project Name Integrity', () => {
it('ensures docker-compose.yml has no legacy klz project fallbacks', () => {
const composeContent = fs.readFileSync(path.resolve(process.cwd(), 'docker-compose.yml'), 'utf-8');
expect(composeContent).not.toContain('klz');
});
it('ensures docker-compose.dev.yml has no legacy klz project fallbacks', () => {
const devComposeContent = fs.readFileSync(path.resolve(process.cwd(), 'docker-compose.dev.yml'), 'utf-8');
expect(devComposeContent).not.toContain('klz');
});
});

View File

@@ -0,0 +1,21 @@
import { describe, it, expect } from 'vitest';
import { config } from '../proxy';
describe('Proxy Middleware Config Matcher', () => {
const matcherRegex = new RegExp(`^${config.matcher[0]}`);
it('matches localized page routes for next-intl processing', () => {
expect(matcherRegex.test('/de')).toBe(true);
expect(matcherRegex.test('/en/contact')).toBe(true);
expect(matcherRegex.test('/karriere')).toBe(true);
});
it('excludes static assets with any file extension including uppercase .JPG and subdirectories', () => {
// These should NOT match the middleware (expect test to be false so middleware is bypassed)
expect(matcherRegex.test('/assets/photos/DJI_0048.JPG')).toBe(false);
expect(matcherRegex.test('/assets/videos/web/hero-kabelpflug-poster.jpg')).toBe(false);
expect(matcherRegex.test('/assets/logo-white.png')).toBe(false);
expect(matcherRegex.test('/_next/image')).toBe(false);
});
});