fix: resolve translation routing bugs for MDX pages/posts, fix QA smoke tests, and automate target host network cleanup
All checks were successful
Build & Deploy / 🔍 Prepare (push) Successful in 8s
Build & Deploy / 🧪 QA (push) Successful in 56s
Build & Deploy / 🏗️ Build (push) Successful in 2m18s
Build & Deploy / 🚀 Deploy (push) Successful in 15s
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s

This commit is contained in:
2026-06-10 10:35:06 +02:00
parent 3cbb4ba700
commit d11c27fda0
8 changed files with 138 additions and 11 deletions

View File

@@ -357,7 +357,7 @@ 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"
ssh root@alpha.mintel.me "docker network prune -f"
ssh root@alpha.mintel.me "docker system prune -f --filter 'until=24h'"
- name: 🧹 Post-Deploy Cleanup (Runner)

View File

@@ -44,7 +44,6 @@ jobs:
env:
NEXT_PUBLIC_BASE_URL: ${{ env.TARGET_URL }}
GATEKEEPER_PASSWORD: ${{ secrets.GATEKEEPER_PASSWORD }}
PUPPETEER_EXECUTABLE_PATH: /usr/bin/chromium
run: npx tsx scripts/smoke.ts
- name: 🔔 Notify
if: always()

View File

@@ -56,9 +56,12 @@ export function isPostVisible(post: { frontmatter: { date: string; public?: bool
return !(postDate > now && config.isProduction);
}
import { mapSlugToFileSlug, mapFileSlugToTranslated } from './slugs';
export async function getPostBySlug(slug: string, locale: string): Promise<PostData | null> {
try {
const filePath = path.join(process.cwd(), 'content', 'posts', `${slug}.mdx`);
const fileSlug = await mapSlugToFileSlug(slug, locale);
const filePath = path.join(process.cwd(), 'content', 'posts', `${fileSlug}.mdx`);
const fileContent = await fs.readFile(filePath, 'utf-8');
const { data, content } = matter(fileContent);
@@ -77,8 +80,10 @@ export async function getPostBySlug(slug: string, locale: string): Promise<PostD
// Not JSON
}
const resolvedSlug = await mapFileSlugToTranslated(fileSlug, locale);
return {
slug,
slug: resolvedSlug,
frontmatter: {
title: data.title || '',
date: data.date || '',
@@ -98,8 +103,10 @@ export async function getPostBySlug(slug: string, locale: string): Promise<PostD
}
export async function getPostSlugs(slug: string, locale: string): Promise<Record<string, string>> {
// Mock function, simply returns the slug for all locales
return { de: slug, en: slug };
const fileSlug = await mapSlugToFileSlug(slug, locale);
const de = await mapFileSlugToTranslated(fileSlug, 'de');
const en = await mapFileSlugToTranslated(fileSlug, 'en');
return { de, en };
}
export async function getAllPosts(locale: string): Promise<PostData[]> {

View File

@@ -2,6 +2,7 @@ import fs from 'fs/promises';
import path from 'path';
import matter from 'gray-matter';
import { config } from '@/lib/config';
import { mapSlugToFileSlug } from './slugs';
export interface PageFrontmatter {
title: string;
@@ -47,7 +48,8 @@ function mapDoc(doc: any): PageData {
export async function getPageBySlug(slug: string, locale: string): Promise<PageData | null> {
try {
const filePath = path.join(process.cwd(), 'content', 'pages', `${slug}.mdx`);
const fileSlug = await mapSlugToFileSlug(slug, locale);
const filePath = path.join(process.cwd(), 'content', 'pages', `${fileSlug}.mdx`);
const fileContent = await fs.readFile(filePath, 'utf-8');
const { data, content } = matter(fileContent);

View File

@@ -31,6 +31,11 @@ export async function mapSlugToFileSlug(translatedSlug: string, locale: string):
return slugs.categories[translatedSlug as keyof typeof slugs.categories];
}
// Check posts
if ((slugs as any).posts && translatedSlug in (slugs as any).posts) {
return (slugs as any).posts[translatedSlug];
}
// Return original slug if no mapping found
return translatedSlug;
}
@@ -45,7 +50,7 @@ export async function mapFileSlugToTranslated(fileSlug: string, locale: string):
const messages = getMessages(locale);
const slugs = messages.Slugs;
const sections = [slugs.pages, slugs.products, slugs.categories];
const sections = [slugs.pages, slugs.products, slugs.categories, (slugs as any).posts];
for (const sectionData of sections) {
if (sectionData && typeof sectionData === 'object') {
@@ -63,16 +68,16 @@ export async function mapFileSlugToTranslated(fileSlug: string, locale: string):
/**
* Gets all translated slugs for a section
* @param section - The section name (pages, products, categories)
* @param section - The section name (pages, products, categories, posts)
* @param locale - The current locale
* @returns Object mapping translated slugs to file slugs
*/
export async function getSlugMappings(
section: 'pages' | 'products' | 'categories',
section: 'pages' | 'products' | 'categories' | 'posts',
locale: string,
): Promise<Record<string, string>> {
const messages = getMessages(locale);
const sectionData = messages.Slugs[section];
const sectionData = (messages.Slugs as any)[section];
if (sectionData && typeof sectionData === 'object') {
return sectionData as Record<string, string>;

View File

@@ -47,6 +47,39 @@
"mittelspannungskabel": "medium-voltage-cables",
"hochspannungskabel": "high-voltage-cables",
"solarkabel": "solar-cables"
},
"posts": {
"100-erneuerbare-energie-nur-mit-der-richtigen-kabelinfrastruktur": "100-erneuerbare-energie-nur-mit-der-richtigen-kabelinfrastruktur",
"das-muessen-sie-ueber-erneuerbare-energien-im-jahr-2025-wissen": "das-muessen-sie-ueber-erneuerbare-energien-im-jahr-2025-wissen",
"die-besten-erdkabel-fuer-windkraft-und-solar-jetzt-bei-uns-bestellen": "die-besten-erdkabel-fuer-windkraft-und-solar-jetzt-bei-uns-bestellen",
"die-kunst-der-kabellogistik-der-transport-des-fundamentes-moderner-energienetze": "die-kunst-der-kabellogistik-der-transport-des-fundamentes-moderner-energienetze",
"die-perfekte-kabelanfrage-so-sparen-sie-sich-unnoetige-rueckfragen": "die-perfekte-kabelanfrage-so-sparen-sie-sich-unnoetige-rueckfragen",
"erkenntnisse-ueber-die-gruene-energiewende-herausforderungen-und-chancen": "erkenntnisse-ueber-die-gruene-energiewende-herausforderungen-und-chancen",
"gruene-energie-beginnt-unter-der-erde-und-zwar-mit-plan": "gruene-energie-beginnt-unter-der-erde-und-zwar-mit-plan",
"johannes-gleich-startet-als-senior-key-account-manager-durch": "johannes-gleich-startet-als-senior-key-account-manager-durch",
"kabelabkuerzungen-entschluesselt-der-schluessel-zur-richtigen-kabelwahl": "kabelabkuerzungen-entschluesselt-der-schluessel-zur-richtigen-kabelwahl",
"kabeltrommelqualitaet-die-grundlage-der-kabelzuverlaessigkeit": "kabeltrommelqualitaet-die-grundlage-der-kabelzuverlaessigkeit",
"klimaneutral-bis-2050-was-wir-tun-muessen-um-das-ziel-zu-erreichen": "klimaneutral-bis-2050-was-wir-tun-muessen-um-das-ziel-zu-erreichen",
"klz-im-adressbuch-der-windenergie-2025": "klz-im-adressbuch-der-windenergie-2025",
"klz-waechst-weiter-neue-staerke-im-bereich-financial-sales": "klz-waechst-weiter-neue-staerke-im-bereich-financial-sales",
"kostenvergleich-kupfer-vs-aluminiumkabel-in-windparks-was-lohnt-sich-langfristig": "kostenvergleich-kupfer-vs-aluminiumkabel-in-windparks-was-lohnt-sich-langfristig",
"milliarden-paket-fuer-infrastruktur-der-kabel-boom-steht-bevor": "milliarden-paket-fuer-infrastruktur-der-kabel-boom-steht-bevor",
"n2xsf2y-mittelspannungskabel-energieprojekt": "n2xsf2y-mittelspannungskabel-energieprojekt",
"na2xsf2y-dreileiter-mittelspannungskabel-lieferbar": "na2xsf2y-dreileiter-mittelspannungskabel-lieferbar",
"netzausbau-2025-warum-jede-neue-leitung-ein-schritt-zur-energiewende-ist": "netzausbau-2025-warum-jede-neue-leitung-ein-schritt-zur-energiewende-ist",
"recycling-von-kabeltrommeln-nachhaltigkeit-im-windkraftprojekt": "recycling-von-kabeltrommeln-nachhaltigkeit-im-windkraftprojekt",
"reicht-windenergie-wirklich-nicht-ein-blick-hinter-die-schlagzeilen": "reicht-windenergie-wirklich-nicht-ein-blick-hinter-die-schlagzeilen",
"sicherheit-bei-kabeltrommeln-unfallfrei-und-effizient-arbeiten": "sicherheit-bei-kabeltrommeln-unfallfrei-und-effizient-arbeiten",
"so-waehlen-sie-das-richtige-kabel-fuer-ihr-naechstes-projekt-aus": "so-waehlen-sie-das-richtige-kabel-fuer-ihr-naechstes-projekt-aus",
"von-smart-bis-nachhaltig-so-sieht-die-energiewirtschaft-in-naher-zukunft-aus": "von-smart-bis-nachhaltig-so-sieht-die-energiewirtschaft-in-naher-zukunft-aus",
"warum-die-richtigen-kabel-der-geheime-held-der-gruenen-energie-sind": "warum-die-richtigen-kabel-der-geheime-held-der-gruenen-energie-sind",
"warum-windpark-netzanschlusskabel-extremen-belastungen-standhalten-muessen": "warum-windpark-netzanschlusskabel-extremen-belastungen-standhalten-muessen",
"was-macht-ein-erstklassiges-kabel-aus-finden-sie-es-hier-heraus": "was-macht-ein-erstklassiges-kabel-aus-finden-sie-es-hier-heraus",
"welche-kabel-fuer-windkraft-unterschiede-von-nieder-bis-hoechstspannung-erklaert": "welche-kabel-fuer-windkraft-unterschiede-von-nieder-bis-hoechstspannung-erklaert",
"wie-die-kabelbranche-nachhaltigkeit-und-erneuerbare-energien-vorantreibt": "wie-die-kabelbranche-nachhaltigkeit-und-erneuerbare-energien-vorantreibt",
"willkommen-in-der-zukunft-von-klz-unsere-neue-website-ist-online": "willkommen-in-der-zukunft-von-klz-unsere-neue-website-ist-online",
"windparkbau-im-fokus-drei-typische-kabelherausforderungen": "windparkbau-im-fokus-drei-typische-kabelherausforderungen",
"zukunft-sichern-mit-h1z2z2-k-unser-solarkabel-zur-intersolar-2025": "zukunft-sichern-mit-h1z2z2-k-unser-solarkabel-zur-intersolar-2025"
}
},
"Index": {

View File

@@ -47,6 +47,37 @@
"medium-voltage-cables": "medium-voltage-cables",
"high-voltage-cables": "high-voltage-cables",
"solar-cables": "solar-cables"
},
"posts": {
"100-renewable-energy-only-with-the-right-cable-infrastructure": "100-erneuerbare-energie-nur-mit-der-richtigen-kabelinfrastruktur",
"billion-euro-package-for-infrastructure-the-cable-boom-is-coming": "milliarden-paket-fuer-infrastruktur-der-kabel-boom-steht-bevor",
"cable-abbreviations-decoded-the-key-to-choosing-the-right-cable": "kabelabkuerzungen-entschluesselt-der-schluessel-zur-richtigen-kabelwahl",
"cable-drum-quality-the-foundation-of-cable-reliability": "kabeltrommelqualitaet-die-grundlage-der-kabelzuverlaessigkeit",
"cable-drum-safety-ensuring-smooth-operations-and-accident-free-environments": "sicherheit-bei-kabeltrommeln-unfallfrei-und-effizient-arbeiten",
"climate-neutral-by-2050-what-we-need-to-do-to-achieve-this-goal": "klimaneutral-bis-2050-was-wir-tun-muessen-um-das-ziel-zu-erreichen",
"cost-comparison-copper-vs-aluminum-cables-in-wind-farms-which-is-worthwhile-in-the-long-term": "kostenvergleich-kupfer-vs-aluminiumkabel-in-windparks-was-lohnt-sich-langfristig",
"expanding-the-grid-by-2025-building-the-foundation-for-a-successful-energy-transition": "netzausbau-2025-warum-jede-neue-leitung-ein-schritt-zur-energiewende-ist",
"eye-opening-realities-of-green-energy-transformation": "erkenntnisse-ueber-die-gruene-energiewende-herausforderungen-und-chancen",
"focus-on-wind-farm-construction-three-typical-cable-challenges": "windparkbau-im-fokus-drei-typische-kabelherausforderungen",
"from-smart-to-sustainable-this-is-what-the-energy-industry-will-look-like-in-the-near-future": "von-smart-bis-nachhaltig-so-sieht-die-energiewirtschaft-in-naher-zukunft-aus",
"green-energy-starts-underground-and-with-a-plan": "gruene-energie-beginnt-unter-der-erde-und-zwar-mit-plan",
"how-the-cable-industry-is-driving-sustainability-and-renewable-energies-forward": "wie-die-kabelbranche-nachhaltigkeit-und-erneuerbare-energien-vorantreibt",
"how-the-right-cables-quietly-power-the-green-energy-revolution": "warum-die-richtigen-kabel-der-geheime-held-der-gruenen-energie-sind",
"how-to-choose-the-right-cable-for-your-next-project": "so-waehlen-sie-das-richtige-kabel-fuer-ihr-naechstes-projekt-aus",
"is-wind-energy-really-enough-a-deeper-dive-behind-the-headlines": "reicht-windenergie-wirklich-nicht-ein-blick-hinter-die-schlagzeilen",
"klz-in-the-directory-of-wind-energy-2025": "klz-im-adressbuch-der-windenergie-2025",
"na2xsf2y-three-conductor-medium-voltage-cable-available": "na2xsf2y-dreileiter-mittelspannungskabel-lieferbar",
"recycling-of-cable-drums-sustainability-in-wind-power-projects": "recycling-von-kabeltrommeln-nachhaltigkeit-im-windkraftprojekt",
"securing-the-future-with-h1z2z2-k-our-solar-cable-for-intersolar-2025": "zukunft-sichern-mit-h1z2z2-k-unser-solarkabel-zur-intersolar-2025",
"the-art-of-cable-logistics-moving-the-backbone-of-modern-energy-networks": "die-kunst-der-kabellogistik-der-transport-des-fundamentes-moderner-energienetze",
"the-best-underground-cables-for-wind-power-and-solar-order-from-us-now": "die-besten-erdkabel-fuer-windkraft-und-solar-jetzt-bei-uns-bestellen",
"the-perfect-cable-inquiry-how-to-save-yourself-unnecessary-queries": "die-perfekte-kabelanfrage-so-sparen-sie-sich-unnoetige-rueckfragen",
"this-what-you-need-to-know-about-renewable-energies-in-2025": "das-muessen-sie-ueber-erneuerbare-energien-im-jahr-2025-wissen",
"welcome-to-the-future-of-klz-our-new-website-is-live": "willkommen-in-der-zukunft-von-klz-unsere-neue-website-ist-online",
"what-makes-a-first-class-cable-find-out-here": "was-macht-ein-erstklassiges-kabel-aus-finden-sie-es-hier-heraus",
"which-cables-for-wind-power-differences-from-low-to-extra-high-voltage-explained-2": "welche-kabel-fuer-windkraft-unterschiede-von-nieder-bis-hoechstspannung-erklaert",
"why-the-n2xsf2y-is-the-ideal-cable-for-your-energy-project": "n2xsf2y-mittelspannungskabel-energieprojekt",
"why-wind-farm-grid-connection-cables-must-withstand-extreme-loads": "warum-windpark-netzanschlusskabel-extremen-belastungen-standhalten-muessen"
}
},
"Index": {

50
tests/pages.test.ts Normal file
View File

@@ -0,0 +1,50 @@
import { describe, it, expect } from 'vitest';
import { getPageBySlug } from '../lib/pages';
import { getPostBySlug, getPostSlugs } from '../lib/blog';
describe('getPageBySlug translation routing', () => {
it('should resolve standard page in German', async () => {
const page = await getPageBySlug('impressum', 'de');
expect(page).not.toBeNull();
expect(page?.frontmatter.title).toBe('Impressum');
});
it('should resolve translated slug "legal-notice" for English', async () => {
const page = await getPageBySlug('legal-notice', 'en');
expect(page).not.toBeNull();
expect(page?.frontmatter.title).toBe('Impressum');
});
it('should resolve translated slug "privacy-policy" for English', async () => {
const page = await getPageBySlug('privacy-policy', 'en');
expect(page).not.toBeNull();
expect(page?.frontmatter.title).toBe('Datenschutzerklärung');
});
it('should return null for non-existent slugs', async () => {
const page = await getPageBySlug('does-not-exist', 'de');
expect(page).toBeNull();
});
});
describe('getPostBySlug and getPostSlugs translation routing', () => {
it('should resolve post by English slug', async () => {
const post = await getPostBySlug(
'focus-on-wind-farm-construction-three-typical-cable-challenges',
'en',
);
expect(post).not.toBeNull();
expect(post?.frontmatter.title).toBe(
'Windparkbau im Fokus: drei typische Kabelherausforderungen',
);
});
it('should return correct localized post slugs', async () => {
const slugs = await getPostSlugs(
'focus-on-wind-farm-construction-three-typical-cable-challenges',
'en',
);
expect(slugs.de).toBe('windparkbau-im-fokus-drei-typische-kabelherausforderungen');
expect(slugs.en).toBe('focus-on-wind-farm-construction-three-typical-cable-challenges');
});
});