From fcb3169d04a810d0a75049722cfe1f969000cd16 Mon Sep 17 00:00:00 2001 From: Marc Mintel Date: Sun, 1 Feb 2026 16:27:52 +0100 Subject: [PATCH] feat: Implement a gatekeeper service for access control and add CMS health monitoring with a connectivity notice. --- .env.example | 4 ++ .gitea/workflows/deploy.yml | 13 ++++ app/[locale]/layout.tsx | 12 ++-- app/api/health/cms/route.ts | 9 +++ components/CMSConnectivityNotice.tsx | 72 +++++++++++++++++++++ docker-compose.yml | 32 ++++++++- gatekeeper/Dockerfile | 7 ++ gatekeeper/index.js | 60 +++++++++++++++++ gatekeeper/package.json | 11 ++++ gatekeeper/views/login.ejs | 97 ++++++++++++++++++++++++++++ lib/directus.ts | 16 +++++ 11 files changed, 327 insertions(+), 6 deletions(-) create mode 100644 app/api/health/cms/route.ts create mode 100644 components/CMSConnectivityNotice.tsx create mode 100644 gatekeeper/Dockerfile create mode 100644 gatekeeper/index.js create mode 100644 gatekeeper/package.json create mode 100644 gatekeeper/views/login.ejs diff --git a/.env.example b/.env.example index 802ec26f..e662e6cd 100644 --- a/.env.example +++ b/.env.example @@ -39,6 +39,10 @@ MAIL_RECIPIENTS=info@klz-cables.com # Logging # ──────────────────────────────────────────────────────────────────────────── LOG_LEVEL=info +GATEKEEPER_PASSWORD=klz2026 +SENTRY_DSN= +# For Directus Error Tracking +# SENTRY_ENVIRONMENT is set automatically by CI # ──────────────────────────────────────────────────────────────────────────── # Deployment Configuration (CI/CD only) diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index e971e93d..1303401f 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -193,6 +193,16 @@ jobs: --cache-to type=registry,ref=registry.infra.mintel.me/mintel/klz-cables.com:buildcache,mode=max \ --push . + - name: 🏗️ Gatekeeper bauen & pushen + env: + IMAGE_TAG: ${{ needs.prepare.outputs.image_tag }} + run: | + docker buildx build \ + --pull \ + --platform linux/arm64 \ + -t registry.infra.mintel.me/mintel/klz-cables-gatekeeper:$IMAGE_TAG \ + --push ./gatekeeper + # ────────────────────────────────────────────────────────────────────────────── # JOB 4: Deploy via SSH @@ -227,6 +237,7 @@ jobs: DIRECTUS_DB_USER: ${{ secrets.DIRECTUS_DB_USER || 'directus' }} DIRECTUS_DB_PASSWORD: ${{ secrets.DIRECTUS_DB_PASSWORD }} DIRECTUS_API_TOKEN: ${{ secrets.DIRECTUS_API_TOKEN }} + GATEKEEPER_PASSWORD: ${{ secrets.GATEKEEPER_PASSWORD || 'klz2026' }} steps: - name: Checkout repository uses: actions/checkout@v4 @@ -265,10 +276,12 @@ jobs: DIRECTUS_DB_USER=$DIRECTUS_DB_USER DIRECTUS_DB_PASSWORD=$DIRECTUS_DB_PASSWORD DIRECTUS_API_TOKEN=$DIRECTUS_API_TOKEN + GATEKEEPER_PASSWORD=$GATEKEEPER_PASSWORD IMAGE_TAG=$IMAGE_TAG TRAEFIK_HOST=$TRAEFIK_HOST ENV_FILE=$ENV_FILE + AUTH_MIDDLEWARE=$( [[ "$TARGET" == "production" ]] && echo "compress" || echo "${PROJECT_NAME}-auth,compress" ) EOF scp -o StrictHostKeyChecking=accept-new /tmp/klz-cables.env root@alpha.mintel.me:/home/deploy/sites/klz-cables.com/$ENV_FILE diff --git a/app/[locale]/layout.tsx b/app/[locale]/layout.tsx index e3c3ad72..cc51684e 100644 --- a/app/[locale]/layout.tsx +++ b/app/[locale]/layout.tsx @@ -2,6 +2,7 @@ import Footer from '@/components/Footer'; import Header from '@/components/Header'; import JsonLd from '@/components/JsonLd'; import AnalyticsProvider from '@/components/analytics/AnalyticsProvider'; +import CMSConnectivityNotice from '@/components/CMSConnectivityNotice'; import { Metadata, Viewport } from 'next'; import { NextIntlClientProvider } from 'next-intl'; import { getMessages } from 'next-intl/server'; @@ -20,18 +21,18 @@ export const viewport: Viewport = { viewportFit: 'cover', themeColor: '#001a4d', }; - + export default async function LocaleLayout({ children, - params: {locale} + params: { locale } }: { children: React.ReactNode; - params: {locale: string}; + params: { locale: string }; }) { // Providing all messages to the client // side is the easiest way to get started const messages = await getMessages(); - + return ( @@ -42,7 +43,8 @@ export default async function LocaleLayout({ {children}