6 Commits

Author SHA1 Message Date
71b30ba8c5 fix: sentry issues
All checks were successful
Build & Deploy / 🔍 Prepare Environment (push) Successful in 5s
Build & Deploy / 🧪 QA (push) Successful in 1m23s
Build & Deploy / 🏗️ Build (push) Successful in 2m55s
Build & Deploy / 🚀 Deploy (push) Successful in 18s
Build & Deploy / 🔔 Notifications (push) Successful in 1s
2026-02-10 13:49:22 +01:00
e9ea253021 fix: build and lint
All checks were successful
Build & Deploy / 🔍 Prepare Environment (push) Successful in 5s
Build & Deploy / 🧪 QA (push) Successful in 1m22s
Build & Deploy / 🏗️ Build (push) Successful in 1m54s
Build & Deploy / 🚀 Deploy (push) Successful in 16s
Build & Deploy / 🔔 Notifications (push) Successful in 1s
2026-02-10 00:31:51 +01:00
237bd46593 feat: more transparency
Some checks failed
Build & Deploy / 🔍 Prepare Environment (push) Successful in 4s
Build & Deploy / 🧪 QA (push) Failing after 39s
Build & Deploy / 🏗️ Build (push) Failing after 30s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🔔 Notifications (push) Successful in 2s
2026-02-10 00:22:53 +01:00
40ebdb31d9 fix: analytics 2026-02-10 00:15:36 +01:00
8f39ec3d35 fix: resolve lint errors in layout and route by updating analytics interface
Some checks failed
Build & Deploy / 🔍 Prepare Environment (push) Successful in 9s
Build & Deploy / 🧪 QA (push) Successful in 1m22s
Build & Deploy / 🏗️ Build (push) Failing after 1m59s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🔔 Notifications (push) Successful in 2s
2026-02-10 00:01:31 +01:00
7734440b90 refactor: remove arrogant marketing terms and localize to German
Some checks failed
Build & Deploy / 🔍 Prepare Environment (push) Successful in 8s
Build & Deploy / 🧪 QA (push) Failing after 48s
Build & Deploy / 🏗️ Build (push) Failing after 3m1s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🔔 Notifications (push) Successful in 2s
2026-02-09 23:50:44 +01:00
15 changed files with 157 additions and 39 deletions

View File

@@ -51,7 +51,8 @@ COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
# Ensure the cache directory specifically is writeable (Mintel Standard #16)
RUN mkdir -p .next/cache && chown -R nextjs:nodejs .next/cache
# We copy a small directory or just create it via COPY to avoid RUN chown permission issues
COPY --from=builder --chown=nextjs:nodejs /app/.next/cache ./.next/cache
USER nextjs

View File

@@ -118,8 +118,8 @@ export default async function RootLayout({
const { headers } = await import("next/headers");
const requestHeaders = await headers();
if ("setServerContext" in serverServices.analytics) {
(serverServices.analytics as any).setServerContext({
if (serverServices.analytics.setServerContext) {
serverServices.analytics.setServerContext({
userAgent: requestHeaders.get("user-agent") || undefined,
language:
requestHeaders.get("accept-language")?.split(",")[0] || undefined,

View File

@@ -86,7 +86,7 @@ export default async function Image() {
letterSpacing: "0.1em",
}}
>
Engineering Excellence
Technische Beratung
</div>
</div>
@@ -118,6 +118,8 @@ export default async function Image() {
{/* Title */}
<div
style={{
display: "flex",
flexDirection: "row",
fontSize: "72px",
fontWeight: "900",
color: "#0f172a",
@@ -126,12 +128,19 @@ export default async function Image() {
letterSpacing: "-0.02em",
}}
>
MB Grid <span style={{ color: "#10b981" }}>Solutions</span>
MB Grid{" "}
<span
style={{ color: "#10b981", display: "flex", marginLeft: "16px" }}
>
Solutions
</span>
</div>
{/* Subtitle */}
<div
style={{
display: "flex",
flexDirection: "column",
fontSize: "32px",
fontWeight: "500",
color: "#64748b",
@@ -140,9 +149,8 @@ export default async function Image() {
lineHeight: 1.4,
}}
>
Energiekabelprojekte & Technische Beratung
<br />
bis 110 kV
<span>Energiekabelprojekte & Technische Beratung</span>
<span>bis 110 kV</span>
</div>
</div>

View File

@@ -86,7 +86,7 @@ export default async function Image() {
letterSpacing: "0.1em",
}}
>
Engineering Excellence
Technische Beratung
</div>
</div>
@@ -118,6 +118,8 @@ export default async function Image() {
{/* Title */}
<div
style={{
display: "flex",
flexDirection: "row",
fontSize: "72px",
fontWeight: "900",
color: "#0f172a",
@@ -126,12 +128,19 @@ export default async function Image() {
letterSpacing: "-0.02em",
}}
>
MB Grid <span style={{ color: "#10b981" }}>Solutions</span>
MB Grid{" "}
<span
style={{ color: "#10b981", display: "flex", marginLeft: "16px" }}
>
Solutions
</span>
</div>
{/* Subtitle */}
<div
style={{
display: "flex",
flexDirection: "column",
fontSize: "32px",
fontWeight: "500",
color: "#64748b",
@@ -140,9 +149,8 @@ export default async function Image() {
lineHeight: 1.4,
}}
>
Energiekabelprojekte & Technische Beratung
<br />
bis 110 kV
<span>Energiekabelprojekte & Technische Beratung</span>
<span>bis 110 kV</span>
</div>
</div>

View File

@@ -10,8 +10,8 @@ export async function POST(req: Request) {
// Set analytics context from request headers for high-fidelity server-side tracking
// This fulfills the "server-side via nextjs proxy" requirement
if ("setServerContext" in services.analytics) {
(services.analytics as any).setServerContext({
if (services.analytics.setServerContext) {
services.analytics.setServerContext({
userAgent: req.headers.get("user-agent") || undefined,
language: req.headers.get("accept-language")?.split(",")[0] || undefined,
referrer: req.headers.get("referer") || undefined,

View File

@@ -204,7 +204,7 @@ export default function Home() {
<div className="absolute inset-0 bg-accent/10 opacity-0 group-hover:opacity-100 transition-opacity duration-500 z-10 pointer-events-none" />
<Image
src="/media/cables/hs-kabel.png"
alt="Technical Engineering"
alt="Technische Beratung"
width={800}
height={600}
className="w-full h-[400px] md:h-[500px] object-cover hover:scale-105 transition-transform duration-700"

View File

@@ -275,7 +275,7 @@ const Layout = ({ children }: { children: React.ReactNode }) => {
<div className="bg-slate-950 py-6 border-t border-white/5">
<div className="container-custom">
<p className="text-[10px] uppercase tracking-[0.2em] text-slate-600 text-center md:text-left">
Website developed by{" "}
Website entwickelt von{" "}
<a
href="https://mintel.me"
target="_blank"

View File

@@ -73,4 +73,15 @@ export interface AnalyticsService {
* ```
*/
trackPageview(url?: string): void;
/**
* Set the server-side context for the current request.
* This is used for server-side tracking (e.g. from Next.js proxy).
*/
setServerContext?(context: {
userAgent?: string;
language?: string;
referrer?: string;
ip?: string;
}): void;
}

View File

@@ -68,4 +68,16 @@ export class NoopAnalyticsService implements AnalyticsService {
trackPageview(_url?: string) {
// intentionally noop - analytics are disabled
}
/**
* No-op implementation of setServerContext.
*/
setServerContext(_context: {
userAgent?: string;
language?: string;
referrer?: string;
ip?: string;
}) {
// intentionally noop - analytics are disabled
}
}

View File

@@ -66,7 +66,11 @@ export class UmamiAnalyticsService implements AnalyticsService {
const payload = {
website: this.websiteId,
hostname:
typeof window !== "undefined" ? window.location.hostname : "server",
typeof window !== "undefined"
? window.location.hostname
: this.serverContext?.referrer
? new URL(this.serverContext.referrer).hostname
: "server",
screen:
typeof window !== "undefined"
? `${window.screen.width}x${window.screen.height}`
@@ -131,7 +135,9 @@ export class UmamiAnalyticsService implements AnalyticsService {
url:
typeof window !== "undefined"
? window.location.pathname + window.location.search
: undefined,
: this.serverContext?.referrer
? new URL(this.serverContext.referrer).pathname
: "/",
});
}
@@ -144,7 +150,9 @@ export class UmamiAnalyticsService implements AnalyticsService {
url ||
(typeof window !== "undefined"
? window.location.pathname + window.location.search
: undefined),
: this.serverContext?.referrer
? new URL(this.serverContext.referrer).pathname
: "/"),
});
}
}

View File

@@ -31,33 +31,63 @@ export class PinoLoggerService implements LoggerService {
}
trace(msg: string, ...args: unknown[]) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
this.logger.trace(msg, ...(args as any));
if (args.length > 0 && typeof args[0] === "object" && args[0] !== null) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(this.logger as any).trace(args[0] as object, msg, ...args.slice(1));
} else {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(this.logger as any).trace(msg, ...args);
}
}
debug(msg: string, ...args: unknown[]) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
this.logger.debug(msg, ...(args as any));
if (args.length > 0 && typeof args[0] === "object" && args[0] !== null) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(this.logger as any).debug(args[0] as object, msg, ...args.slice(1));
} else {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(this.logger as any).debug(msg, ...args);
}
}
info(msg: string, ...args: unknown[]) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
this.logger.info(msg, ...(args as any));
if (args.length > 0 && typeof args[0] === "object" && args[0] !== null) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(this.logger as any).info(args[0] as object, msg, ...args.slice(1));
} else {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(this.logger as any).info(msg, ...args);
}
}
warn(msg: string, ...args: unknown[]) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
this.logger.warn(msg, ...(args as any));
if (args.length > 0 && typeof args[0] === "object" && args[0] !== null) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(this.logger as any).warn(args[0] as object, msg, ...args.slice(1));
} else {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(this.logger as any).warn(msg, ...args);
}
}
error(msg: string, ...args: unknown[]) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
this.logger.error(msg, ...(args as any));
if (args.length > 0 && typeof args[0] === "object" && args[0] !== null) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(this.logger as any).error(args[0] as object, msg, ...args.slice(1));
} else {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(this.logger as any).error(msg, ...args);
}
}
fatal(msg: string, ...args: unknown[]) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
this.logger.fatal(msg, ...(args as any));
if (args.length > 0 && typeof args[0] === "object" && args[0] !== null) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(this.logger as any).fatal(args[0] as object, msg, ...args.slice(1));
} else {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(this.logger as any).fatal(msg, ...args);
}
}
child(bindings: Record<string, unknown>): LoggerService {

View File

@@ -1,7 +1,7 @@
{
"Index": {
"hero": {
"tag": "Engineering Excellence",
"tag": "Technische Beratung",
"title": "Spezialisierter Partner für Energiekabelprojekte",
"titleHighlight": "Energiekabelprojekte",
"subtitle": "Herstellerneutrale technische Beratung für Ihre Projekte in Mittel- und Hochspannungsnetzen bis zu 110 kV.",
@@ -31,7 +31,7 @@
"expertise": {
"tag": "Expertise",
"title": "Anwendungen & Zielgruppen",
"description": "Wir unterstützen Akteure der Energiewende bei der Realisierung komplexer Kabelprojekte mit höchster Präzision.",
"description": "Wir unterstützen Sie bei der Realisierung Ihrer Kabelprojekte.",
"groups": [
"Energieversorger",
"Ingenieurbüros",
@@ -83,16 +83,16 @@
"datenschutz": "Datenschutz",
"agb": "AGB",
"rights": "Alle Rechte vorbehalten.",
"madeWith": "Made with",
"precision": "precision",
"inGermany": "in Germany"
"madeWith": "Entwickelt mit",
"precision": "Präzision",
"inGermany": "in Deutschland"
}
},
"About": {
"hero": {
"tagline": "Über uns",
"title": "Wir gestalten die Infrastructure der Zukunft",
"subtitle": "MB Grid Solution steht für technische Exzellenz in der Energiekabeltechnologie. Wir verstehen uns als Ihr technischer Lotse."
"title": "Zuverlässige Begleitung für Ihre Netzinfrastruktur",
"subtitle": "Herstellerneutrale Beratung in der Energiekabeltechnologie. Wir verstehen uns als Ihr technischer Lotse."
},
"intro": {
"p1": "Unsere Wurzeln liegen in der tiefen praktischen Erfahrung unserer technischen Berater und unserer Netzwerke im globalem Kabelmarkt. Wir vereinen Tradition mit modernster Innovation, um zuverlässige Energielösungen für Projekte bis 110 kV zu realisieren.",

13
sentry.client.config.ts Normal file
View File

@@ -0,0 +1,13 @@
import * as Sentry from "@sentry/nextjs";
import { config } from "./lib/config";
if (config.errors.glitchtip.enabled) {
Sentry.init({
dsn: config.errors.glitchtip.dsn,
tracesSampleRate: 1.0,
debug: config.isDevelopment,
environment: config.target || "production",
// Use the proxy path defined in config
tunnel: config.errors.glitchtip.proxyPath,
});
}

16
sentry.edge.config.ts Normal file
View File

@@ -0,0 +1,16 @@
import * as Sentry from "@sentry/nextjs";
import { config } from "./lib/config";
if (config.errors.glitchtip.enabled) {
console.log("Initializing Sentry in Edge runtime...", {
environment: config.target || "production",
});
Sentry.init({
dsn: config.errors.glitchtip.dsn,
tracesSampleRate: 1.0,
debug: true, // Force debug for now to see why it's failing
environment: config.target || "production",
});
} else {
console.warn("Sentry is DISABLED in Edge runtime (missing DSN)");
}

11
sentry.server.config.ts Normal file
View File

@@ -0,0 +1,11 @@
import * as Sentry from "@sentry/nextjs";
import { config } from "./lib/config";
if (config.errors.glitchtip.enabled) {
Sentry.init({
dsn: config.errors.glitchtip.dsn,
tracesSampleRate: 1.0,
debug: config.isDevelopment,
environment: config.target || "production",
});
}