diff --git a/packages/app/next.config.js b/packages/app/next.config.js index 2b538696c..9b8b329c9 100644 --- a/packages/app/next.config.js +++ b/packages/app/next.config.js @@ -45,10 +45,6 @@ const nextConfig = { source: "/api/public/referents_egalite_professionnelle", destination: "/api/public/referents_egalite_professionnelle/json", }, - { - source: "/_sentry/:path*", - destination: `${process.env.SENTRY_URL}/api/:path*`, - }, ]; }, async headers() { @@ -62,19 +58,6 @@ const nextConfig = { }, ], }, - { - source: "/_sentry/:path*", - headers: [ - { key: "Access-Control-Allow-Credentials", value: "true" }, - { key: "Access-Control-Allow-Origin", value: "*" }, - { key: "Access-Control-Allow-Methods", value: "GET,OPTIONS,PATCH,DELETE,POST,PUT" }, - { - key: "Access-Control-Allow-Headers", - value: - "X-Sentry-Auth,X-CSRF-Token,X-Requested-With,Accept,Accept-Version,Content-Length,Content-MD5,Content-Type,Date,X-Api-Version", - }, - ], - }, ]; }, // Uncomment to debug dsfr script in node_modules with reload / nocache @@ -136,7 +119,7 @@ module.exports = withSentryConfig( transpileClientSDK: true, // Routes browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers and CORS issues - tunnelRoute: "/_sentry", + // tunnelRoute: "/_sentry", // Using custom tunnel endpoint instead // Don't hide source maps from generated client bundles hideSourceMaps: false, diff --git a/packages/app/sentry.client.config.ts b/packages/app/sentry.client.config.ts index b60626fd7..a9b3eac3c 100644 --- a/packages/app/sentry.client.config.ts +++ b/packages/app/sentry.client.config.ts @@ -11,6 +11,7 @@ const IS_PRODUCTION = ENVIRONMENT === "production"; Sentry.init({ dsn: process.env.NEXT_PUBLIC_SENTRY_DSN, environment: ENVIRONMENT, + tunnel: "/api/monitoring/tunnel", // Optimal sample rates based on environment tracesSampleRate: IS_PRODUCTION ? 0.1 : 1.0, diff --git a/packages/app/src/app/api/monitoring/tunnel/route.ts b/packages/app/src/app/api/monitoring/tunnel/route.ts new file mode 100644 index 000000000..27e615469 --- /dev/null +++ b/packages/app/src/app/api/monitoring/tunnel/route.ts @@ -0,0 +1,47 @@ +import { type NextRequest } from "next/server"; + +export const dynamic = "force-dynamic"; + +export async function POST(request: NextRequest) { + const sentryUrl = process.env.SENTRY_URL; + if (!sentryUrl) { + return new Response("Sentry URL not configured", { status: 500 }); + } + + try { + // Forward the request to Sentry + const sentryResponse = await fetch(`${sentryUrl}/api/34/envelope/`, { + method: "POST", + headers: { + ...Object.fromEntries(request.headers), + "Content-Type": "application/x-sentry-envelope", + }, + body: await request.text(), + }); + + // Return the response from Sentry + return new Response(await sentryResponse.text(), { + status: sentryResponse.status, + headers: { + "Content-Type": "application/x-sentry-envelope", + "Access-Control-Allow-Origin": "*", + }, + }); + } catch (error) { + console.error("Error forwarding to Sentry:", error); + return new Response("Error forwarding to Sentry", { status: 500 }); + } +} + +// Handle OPTIONS requests for CORS +export async function OPTIONS() { + return new Response(null, { + status: 200, + headers: { + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Methods": "POST, OPTIONS", + "Access-Control-Allow-Headers": "Content-Type, X-Sentry-Auth", + "Access-Control-Max-Age": "86400", + }, + }); +}