5 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
12 changed files with 146 additions and 28 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

@@ -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

@@ -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

@@ -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 {

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",
});
}