Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 037794c4b7 | |||
| 06147dade4 | |||
| 08e16ce754 | |||
| df37f23270 | |||
| 503676dab5 | |||
| 52480b4f55 |
@@ -29,8 +29,9 @@ jobs:
|
||||
run: |
|
||||
rm -rf .next .turbo node_modules || true
|
||||
pnpm install --frozen-lockfile
|
||||
- name: 🧪 Parallel Checks
|
||||
- name: 🧪 QA Checks
|
||||
env:
|
||||
NODE_OPTIONS: "--max-old-space-size=4096"
|
||||
run: |
|
||||
pnpm lint &
|
||||
pnpm build &
|
||||
wait
|
||||
pnpm lint
|
||||
pnpm build
|
||||
|
||||
@@ -93,6 +93,15 @@ jobs:
|
||||
echo "short_sha=$SHORT_SHA"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
|
||||
echo "--- DEBUG ---"
|
||||
echo "TARGET: $TARGET"
|
||||
echo "IMAGE_TAG: $IMAGE_TAG"
|
||||
echo "ENV_FILE: $ENV_FILE"
|
||||
echo "PRIMARY_HOST: $PRIMARY_HOST"
|
||||
echo "TRAEFIK_RULE: $TRAEFIK_RULE"
|
||||
echo "PROJECT_NAME: $PRJ-$TARGET"
|
||||
echo "--- END DEBUG ---"
|
||||
|
||||
# ⏳ Wait for Upstream Packages/Images if Tagged
|
||||
if [[ "${{ github.ref_type }}" == "tag" ]]; then
|
||||
echo "🔎 Checking for @mintel dependencies in package.json..."
|
||||
@@ -167,12 +176,16 @@ jobs:
|
||||
pnpm install --no-frozen-lockfile
|
||||
- name: 🧪 QA Checks
|
||||
if: github.event.inputs.skip_checks != 'true'
|
||||
env:
|
||||
NODE_OPTIONS: "--max-old-space-size=4096"
|
||||
run: |
|
||||
pnpm lint
|
||||
pnpm exec tsc --noEmit
|
||||
pnpm test run
|
||||
- name: 🏗️ Build Test
|
||||
if: github.event.inputs.skip_checks != 'true'
|
||||
env:
|
||||
NODE_OPTIONS: "--max-old-space-size=4096"
|
||||
run: pnpm build
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
@@ -231,14 +244,7 @@ jobs:
|
||||
IMAGE_TAG: ${{ needs.prepare.outputs.image_tag }}
|
||||
PROJECT_NAME: ${{ needs.prepare.outputs.project_name }}
|
||||
NEXT_PUBLIC_BASE_URL: ${{ needs.prepare.outputs.next_public_url }}
|
||||
|
||||
# Secrets mapping (Database & CMS)
|
||||
PAYLOAD_SECRET: ${{ (needs.prepare.outputs.target == 'testing' && secrets.TESTING_PAYLOAD_SECRET) || (needs.prepare.outputs.target == 'staging' && secrets.STAGING_PAYLOAD_SECRET) || secrets.PAYLOAD_SECRET || secrets.MINTEL_PRIVATE_TOKEN || secrets.DIRECTUS_SECRET || vars.PAYLOAD_SECRET || 'you-need-to-set-a-payload-secret' }}
|
||||
DATABASE_URI: ${{ (needs.prepare.outputs.target == 'testing' && secrets.TESTING_DATABASE_URI) || (needs.prepare.outputs.target == 'staging' && secrets.STAGING_DATABASE_URI) || secrets.DATABASE_URI || vars.DATABASE_URI }}
|
||||
POSTGRES_DB: ${{ secrets.POSTGRES_DB || vars.POSTGRES_DB || 'payload' }}
|
||||
POSTGRES_USER: ${{ secrets.POSTGRES_USER || vars.POSTGRES_USER || 'directus' }}
|
||||
POSTGRES_PASSWORD: ${{ (needs.prepare.outputs.target == 'testing' && secrets.TESTING_POSTGRES_PASSWORD) || (needs.prepare.outputs.target == 'staging' && secrets.STAGING_POSTGRES_PASSWORD) || secrets.POSTGRES_PASSWORD || vars.POSTGRES_PASSWORD || 'directus' }}
|
||||
|
||||
MINTEL_PRIVATE_TOKEN: ${{ secrets.MINTEL_PRIVATE_TOKEN }}
|
||||
|
||||
# Secrets mapping (Mail)
|
||||
MAIL_HOST: ${{ secrets.SMTP_HOST || vars.SMTP_HOST }}
|
||||
@@ -296,13 +302,6 @@ jobs:
|
||||
PROJECT_COLOR=$PROJECT_COLOR
|
||||
LOG_LEVEL=$LOG_LEVEL
|
||||
|
||||
# Database & Payload
|
||||
DATABASE_URI=\${DATABASE_URI:-postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@mb-grid-db:5432/$POSTGRES_DB}
|
||||
PAYLOAD_SECRET=${PAYLOAD_SECRET:-you-need-to-set-a-payload-secret}
|
||||
POSTGRES_DB=$POSTGRES_DB
|
||||
POSTGRES_USER=$POSTGRES_USER
|
||||
POSTGRES_PASSWORD=$POSTGRES_PASSWORD
|
||||
|
||||
# Mail
|
||||
MAIL_HOST=$MAIL_HOST
|
||||
MAIL_PORT=$MAIL_PORT
|
||||
@@ -351,28 +350,6 @@ jobs:
|
||||
ssh root@alpha.mintel.me "cd $SITE_DIR && docker compose -p '${{ needs.prepare.outputs.project_name }}' --env-file '$ENV_FILE' pull"
|
||||
ssh root@alpha.mintel.me "cd $SITE_DIR && docker compose -p '${{ needs.prepare.outputs.project_name }}' --env-file '$ENV_FILE' up -d --remove-orphans"
|
||||
|
||||
# Apply Payload Migrations using the target app container's programmatic endpoint
|
||||
ssh root@alpha.mintel.me "cd $SITE_DIR && echo '→ Waiting for DB and Running Payload Migrations...' && \
|
||||
for i in {1..15}; do \
|
||||
echo \"Attempt \$i...\"; \
|
||||
docker compose -p '${{ needs.prepare.outputs.project_name }}' --env-file '$ENV_FILE' exec -T mb-grid-app sh -c 'curl -s -i -X POST -H \"Authorization: Bearer \$PAYLOAD_SECRET\" http://localhost:3000/api/payload/migrate' > /tmp/migrate_res 2>&1; \
|
||||
if grep -q \"200 OK\" /tmp/migrate_res; then \
|
||||
echo \"✅ Migrations successful!\"; \
|
||||
cat /tmp/migrate_res; \
|
||||
break; \
|
||||
else \
|
||||
echo \"❌ Attempt \$i failed. Response:\"; \
|
||||
cat /tmp/migrate_res; \
|
||||
if [ \$i -eq 15 ]; then \
|
||||
echo \"❌ Migration failed after 15 attempts! Dumping app logs...\"; \
|
||||
docker compose -p '${{ needs.prepare.outputs.project_name }}' --env-file '$ENV_FILE' logs --tail 100 mb-grid-app; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
echo \"⏳ Retrying in 5s...\"; \
|
||||
sleep 5; \
|
||||
fi; \
|
||||
done"
|
||||
|
||||
ssh root@alpha.mintel.me "docker system prune -f --filter 'until=24h'"
|
||||
|
||||
- name: 🧹 Post-Deploy Cleanup (Runner)
|
||||
@@ -409,41 +386,6 @@ jobs:
|
||||
run: |
|
||||
pnpm store prune
|
||||
pnpm install --no-frozen-lockfile
|
||||
- name: 📦 Cache APT Packages
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: /var/cache/apt/archives
|
||||
key: apt-cache-${{ runner.os }}-${{ runner.arch }}-chromium
|
||||
|
||||
- name: 💾 Cache Chromium
|
||||
id: cache-chromium
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: /usr/bin/chromium
|
||||
key: ${{ runner.os }}-chromium-native-${{ hashFiles('package.json') }}
|
||||
|
||||
- name: 🔍 Install Chromium (Native & ARM64)
|
||||
if: steps.cache-chromium.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
rm -f /etc/apt/apt.conf.d/docker-clean
|
||||
apt-get update
|
||||
apt-get install -y gnupg wget ca-certificates
|
||||
OS_ID=$(. /etc/os-release && echo $ID)
|
||||
CODENAME=$(. /etc/os-release && echo $VERSION_CODENAME)
|
||||
if [ "$OS_ID" = "debian" ]; then
|
||||
apt-get install -y chromium
|
||||
else
|
||||
mkdir -p /etc/apt/keyrings
|
||||
KEY_ID="82BB6851C64F6880"
|
||||
wget -qO- "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x$KEY_ID" | gpg --dearmor > /etc/apt/keyrings/xtradeb.gpg
|
||||
echo "deb [signed-by=/etc/apt/keyrings/xtradeb.gpg] http://ppa.launchpad.net/xtradeb/apps/ubuntu $CODENAME main" > /etc/apt/sources.list.d/xtradeb-ppa.list
|
||||
printf "Package: *\nPin: release o=LP-PPA-xtradeb-apps\nPin-Priority: 1001\n" > /etc/apt/preferences.d/xtradeb
|
||||
apt-get update
|
||||
apt-get install -y --allow-downgrades chromium
|
||||
fi
|
||||
[ -f /usr/bin/chromium ] && ln -sf /usr/bin/chromium /usr/bin/google-chrome
|
||||
[ -f /usr/bin/chromium ] && ln -sf /usr/bin/chromium /usr/bin/chromium-browser
|
||||
|
||||
# ── Critical Smoke Tests (MUST pass) ──────────────────────────────────
|
||||
- name: 🏥 CMS Deep Health Check
|
||||
env:
|
||||
@@ -465,14 +407,6 @@ jobs:
|
||||
UMAMI_API_ENDPOINT: ${{ secrets.UMAMI_API_ENDPOINT || secrets.NEXT_PUBLIC_UMAMI_SCRIPT_URL || vars.UMAMI_API_ENDPOINT || 'https://analytics.infra.mintel.me' }}
|
||||
SENTRY_DSN: ${{ secrets.SENTRY_DSN || vars.SENTRY_DSN }}
|
||||
|
||||
- name: 📝 E2E Form Submission Test
|
||||
if: always() && steps.deps.outcome == 'success'
|
||||
env:
|
||||
NEXT_PUBLIC_BASE_URL: ${{ needs.prepare.outputs.next_public_url }}
|
||||
GATEKEEPER_PASSWORD: ${{ secrets.GATEKEEPER_PASSWORD || vars.GATEKEEPER_PASSWORD }}
|
||||
PUPPETEER_EXECUTABLE_PATH: /usr/bin/chromium
|
||||
run: pnpm test run
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
# JOB 6: Notifications
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -21,6 +21,7 @@ ENV UMAMI_API_ENDPOINT=$UMAMI_API_ENDPOINT
|
||||
ENV NEXT_PUBLIC_UMAMI_WEBSITE_ID=$NEXT_PUBLIC_UMAMI_WEBSITE_ID
|
||||
ENV SKIP_RUNTIME_ENV_VALIDATION=true
|
||||
ENV CI=true
|
||||
ENV NODE_OPTIONS="--max-old-space-size=4096"
|
||||
|
||||
# Enable pnpm
|
||||
RUN corepack enable && corepack prepare pnpm@10.3.0 --activate
|
||||
|
||||
@@ -14,7 +14,7 @@ export default async function Page({ params }: PageProps) {
|
||||
try {
|
||||
const mdx = await import(`@/content/${locale}/avb.mdx`);
|
||||
Content = mdx.default;
|
||||
} catch (e) {
|
||||
} catch {
|
||||
notFound();
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ export default async function Page({ params }: PageProps) {
|
||||
try {
|
||||
const mdx = await import(`@/content/${locale}/datenschutz.mdx`);
|
||||
Content = mdx.default;
|
||||
} catch (e) {
|
||||
} catch {
|
||||
notFound();
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ export default async function Page({ params }: PageProps) {
|
||||
try {
|
||||
const mdx = await import(`@/content/${locale}/impressum.mdx`);
|
||||
Content = mdx.default;
|
||||
} catch (e) {
|
||||
} catch {
|
||||
notFound();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from "react";
|
||||
import { Metadata } from "next";
|
||||
import { notFound } from "next/navigation";
|
||||
|
||||
import HomeContent from "@/components/HomeContent";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
@@ -21,7 +21,7 @@ export default async function Page({ params }: PageProps) {
|
||||
try {
|
||||
const mdx = await import(`@/content/${locale}/index.mdx`);
|
||||
Content = mdx.default;
|
||||
} catch (e) {
|
||||
} catch {
|
||||
// Fallback if MDX is not yet created for this locale
|
||||
return <HomeContent />;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ export default async function Page({ params }: PageProps) {
|
||||
try {
|
||||
const mdx = await import(`@/content/${locale}/about.mdx`);
|
||||
Content = mdx.default;
|
||||
} catch (e) {
|
||||
} catch {
|
||||
notFound();
|
||||
}
|
||||
|
||||
|
||||
@@ -163,7 +163,11 @@ export default function Contact() {
|
||||
<div className="absolute inset-0 border-2 border-accent/0 group-hover:border-accent/20 transition-all duration-500 z-10 pointer-events-none rounded-[2.5rem]" />
|
||||
<iframe
|
||||
className="w-full h-full border-0"
|
||||
title={t("info.mapTitle") || "Location Map"}
|
||||
title={
|
||||
t.has("info.mapTitle")
|
||||
? t("info.mapTitle")
|
||||
: "Location Map"
|
||||
}
|
||||
src="https://www.openstreetmap.org/export/embed.html?bbox=9.445,48.815,9.465,48.825&layer=mapnik&marker=48.8198,9.4552"
|
||||
></iframe>
|
||||
</div>
|
||||
|
||||
10
lib/env.ts
10
lib/env.ts
@@ -24,14 +24,20 @@ const envExtension = {
|
||||
/**
|
||||
* Full schema including Mintel base and refinements
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
export const envSchema = withMintelRefinements(
|
||||
z.object(mintelEnvSchema).extend(envExtension),
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(z as any).object(mintelEnvSchema).extend(envExtension) as any,
|
||||
);
|
||||
|
||||
/**
|
||||
* Validated environment object.
|
||||
*/
|
||||
export const env = validateMintelEnv(envExtension);
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export const env = validateMintelEnv(envExtension) as any;
|
||||
|
||||
/**
|
||||
* For legacy compatibility with existing code.
|
||||
|
||||
2
next-env.d.ts
vendored
2
next-env.d.ts
vendored
@@ -1,6 +1,6 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
import "./.next/dev/types/routes.d.ts";
|
||||
import "./.next/types/routes.d.ts";
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
# Payload CMS Database Backup
|
||||
# Creates a timestamped pg_dump of the Payload Postgres database.
|
||||
# Usage: npm run backup:db
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
set -euo pipefail
|
||||
|
||||
# Load environment variables
|
||||
if [ -f .env ]; then
|
||||
set -a; source .env; set +a
|
||||
fi
|
||||
|
||||
# Fallback for local development if not in .env
|
||||
DB_NAME="${POSTGRES_DB:-payload}"
|
||||
DB_USER="${POSTGRES_USER:-postgres}"
|
||||
# For production, we need the container name.
|
||||
# We'll use the PROJECT_NAME to find it if possible, otherwise use a default.
|
||||
PROJECT_NAME="${PROJECT_NAME:-mb-grid-solutions-production}"
|
||||
DB_CONTAINER="${DB_CONTAINER:-${PROJECT_NAME}-mb-grid-db-1}"
|
||||
BACKUP_DIR="./backups"
|
||||
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
|
||||
BACKUP_FILE="${BACKUP_DIR}/payload_${TIMESTAMP}.sql.gz"
|
||||
|
||||
# Ensure backup directory exists
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
|
||||
# Check if container is running
|
||||
if ! docker ps --format '{{.Names}}' | grep -q "$DB_CONTAINER"; then
|
||||
echo "❌ Database container '$DB_CONTAINER' is not running."
|
||||
echo " Check your docker-compose status."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "📦 Backing up Payload database..."
|
||||
echo " Container: $DB_CONTAINER"
|
||||
echo " Database: $DB_NAME"
|
||||
echo " Output: $BACKUP_FILE"
|
||||
|
||||
# Run pg_dump inside the container and compress
|
||||
# We use directus as user for now if we haven't fully switched to postgres user in all environments
|
||||
# But the script should be consistent with the environment.
|
||||
docker exec "$DB_CONTAINER" pg_dump -U "$DB_USER" -d "$DB_NAME" --clean --if-exists | gzip > "$BACKUP_FILE"
|
||||
|
||||
# Show result
|
||||
SIZE=$(du -h "$BACKUP_FILE" | cut -f1)
|
||||
echo ""
|
||||
echo "✅ Backup complete: $BACKUP_FILE ($SIZE)"
|
||||
echo ""
|
||||
|
||||
# Show existing backups
|
||||
echo "📋 Available backups:"
|
||||
ls -lh "$BACKUP_DIR"/*.sql.gz 2>/dev/null | awk '{print " " $NF " (" $5 ")"}'
|
||||
Reference in New Issue
Block a user