```
✨ feat(sentry): Ajoute Sentry pour le suivi des erreurs
Ajoute Sentry pour le suivi des erreurs en production et améliore la
gestion des erreurs côté client et serveur.
```
This commit is contained in:
@@ -75,6 +75,7 @@
|
||||
STRIPE_SK=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
|
||||
STRIPE_WEBHOOKS_SIGN=whsec_wNHtgjypqbfP7erAqifCOzZvW8kW9oB7
|
||||
MAILER_DSN=ses+smtp://AKIAWTT2T22CWBRBBDYN:BBdgb6KxRQ8mNcpWFJsZCJxbSGNdgLhKFiITMErfBlQP@default?region=eu-west-3
|
||||
SENTRY_DSN="https://4f43769e7c483f14da26e05824a482d0@o4509563601092608.ingest.de.sentry.io/4510392636473424"
|
||||
dest: "{{ path }}/.env.local"
|
||||
when: ansible_os_family == "Debian"
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import './app.scss'
|
||||
import * as Turbo from "@hotwired/turbo"
|
||||
|
||||
import {PaymentForm} from './PaymentForm'
|
||||
import * as Sentry from "@sentry/browser";
|
||||
|
||||
// --- CLÉS DE STOCKAGE ET VAPID ---
|
||||
const VAPID_PUBLIC_KEY = "BKz0kdcsG6kk9KxciPpkfP8kEDAd408inZecij5kBDbQ1ZGZSNwS4KZ8FerC28LFXvgSqpDXtor3ePo0zBCdNqo";
|
||||
@@ -427,6 +428,24 @@ document.addEventListener('DOMContentLoaded', ()=>{
|
||||
// Gère le bandeau de cookies (bottom-right)
|
||||
handleCookieBanner()
|
||||
|
||||
Sentry.init({
|
||||
dsn: "https://129785624e32fc0d4d7d8002a03b77b7@o4509563601092608.ingest.de.sentry.io/4510392640340048",
|
||||
// Setting this option to true will send default PII data to Sentry.
|
||||
// For example, automatic IP address collection on events
|
||||
sendDefaultPii: true,
|
||||
integrations: [
|
||||
Sentry.browserTracingIntegration(),
|
||||
Sentry.replayIntegration()
|
||||
],
|
||||
tunnel: "/tunnel",
|
||||
// Tracing
|
||||
tracesSampleRate: 1.0, // Capture 100% of the transactions
|
||||
// Set 'tracePropagationTargets' to control for which URLs distributed tracing should be enabled
|
||||
tracePropagationTargets: ["localhost", /^https:\/\/yourserver\.io\/api/],
|
||||
// Session Replay
|
||||
replaysSessionSampleRate: 0.1, // This sets the sample rate at 10%. You may want to change it to 100% while in development and then sample at a lower rate in production.
|
||||
replaysOnErrorSampleRate: 1.0 // If you're not already sampling the entire session, change the sample rate to 100% when sampling sessions where errors occur.
|
||||
});
|
||||
const env = document.querySelector('meta[name="env"]')
|
||||
if(env.getAttribute('content') == "prod") {
|
||||
if (typeof navigator.serviceWorker !== 'undefined') {
|
||||
|
||||
@@ -2,23 +2,7 @@ when@prod:
|
||||
sentry:
|
||||
dsn: '%env(SENTRY_DSN)%'
|
||||
options:
|
||||
# Add request headers, cookies, IP address and the authenticated user
|
||||
# see https://docs.sentry.io/platforms/php/data-management/data-collected/ for more info
|
||||
# send_default_pii: true
|
||||
ignore_exceptions:
|
||||
- 'Symfony\Component\ErrorHandler\Error\FatalError'
|
||||
- 'Symfony\Component\Debug\Exception\FatalErrorException'
|
||||
|
||||
# If you are using Monolog, you also need this additional configuration to log the errors correctly:
|
||||
# https://docs.sentry.io/platforms/php/guides/symfony/#monolog-integration
|
||||
# register_error_listener: false
|
||||
# register_error_handler: false
|
||||
|
||||
# monolog:
|
||||
# handlers:
|
||||
# sentry:
|
||||
# type: sentry
|
||||
# level: !php/const Monolog\Logger::ERROR
|
||||
# hub_id: Sentry\State\HubInterface
|
||||
# fill_extra_context: true # Enables sending monolog context to Sentry
|
||||
# process_psr_3_messages: false # Disables the resolution of PSR-3 placeholders
|
||||
# Specify a fixed sample rate
|
||||
traces_sample_rate: 1.0
|
||||
# Set a sampling rate for profiling - this is relative to traces_sample_rate
|
||||
profiles_sample_rate: 1.0
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
"@grafikart/drop-files-element": "^1.0.9",
|
||||
"@hotwired/turbo": "^8.0.13",
|
||||
"@preact/preset-vite": "^2.10.2",
|
||||
"@sentry/browser": "^10.26.0",
|
||||
"@tailwindcss/vite": "^4.1.13",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"body-scroll-lock": "^4.0.0-beta.0",
|
||||
|
||||
@@ -17,6 +17,7 @@ use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
|
||||
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||
use Twig\Environment;
|
||||
|
||||
class HomeController extends AbstractController
|
||||
@@ -39,4 +40,74 @@ class HomeController extends AbstractController
|
||||
'city' => $city,
|
||||
]);
|
||||
}
|
||||
const SENTRY_HOST = 'o4509563601092608.ingest.de.sentry.io';
|
||||
const SENTRY_PROJECT_IDS = ['4510392640340048'];
|
||||
|
||||
#[Route('/tunnel',name: 'app_tunnel',options: ['sitemap' => false], methods: ['POST'])]
|
||||
public function tunnel(Request $request,HttpClientInterface $httpClient): Response
|
||||
{
|
||||
$envelope = $request->getContent();
|
||||
if (empty($envelope)) {
|
||||
return $this->json([]);
|
||||
}
|
||||
|
||||
try {
|
||||
// 2. Extract the header piece (first line)
|
||||
$pieces = explode("\n", $envelope, 2);
|
||||
$piece = $pieces[0];
|
||||
|
||||
// 3. Parse the header (which is JSON)
|
||||
$header = json_decode($piece, true);
|
||||
|
||||
if (!isset($header['dsn'])) {
|
||||
throw new \Exception("Missing DSN in envelope header.");
|
||||
}
|
||||
|
||||
// 4. Extract and validate DSN and Project ID
|
||||
$dsnUrl = parse_url($header['dsn']);
|
||||
$dsnHostname = $dsnUrl['host'] ?? null;
|
||||
$dsnPath = $dsnUrl['path'] ?? '/';
|
||||
|
||||
// Remove leading/trailing slashes from the path to get the project_id
|
||||
$projectId = trim($dsnPath, '/');
|
||||
|
||||
|
||||
if ($dsnHostname !== self::SENTRY_HOST) {
|
||||
throw new \Exception("Invalid sentry hostname: {$dsnHostname}");
|
||||
}
|
||||
|
||||
if (empty($projectId) || !in_array($projectId, self::SENTRY_PROJECT_IDS)) {
|
||||
throw new \Exception("Invalid sentry project id: {$projectId}");
|
||||
}
|
||||
|
||||
// 5. Construct the upstream Sentry URL
|
||||
$upstreamSentryUrl = "https://" . self::SENTRY_HOST . "/api/" . $projectId . "/envelope/";
|
||||
|
||||
// 6. Forward the request using an HTTP client (e.g., Guzzle)
|
||||
|
||||
$response = $httpClient->request("POST",$upstreamSentryUrl, [
|
||||
'body' => $envelope,
|
||||
'headers' => [
|
||||
// Sentry expects this content type
|
||||
'Content-Type' => 'application/x-sentry-envelope',
|
||||
// Forward the content encoding if present, though often not needed
|
||||
// 'Content-Encoding' => $request->headers->get('Content-Encoding'),
|
||||
],
|
||||
]);
|
||||
|
||||
// 7. Return the status from the upstream Sentry response
|
||||
return new JsonResponse([], $response->getStatusCode());
|
||||
|
||||
} catch (\Exception $e) {
|
||||
// Log the error for server-side debugging
|
||||
error_log("Error tunneling to Sentry: " . $e->getMessage());
|
||||
|
||||
// Return a success status (200/202) or a non-specific 500 to the client.
|
||||
// Returning a non-error status (like 200) is often preferred for tunnels
|
||||
// to avoid triggering ad-blockers on failures.
|
||||
return new JsonResponse([
|
||||
'error' => 'An error occurred during tunneling.'
|
||||
], JsonResponse::HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user