Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e9ea253021 | |||
| 237bd46593 | |||
| 40ebdb31d9 | |||
| 8f39ec3d35 | |||
| 7734440b90 | |||
| 42295c3c41 | |||
| 1e00690dd8 | |||
| 90e9f37849 | |||
| 9eaaa798a3 |
@@ -200,6 +200,7 @@ jobs:
|
||||
--build-arg NEXT_PUBLIC_TARGET=${{ needs.prepare.outputs.target }} \
|
||||
--build-arg DIRECTUS_URL=${{ needs.prepare.outputs.directus_url }} \
|
||||
--build-arg UMAMI_API_ENDPOINT=${{ secrets.UMAMI_API_ENDPOINT || secrets.NEXT_PUBLIC_UMAMI_SCRIPT_URL || vars.UMAMI_API_ENDPOINT || 'https://analytics.infra.mintel.me' }} \
|
||||
--build-arg NEXT_PUBLIC_UMAMI_WEBSITE_ID=${{ secrets.UMAMI_WEBSITE_ID || secrets.NEXT_PUBLIC_UMAMI_WEBSITE_ID || vars.UMAMI_WEBSITE_ID || vars.NEXT_PUBLIC_UMAMI_WEBSITE_ID }} \
|
||||
-t registry.infra.mintel.me/mintel/mb-grid-solutions:${{ needs.prepare.outputs.image_tag }} \
|
||||
--push .
|
||||
|
||||
@@ -269,6 +270,7 @@ jobs:
|
||||
GOTIFY_URL=${{ secrets.GOTIFY_URL || vars.GOTIFY_URL }}
|
||||
GOTIFY_TOKEN=${{ secrets.GOTIFY_TOKEN || vars.GOTIFY_TOKEN }}
|
||||
UMAMI_WEBSITE_ID=${{ secrets.UMAMI_WEBSITE_ID || secrets.NEXT_PUBLIC_UMAMI_WEBSITE_ID || vars.UMAMI_WEBSITE_ID || vars.NEXT_PUBLIC_UMAMI_WEBSITE_ID }}
|
||||
NEXT_PUBLIC_UMAMI_WEBSITE_ID=${{ secrets.UMAMI_WEBSITE_ID || secrets.NEXT_PUBLIC_UMAMI_WEBSITE_ID || vars.UMAMI_WEBSITE_ID || vars.NEXT_PUBLIC_UMAMI_WEBSITE_ID }}
|
||||
UMAMI_API_ENDPOINT=${{ secrets.UMAMI_API_ENDPOINT || secrets.NEXT_PUBLIC_UMAMI_SCRIPT_URL || vars.UMAMI_API_ENDPOINT || 'https://analytics.infra.mintel.me' }}
|
||||
|
||||
# Project
|
||||
|
||||
@@ -11,12 +11,14 @@ ARG NEXT_PUBLIC_BASE_URL
|
||||
ARG UMAMI_API_ENDPOINT
|
||||
ARG NEXT_PUBLIC_TARGET
|
||||
ARG DIRECTUS_URL
|
||||
ARG NEXT_PUBLIC_UMAMI_WEBSITE_ID
|
||||
ARG NPM_TOKEN
|
||||
|
||||
ENV NEXT_PUBLIC_BASE_URL=$NEXT_PUBLIC_BASE_URL
|
||||
ENV UMAMI_API_ENDPOINT=$UMAMI_API_ENDPOINT
|
||||
ENV NEXT_PUBLIC_TARGET=$NEXT_PUBLIC_TARGET
|
||||
ENV DIRECTUS_URL=$DIRECTUS_URL
|
||||
ENV NEXT_PUBLIC_UMAMI_WEBSITE_ID=$NEXT_PUBLIC_UMAMI_WEBSITE_ID
|
||||
ENV NPM_TOKEN=$NPM_TOKEN
|
||||
ENV SENTRY_SUPPRESS_TURBOPACK_WARNING=1
|
||||
ENV SKIP_RUNTIME_ENV_VALIDATION=true
|
||||
@@ -49,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
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ import { NextIntlClientProvider } from "next-intl";
|
||||
import { getMessages } from "next-intl/server";
|
||||
import { notFound } from "next/navigation";
|
||||
import { LazyMotion, domAnimation } from "framer-motion";
|
||||
import AnalyticsProvider from "@/components/analytics/AnalyticsProvider";
|
||||
import { config } from "@/lib/config";
|
||||
|
||||
const inter = Inter({
|
||||
subsets: ["latin"],
|
||||
@@ -108,10 +110,26 @@ export default async function RootLayout({
|
||||
|
||||
// Track pageview on the server
|
||||
// This is safe to call here because layout is a Server Component
|
||||
const services = (
|
||||
const serverServices = (
|
||||
await import("@/lib/services/create-services.server")
|
||||
).getServerAppServices();
|
||||
services.analytics.trackPageview();
|
||||
|
||||
// Populate analytics context with headers for high-fidelity server-side tracking
|
||||
const { headers } = await import("next/headers");
|
||||
const requestHeaders = await headers();
|
||||
|
||||
if (serverServices.analytics.setServerContext) {
|
||||
serverServices.analytics.setServerContext({
|
||||
userAgent: requestHeaders.get("user-agent") || undefined,
|
||||
language:
|
||||
requestHeaders.get("accept-language")?.split(",")[0] || undefined,
|
||||
referrer: requestHeaders.get("referer") || undefined,
|
||||
ip: requestHeaders.get("x-forwarded-for")?.split(",")[0] || undefined,
|
||||
});
|
||||
}
|
||||
|
||||
// Track server-side (initial load)
|
||||
serverServices.analytics.trackPageview("/");
|
||||
|
||||
return (
|
||||
<html lang={locale} className={`${inter.variable}`}>
|
||||
@@ -123,6 +141,7 @@ export default async function RootLayout({
|
||||
</head>
|
||||
<body className="antialiased">
|
||||
<NextIntlClientProvider messages={messages}>
|
||||
<AnalyticsProvider websiteId={config.analytics.umami.websiteId} />
|
||||
<LazyMotion features={domAnimation}>
|
||||
<Layout>{children}</Layout>
|
||||
</LazyMotion>
|
||||
|
||||
@@ -86,7 +86,7 @@ export default async function Image() {
|
||||
letterSpacing: "0.1em",
|
||||
}}
|
||||
>
|
||||
Engineering Excellence
|
||||
Technische Beratung
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ export default async function Image() {
|
||||
letterSpacing: "0.1em",
|
||||
}}
|
||||
>
|
||||
Engineering Excellence
|
||||
Technische Beratung
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -8,9 +8,23 @@ export async function POST(req: Request) {
|
||||
const services = getServerAppServices();
|
||||
const logger = services.logger.child({ action: "contact_submission" });
|
||||
|
||||
// Set analytics context from request headers for high-fidelity server-side tracking
|
||||
// This fulfills the "server-side via nextjs proxy" requirement
|
||||
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,
|
||||
ip: req.headers.get("x-forwarded-for")?.split(",")[0] || undefined,
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const { name, email, company, message, website } = await req.json();
|
||||
|
||||
// Track attempt
|
||||
services.analytics.track("contact-form-attempt");
|
||||
|
||||
// Honeypot check
|
||||
if (website) {
|
||||
logger.info("Spam detected (honeypot)");
|
||||
@@ -118,6 +132,11 @@ ${message}
|
||||
});
|
||||
}
|
||||
|
||||
// Track success
|
||||
services.analytics.track("contact-form-success", {
|
||||
has_company: Boolean(company),
|
||||
});
|
||||
|
||||
return NextResponse.json({ message: "Ok" });
|
||||
} catch (error) {
|
||||
logger.error("Global API Error", { error });
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -27,9 +27,11 @@ function createConfig() {
|
||||
|
||||
analytics: {
|
||||
umami: {
|
||||
websiteId: env.UMAMI_WEBSITE_ID,
|
||||
websiteId: env.NEXT_PUBLIC_UMAMI_WEBSITE_ID || env.UMAMI_WEBSITE_ID,
|
||||
apiEndpoint: env.UMAMI_API_ENDPOINT,
|
||||
enabled: Boolean(env.UMAMI_WEBSITE_ID),
|
||||
enabled: Boolean(
|
||||
env.NEXT_PUBLIC_UMAMI_WEBSITE_ID || env.UMAMI_WEBSITE_ID,
|
||||
),
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
@@ -26,6 +26,10 @@ export const envSchema = z
|
||||
preprocessEmptyString,
|
||||
z.string().optional(),
|
||||
),
|
||||
NEXT_PUBLIC_UMAMI_WEBSITE_ID: z.preprocess(
|
||||
preprocessEmptyString,
|
||||
z.string().optional(),
|
||||
),
|
||||
UMAMI_API_ENDPOINT: z.preprocess(
|
||||
preprocessEmptyString,
|
||||
z.string().url().default("https://analytics.infra.mintel.me"),
|
||||
@@ -115,6 +119,7 @@ export function getRawEnv() {
|
||||
NEXT_PUBLIC_TARGET: process.env.NEXT_PUBLIC_TARGET,
|
||||
UMAMI_WEBSITE_ID:
|
||||
process.env.UMAMI_WEBSITE_ID || process.env.NEXT_PUBLIC_UMAMI_WEBSITE_ID,
|
||||
NEXT_PUBLIC_UMAMI_WEBSITE_ID: process.env.NEXT_PUBLIC_UMAMI_WEBSITE_ID,
|
||||
UMAMI_API_ENDPOINT:
|
||||
process.env.UMAMI_API_ENDPOINT ||
|
||||
process.env.UMAMI_SCRIPT_URL ||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,12 @@ export type UmamiAnalyticsServiceOptions = {
|
||||
export class UmamiAnalyticsService implements AnalyticsService {
|
||||
private websiteId?: string;
|
||||
private endpoint: string;
|
||||
private serverContext?: {
|
||||
userAgent?: string;
|
||||
language?: string;
|
||||
referrer?: string;
|
||||
ip?: string;
|
||||
};
|
||||
|
||||
constructor(private readonly options: UmamiAnalyticsServiceOptions) {
|
||||
this.websiteId = config.analytics.umami.websiteId;
|
||||
@@ -36,6 +42,19 @@ export class UmamiAnalyticsService implements AnalyticsService {
|
||||
: "/stats";
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the server-side context for the current request.
|
||||
* This allows the service to use real request headers for tracking.
|
||||
*/
|
||||
setServerContext(context: {
|
||||
userAgent?: string;
|
||||
language?: string;
|
||||
referrer?: string;
|
||||
ip?: string;
|
||||
}) {
|
||||
this.serverContext = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method to send the payload to Umami API.
|
||||
*/
|
||||
@@ -47,24 +66,47 @@ 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}`
|
||||
: undefined,
|
||||
language:
|
||||
typeof window !== "undefined" ? navigator.language : undefined,
|
||||
referrer: typeof window !== "undefined" ? document.referrer : undefined,
|
||||
typeof window !== "undefined"
|
||||
? navigator.language
|
||||
: this.serverContext?.language,
|
||||
referrer:
|
||||
typeof window !== "undefined"
|
||||
? document.referrer
|
||||
: this.serverContext?.referrer,
|
||||
...data,
|
||||
};
|
||||
|
||||
const headers: Record<string, string> = {
|
||||
"Content-Type": "application/json",
|
||||
};
|
||||
|
||||
// Set User-Agent
|
||||
if (typeof window !== "undefined") {
|
||||
headers["User-Agent"] = navigator.userAgent;
|
||||
} else if (this.serverContext?.userAgent) {
|
||||
headers["User-Agent"] = this.serverContext.userAgent;
|
||||
} else {
|
||||
headers["User-Agent"] = "Mintel-Server-Proxy";
|
||||
}
|
||||
|
||||
// Forward client IP if available (Umami must be configured to trust this)
|
||||
if (this.serverContext?.ip) {
|
||||
headers["X-Forwarded-For"] = this.serverContext.ip;
|
||||
}
|
||||
|
||||
const response = await fetch(`${this.endpoint}/api/send`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"User-Agent":
|
||||
typeof window === "undefined" ? "KLZ-Server" : navigator.userAgent,
|
||||
},
|
||||
headers,
|
||||
body: JSON.stringify({ type, payload }),
|
||||
keepalive: true,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@@ -93,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
|
||||
: "/",
|
||||
});
|
||||
}
|
||||
|
||||
@@ -106,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
|
||||
: "/"),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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.",
|
||||
|
||||
@@ -14,5 +14,9 @@ export default createMiddleware({
|
||||
export const config = {
|
||||
// Matcher for all pages and internationalized pathnames
|
||||
// excluding api, _next, static files, etc.
|
||||
matcher: ["/((?!api|_next|_vercel|.*\\..*).*)", "/", "/(de)/:path*"],
|
||||
matcher: [
|
||||
"/((?!api|stats|errors|_next|_vercel|.*\\..*).*)",
|
||||
"/",
|
||||
"/(de)/:path*",
|
||||
],
|
||||
};
|
||||
|
||||
@@ -17,10 +17,18 @@ const nextConfig = {
|
||||
source: "/stats/:path*",
|
||||
destination: `${umamiUrl}/:path*`,
|
||||
},
|
||||
{
|
||||
source: "/:locale(de)/stats/:path*",
|
||||
destination: `${umamiUrl}/:path*`,
|
||||
},
|
||||
{
|
||||
source: "/errors/:path*",
|
||||
destination: `${glitchtipUrl}/:path*`,
|
||||
},
|
||||
{
|
||||
source: "/:locale(de)/errors/:path*",
|
||||
destination: `${glitchtipUrl}/:path*`,
|
||||
},
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
13
sentry.client.config.ts
Normal file
13
sentry.client.config.ts
Normal 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,
|
||||
});
|
||||
}
|
||||
11
sentry.edge.config.ts
Normal file
11
sentry.edge.config.ts
Normal 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",
|
||||
});
|
||||
}
|
||||
11
sentry.server.config.ts
Normal file
11
sentry.server.config.ts
Normal 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",
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user