-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(sentry): migration sur la nouvelle version de Sentry
- Loading branch information
Showing
21 changed files
with
878 additions
and
55 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
262 changes: 262 additions & 0 deletions
262
packages/code-du-travail-frontend/app/api/monitoring/envelope/route.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
}); | ||
} |
Oops, something went wrong.