Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(sentry): migration sur la nouvelle version de Sentry #6454

Draft
wants to merge 1 commit into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/review-auto.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ jobs:
run: |
echo "SITE_URL=https://${{ steps.env.outputs.subdomain }}.ovh.fabrique.social.gouv.fr" >> $GITHUB_ENV
- name: Run test e2e
env:
CYPRESS: "true"
run: |
TEST_BASEURL=${{ env.SITE_URL }} TEST_MODE=light yarn test:e2e
- name: Archive generated screenshots
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/review.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ jobs:
run: |
echo "SITE_URL=https://${{ steps.env.outputs.subdomain }}.ovh.fabrique.social.gouv.fr" >> $GITHUB_ENV
- name: Run test e2e
env:
CYPRESS: "true"
run: |
TEST_BASEURL=${{ env.SITE_URL }} TEST_MODE=light yarn test:e2e
- name: Archive generated screenshots
Expand Down
16 changes: 16 additions & 0 deletions .kontinuous/env/dev/templates/sentry.sealed-secret.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
annotations:
sealedsecrets.bitnami.com/cluster-wide: 'true'
name: sentry
spec:
encryptedData:
SENTRY_DSN: AgDCMaihNlpRZTWAxW87fBaiGPbNLugKZnWAeLI8Tux2IKwn1xpIkeL/5tqHX3hwUJf1UFbanz2u1NzBAW1l/hQ0CANRGDQcqX06q9/Rbwr+s9aa8yflYJIQM+E0XHVYaauCyJBvfb00Ep8cSdPwUjA8aUug0asfNcz1iPC25bEB8HX+9Nz8L4MPeArsGiirjVNSWqojk+odLKydSatIpO81STTCF8/pCX0+Rs/6Zg2eFZU17sS1E6eyjOAec7WFF1S6l8D0o+B6WOroopSflnmY9+QWHf2UBVRb5y3PLbpR8K8jrn9ZGjjXKT77RHu6/pVR2AklGSk/ri0XAXT+TvFzFFKoy5sloZrDPdvA1JQnVgxZV5syaIQcu+BOKIN9xvUWop/hsFwpmbxFgfyDxgFV5UmGgF014RdhEGpWc6FuQc3kRXbTQerxF/nLH1OTFqnhA+jsHYNIsfv8Q24DIFE17yM7RI7W9CkFy//hqHES1AayRwx2njZxdUSwNyyzQcQIhvGQP5KcLXU9GIOr3UxCdJBswzYqTTt1VGdBMkM0I+xZgdXIWaL4WHrfe+eQhr6pwJtIJZNtpijvFuz2Ot8Ewm1PILCl0kbq9IuJb5FkDqMuzskMa/AYesfFEMUdLo7ybIHaag2i/DYdzw2U1sqT/M8tc7yZW/ztcG5rHQ3vK1twhmMznl05bpuzSozTz2X68qYwC6otGlNSlPxntOwzpL+irlJYE3JXqz8nds064EOJf7w7lffzMKpPWiyNEL3j39/ma7k6E4EwWFUwbqbSvJju6ajB8YD3e6U=
SENTRY_AUTH_TOKEN: AgAaGu/aJjM6cEx4sEKuLC3bjJrE4AnaTQKTmcG57OcJUonD6zo2ytoNx8dKwEhp6SNO8mq1ZHXJuzEDc/0vqVinTYfg3ZYmHpLMmK6ofclVcgqT/za45gDJCgt/3x3hUGrNiUz1c1hYjNje2FQ4gIvUYUYGaMSEsWGPi2cZrIsCoeaYP2l2IpVoohmAj/lfCboJD2A3d0hFJDbArqTYoBECuPf2QpidNWi14vSwZTtugGbz9wplR+Vc/P0pzkz/M/p9dQB/XwMXiPyrUHNC2bWmBfs4MctoMiG/mRRAAnUyssHLJt615IAZuRpXN0ZGi2yw+vQDG5ggq2VKHbWzPnXR2ggZI19iFFLGvdjvYdu3uQiPin3PWzqo6pbAkFCjg8h7gTaaMBtXd5zFa4xrSQ+UfQfJ7wKMABZ0Ry+XpqkW2ZkRuQdOWQaOFKy13j/+3PLg1hQBd7iEyoYIUYjR1PyIQG1fp7jG9FRvglvJimzlW8PsjSw19p6wvsRscK+u3jlHdQJ0+pZTCe7AyZvM91T9LPwW5mj9s+RM5jtJ+/b6WKrDWt9cAk4YR0Vjdja+I3yueLs4CM8a68kcLY1vsjfATWUMh1keMaMngn39UMhPOMdz76gcFGIwcn63v7wi//jIe1ZhY97566JezPZ9AFx4Iw44/T5fF/gA6SGLw0+UGuQbaPpLf1w6Dmz9KaOnQzsZSgWyTNBEX7+9LnuRL1UL4RRCah4MlFdRigvP7H1nay56QKSaZREPCisXp/h3t0XPlbjdNr529Sqwj674LC039JWYRhy+6r11rjuPz7V45FnNSoACozkNlGoFkXkP4fesBsyLiUR89n0Qs2k5ku/JTrosLLl/aRDDPXvuP0f6xXuMiFSt66MSXR/US2JGaokuOC0NL/rdPWcULGWMK8JlAtjZyimqO/KAlneLnIBa3EV1kbpSHbOdvaKf4M0RQBu/AUPT4NiDLrg9ddGkh2e2lF4wRNACysuNjb00R4YuKOe8c+VCJX2HUEvgXxsnrWL76UZi9wiQ
template:
metadata:
annotations:
sealedsecrets.bitnami.com/cluster-wide: 'true'
name: sentry
type: Opaque
1 change: 0 additions & 1 deletion .kontinuous/env/dev/templates/www.sealed-secret.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ spec:
encryptedData:
ELASTICSEARCH_URL: AgAUJaPLaiM5gyJSFwbPwfZauAtxMMmW85PhhA85jAmz1z2k3MS/tgc0hzdwLJ6psJcivAONP11BcJ3uXuz7MvOsdXKhkKbU03nb4lB/jDhYh3dAj2Keuz0TMKgZSZUxj8rETucDRgusJ6I35tVFshXG3tbqlP/K290CrmlqA2hWVAHoR9HtdBdbRjW3vBxiOjjSvcUo7bWCU/9/TmMhWskkgVLG6bQOSUWZBS+9ab2yzg/2aqNN7ZwzZc5yzurLjdkk/vJhPTSMbmdCy3DIxTaDMt4fcJ6ONxQZtOW5MZqlhdMEmYbW+/OuHQlDt8kYR5hIn35WIRWOXa5109GyyI9UXFOHtRn4AxrxuWlyzzwiuQsWk147ozqU1bV1j80HFDJtFmopP2jDCRK9ijmLsBThBBqyPzFrfPZWQsLEj+xAb+jc2fuwKtfeNYnzoY9p0/IKwrUFkBjzjrggCSkm87bVPZGFUBK0tnL1l4OYB3qJhyWEVlL1nPFDCxIXOO3nIwC9XH6wUKe5VIIR/XZDWPnYFI+kktHFgPgGCqsHEujwp8w+aVpQy+L3r5kSuWonUguUIUDuvGYc/I5nXzpy25yKpSXjq/1d1vvx66pD8AngyDSGowGVX3nfvf2dAq1K/UQQUvWq2Aaf5Bh03tA0RVPczgtAQZ7iwueoMVWzBddGAywcIJOW1lJQJ6kQZ6flt1dnDosLSU7Ra6KdRAElopLjqNzEUFJpGzv+Di72mcrOWDkUxqz2qFssfwDuPaQ/HLS0+mgt8qHFJLgQxCOCuhyz2GWv
ELASTICSEARCH_TOKEN_API: AgC4tRnPP5Rq9G12mvlWjt/m8w4Z6AAZPQwbLxK2sYHEfF7KuH+QVeknaX6x861lGHSZq4r1bJJJUBInCG1gwV8j8ndt6g4GfAul+fouRoYVCoyCyRZ063qXm+OsTvaihlbi8hAPoTt+iDWkX6PRkMMrWrBTgxGHpoTizeqLYyu2EXz5nSmF1UfzIgXfcArD7k8ESbQ9rJEPdEWSoH9xbami3FnPltID21g0JViXjHTm3nuIO7NHH6/gkQdXYL7mxGh+wy6bDo0+98WV5eFzUc22D9rqsd/q2RaW5s/fUCnWnU0WDknSJR4JWAOh3RFWIJ+NiWMpa4oiYs/sTp560GdJC4aZoWI7oGEzjVdHRjC8IbQIiPCLj+3KKFVFsgGLub6/ILRVtZwynYLv1V+D9GvjNMQJAsLVk/98+6GneZKHAEObEbuCw9mij+cgJzYog/aKMHF4JAnM+LAZVYJVWyn2Te0fqq6Z4NjjP6XIOGNMNQFro9+d335u1qAb50L/Rpkvzi1Qon0EBVGgtj9/VZOLk7MsJstKtB3nMI95ObsVfRCh5BxNEbC0FHMmRdUOcx204F2Lteb51LaVqL/ARFZPYRPw9Z1Cgi4focKH3J+oEAKp1N8jvymtobhIqoM+xLfmSmVvBbq5F3VfwdXEXm3WYC5gAZW7Fgkx3qForfw/lBShk/aXhs3FzAAWnXgZao8bHEsbGxGpRtwBwG5Y/WnzlVy/Z1VKI6klxK7Vn63AgrwQjsQ8izdK6EtzhJhjG/keor2Ye5Hp3MOA16A=
SENTRY_AUTH_TOKEN: AgCKID/es7BZx/js5TA1A0OXpHRvTMzxsKYdfrwXEtM+sUj9NAjPWnNMw+x/ZrxJ+927XkzvOc9bCnMMEHylXUwuz3zf1u3XYFBaGd3vG0VCDeHshH5vokLEhEy3d44qdJmIMWqXYFGWjuWj/3yW1x3+4DyBQGO1zxYleiPppYrnheF3yrdwFUM/v4PnHsBPxqL8Tqr+tdi4J3o3POanNglbF4/AsgNJbpGOFxkhZXCewWhEr9lF+sX7bxbo7Pl5tc/RwdTn9xtQiEvO8Sqxkl/cPwXcEvylTWc2eaGg76deD4Le1LEyFpNDQ5AzTmt+LnC9tGmBvpkYVZz5uJev+ybhAcj0OcOOZq1Cg0p5hZl8zD5JmHES78mMxpGvc9799wIfNx26ASNtHj+dFBRrHrgfqIrGoI9MDaSSEB9w4PBLTZsuLdaFZJgiWtzVPud1cnYSaYC4tsfG3qhHbKOUJXOA+BvJR2TZEdzHZCWMGUVg0slUjGCCbkvD7HDEswXYATDj3RzhkNbwhvbWp2HSfS8awlzTbOOhP3SQGr3EiuD0OVgBW/fRBAY77cq6NnNqpiK4STutN9uC801Zxd9uPsWiY5nv0XtMzS6No4ZutyKR6gI10ZSQqvRrdcYbAPoYbOBIUR2zXyzd1bx+wCnojRofTf/gHptKq9FopKDlTmxj040rTW42CIQzXgf5kVRUxKZrDB0LICLwRNU+YYdK8kuBIvisOQ9dx2P6Ez4nAfWdnO93z87tt7306a9kIyuw/diN75t6ai6uJhF0ac+FiIEDJTNexVg7CQg2kPzgpH4M5H7RgsxnKxPm1qnbDMJ7KG3Vzu4BG4vc/fa6fgihpN+t0b5PqHbuWaskhqUSEPsKdx8Q2cRgL0+27JCpdWRDQDX8wF3MTgdWWxE3TYZmjGq42u9TNfHBBmr/PCDfhPI3PFV+WZkwayV3K3LIjQf9H8GcpZD7w5OyL44oAYcL6Iye6Y1420A6mq5b1a9rayO6wWlLQQwtX78haGVcMv7RXo+bK/CXu+IR
template:
metadata:
annotations:
Expand Down
16 changes: 10 additions & 6 deletions .kontinuous/env/dev/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@ jobs:
runs:
build-app:
use: build
env:
- name: NEXT_PUBLIC_SENTRY_DSN
valueFrom:
secretKeyRef:
name: sentry
key: SENTRY_DSN
with:
buildArgs:
NEXT_PUBLIC_SENTRY_DSN: https://[email protected]/107
NEXT_PUBLIC_SENTRY_PUBLIC_KEY: 81b6e3d265cf736588f894040d265705
NEXT_PUBLIC_SENTRY_PROJECT_ID: 107
NEXT_PUBLIC_SENTRY_BASE_URL: https://sentry.fabrique.social.gouv.fr
NEXT_PUBLIC_SENTRY_ENV: dev
NEXT_PUBLIC_SENTRY_DSN: "$NEXT_PUBLIC_SENTRY_DSN"
NEXT_PUBLIC_SENTRY_BASE_URL: https://sentry2.fabrique.social.gouv.fr
NEXT_PUBLIC_CDTN_ENV: development
NEXT_PUBLIC_SENTRY_RELEASE: "{{.Values.global.branchSlug32}}"
NEXT_PUBLIC_BUCKET_DEFAULT_FOLDER: "default"
NEXT_PUBLIC_BUCKET_SITEMAP_FOLDER: "sitemap"
Expand All @@ -24,7 +28,7 @@ jobs:
NEXT_PUBLIC_BRANCH_NAME_SLUG: "{{.Values.global.branchSlug32}}"
secrets:
sentry_auth_token:
secretName: www-secret
secretName: sentry-secret
secretKey: SENTRY_AUTH_TOKEN
elasticsearch_token_api:
secretName: www-secret
Expand Down
11 changes: 11 additions & 0 deletions .kontinuous/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,17 @@ app:
name: www-configmap
- secretRef:
name: www-secret
env:
- name: SENTRY_AUTH_TOKEN
valueFrom:
secretKeyRef:
name: sentry
key: SENTRY_AUTH_TOKEN
- name: NEXT_PUBLIC_SENTRY_DSN
valueFrom:
secretKeyRef:
name: sentry
key: SENTRY_DSN
resources:
limits:
cpu: 400m
Expand Down
10 changes: 6 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,6 @@ ARG NEXT_PUBLIC_SENTRY_DSN
ENV NEXT_PUBLIC_SENTRY_DSN=$NEXT_PUBLIC_SENTRY_DSN
ARG NEXT_PUBLIC_SENTRY_ENV
ENV NEXT_PUBLIC_SENTRY_ENV=$NEXT_PUBLIC_SENTRY_ENV
ARG NEXT_PUBLIC_SENTRY_PUBLIC_KEY
ENV NEXT_PUBLIC_SENTRY_PUBLIC_KEY=$NEXT_PUBLIC_SENTRY_PUBLIC_KEY
ARG NEXT_PUBLIC_SENTRY_PROJECT_ID
ENV NEXT_PUBLIC_SENTRY_PROJECT_ID=$NEXT_PUBLIC_SENTRY_PROJECT_ID
ARG NEXT_PUBLIC_SENTRY_BASE_URL
ENV NEXT_PUBLIC_SENTRY_BASE_URL=$NEXT_PUBLIC_SENTRY_BASE_URL
ARG NEXT_PUBLIC_COMMIT
Expand All @@ -56,13 +52,18 @@ ENV NEXT_PUBLIC_ES_INDEX_PREFIX=$NEXT_PUBLIC_ES_INDEX_PREFIX
ARG NEXT_PUBLIC_BRANCH_NAME_SLUG
ENV NEXT_PUBLIC_BRANCH_NAME_SLUG=$NEXT_PUBLIC_BRANCH_NAME_SLUG

# Enable source map generation during build
ENV GENERATE_SOURCEMAP=true \
NODE_ENV=production

# hadolint ignore=SC2046
RUN --mount=type=secret,id=sentry_auth_token \
--mount=type=secret,id=elasticsearch_token_api \
--mount=type=secret,id=elasticsearch_url \
export SENTRY_AUTH_TOKEN=$(cat /run/secrets/sentry_auth_token) && \
export ELASTICSEARCH_TOKEN_API=$(cat /run/secrets/elasticsearch_token_api) && \
export ELASTICSEARCH_URL=$(cat /run/secrets/elasticsearch_url) && \
export GENERATE_SOURCEMAP=true && \
yarn build && \
yarn workspaces focus --production --all && \
yarn cache clean
Expand All @@ -86,6 +87,7 @@ COPY --from=dist --chown=1000:1000 /dep/packages/code-du-travail-frontend/next.c
COPY --from=dist --chown=1000:1000 /dep/packages/code-du-travail-frontend/instrumentation.ts /app/packages/code-du-travail-frontend/instrumentation.ts
COPY --from=dist --chown=1000:1000 /dep/packages/code-du-travail-frontend/sentry.client.config.ts /app/packages/code-du-travail-frontend/sentry.client.config.ts
COPY --from=dist --chown=1000:1000 /dep/packages/code-du-travail-frontend/sentry.server.config.ts /app/packages/code-du-travail-frontend/sentry.server.config.ts
COPY --from=dist --chown=1000:1000 /dep/packages/code-du-travail-frontend/sentry.edge.config.ts /app/packages/code-du-travail-frontend/sentry.edge.config.ts
COPY --from=dist --chown=1000:1000 /dep/packages/code-du-travail-frontend/redirects.json /app/packages/code-du-travail-frontend/redirects.json
COPY --from=dist --chown=1000:1000 /dep/packages/code-du-travail-frontend/scripts /app/packages/code-du-travail-frontend/scripts
COPY --from=dist --chown=1000:1000 /dep/package.json /app/package.json
Expand Down
262 changes: 262 additions & 0 deletions packages/code-du-travail-frontend/app/api/monitoring/envelope/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
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) {
console.error("Sentry URL not configured");
return new Response("Sentry URL not configured", { status: 500 });
}

try {
// Get the raw body
const body = await request.text();
// console.log("Received envelope body:", body);
// console.log("Envelope body length:", body.length);
// console.log("Envelope newlines count:", (body.match(/\n/g) || []).length);
// Parse envelope format (newline-delimited JSON)
let projectId: string | undefined;
let publicKey: string | undefined;

// Split into lines
const lines = body.split("\n");
if (lines.length < 2) {
console.error("Invalid envelope format: missing parts");
return new Response("Invalid envelope format", { status: 400 });
}

const headerRaw = lines[0];
const items: string[] = [];

// Process lines after header to build items with proper newlines
let i = 1;
while (i < lines.length) {
const itemHeaderLine = lines[i];
if (!itemHeaderLine || itemHeaderLine.trim() === "") {
i++;
continue;
}

try {
// Try to parse item header
const itemHeader = JSON.parse(itemHeaderLine);
if (!itemHeader.type) {
i++;
continue;
}

// Found an item header, collect payload until next item header or end
const itemPayloadLines: string[] = [];
i++;
while (i < lines.length) {
const payloadLine = lines[i];
// Stop if we hit an empty line or another item header
if (payloadLine.trim() === "") {
i++;
continue;
}
try {
const parsed = JSON.parse(payloadLine);
if (parsed.type) {
break;
}
} catch (e) {
// Not a header, add to payload
}
itemPayloadLines.push(payloadLine);
i++;
}

// Add item with its payload, ensuring proper newlines
if (itemPayloadLines.length > 0) {
// For items with payload: header + \n + payload
items.push(itemHeaderLine + "\n" + itemPayloadLines.join("\n"));
} else {
// For header-only items: just the header
items.push(itemHeaderLine);
}

// Log item structure for debugging
// console.log("Added item:", {
// type: itemHeader.type,
// hasPayload: itemPayloadLines.length > 0,
// payloadLines: itemPayloadLines.length,
// });
} catch (e) {
// Not a valid item header, skip
i++;
}
}

// Log parsed items with detailed structure
// items.forEach((item, index) => {
// console.log(`Item ${index} structure:`, {
// content: item,
// length: item.length,
// newlines: (item.match(/\n/g) || []).length,
// endsWithNewline: item.endsWith("\n"),
// });
// });

// Reconstruct envelope with proper format:
// header + \n\n + item1 + \n\n + item2 + \n\n
const envelope = headerRaw + "\n\n" + items.join("\n");

// Log detailed envelope structure
// console.log("Envelope structure:", {
// headerLength: headerRaw.length,
// itemsCount: items.length,
// totalLength: envelope.length,
// newlines: (envelope.match(/\n/g) || []).length,
// firstNewlineAt: envelope.indexOf("\n"),
// headerAndFirstItem: envelope.split("\n").slice(0, 3),
// });

try {
// Parse header
const header = JSON.parse(headerRaw);

// Try to get DSN from envelope header
if (header.dsn) {
const dsnUrl = new URL(header.dsn);
projectId = dsnUrl.pathname.split("/")[1];
publicKey = dsnUrl.username;
// console.log("Parsed DSN from envelope:", { projectId, publicKey });
}
} catch (e) {
console.error("Failed to parse envelope header:", e);
return new Response("Invalid envelope header", { status: 400 });
}

// Fallback to environment DSN if needed
if (!projectId || !publicKey) {
const dsn = process.env.NEXT_PUBLIC_SENTRY_DSN;
if (dsn) {
try {
const dsnUrl = new URL(dsn);
projectId = dsnUrl.pathname.split("/")[1];
publicKey = dsnUrl.username;
// console.log("Parsed DSN from environment:", { projectId, publicKey });
} catch (e) {
console.warn("Could not parse environment DSN:", e);
}
}
}

if (!projectId || !publicKey) {
console.warn("Could not extract project details from DSN");
return new Response("Could not parse Sentry DSN", { status: 500 });
}

// Get origin for CORS headers
const origin = request.headers.get("origin");

// Forward the request to Sentry's envelope endpoint
const sentryResponse = await fetch(
`${sentryUrl}/api/${projectId}/envelope/`,
{
method: "POST",
credentials: "omit", // Don't send cookies for client-side error reporting
headers: {
// Forward original headers needed for client error reporting
"Content-Type": "text/plain;charset=UTF-8",
Accept: "*/*",
// Forward original auth header from client request
"X-Sentry-Auth":
request.headers.get("X-Sentry-Auth") ||
`Sentry sentry_key=${publicKey},sentry_version=7,sentry_client=sentry.javascript.nextjs/8.0.0`,
},
// Use reconstructed envelope with proper newlines
body: envelope,
}
);

if (sentryResponse.status === 403) {
console.error("Sentry authentication failed:", {
responseStatus: sentryResponse.status,
responseStatusText: sentryResponse.statusText,
sentryError: sentryResponse.headers.get("X-Sentry-Error"),
url: `${sentryUrl}/api/${projectId}/envelope/`,
});

// Try to get response body for more error details
try {
const errorBody = await sentryResponse.clone().text();
console.error("Sentry error response body:", errorBody);
} catch (e) {
console.error("Could not read error response body");
}
}

// console.log("Sentry response:", {
// status: sentryResponse.status,
// statusText: sentryResponse.statusText,
// error: sentryResponse.headers.get("X-Sentry-Error"),
// });

// Get Sentry response headers we want to forward
const sentryHeaders = [
"X-Sentry-Error",
"X-Sentry-Rate-Limits",
"Retry-After",
];

// Get the request origin or default to *
const requestOrigin = request.headers.get("origin") || "*";

const responseHeaders: Record<string, string> = {
"Content-Type": "text/plain;charset=UTF-8",
"Access-Control-Allow-Origin": requestOrigin,
"Access-Control-Allow-Credentials": "true",
"Access-Control-Expose-Headers":
"X-Sentry-Error, X-Sentry-Rate-Limits, Retry-After",
// Add Vary header when using dynamic origin
...(requestOrigin !== "*" ? { Vary: "Origin" } : {}),
};

// Forward specific Sentry headers if they exist
for (const header of sentryHeaders) {
const value = sentryResponse.headers.get(header);
if (value) {
responseHeaders[header] = value;
// console.log(`Forwarding header ${header}:`, value);
}
}

// Return the response from Sentry with forwarded headers
return new Response(await sentryResponse.text(), {
status: sentryResponse.status,
headers: responseHeaders,
});
} 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(request: NextRequest) {
// Get the request origin or default to *
const requestOrigin = request.headers.get("origin") || "*";

const headers: Record<string, string> = {
"Access-Control-Allow-Origin": requestOrigin,
"Access-Control-Allow-Methods": "POST, OPTIONS",
"Access-Control-Allow-Credentials": "true",
"Access-Control-Allow-Headers": "Accept, Content-Type, X-Sentry-Auth",
"Access-Control-Expose-Headers":
"X-Sentry-Error, X-Sentry-Rate-Limits, Retry-After",
"Access-Control-Max-Age": "86400",
};

// Add Vary header when using dynamic origin
if (requestOrigin !== "*") {
headers["Vary"] = "Origin";
}

return new Response(null, {
status: 200,
headers,
});
}
Loading
Loading