feat: Sentry monitoring a kompletnější sledování stavů Bolt Food

- integrace Sentry na serveru (@sentry/node) i klientu (@sentry/react):
  chyby 5xx, pády a console logy se hlásí do self-hosted Sentry
- nový veřejný endpoint GET /api/config — klient si z něj za běhu
  vyzvedne Sentry DSN (SENTRY_DSN / SENTRY_CLIENT_DSN v env)
- ErrorBoundary na klientu a sourcemapy pro čitelné stack traces
- oprava mapování stavu waiting_acceptance (zobrazoval se jako Příprava)
- mapování stavů kurýra matched/accepted, logování přechodů stavů
  Bolt objednávek pro postupné zmapování nedokumentovaného API
- simulátor Bolt ve vývoji nově prochází celou reálnou sekvenci stavů
- vypnuté logování connect/disconnect websocket klientů
This commit is contained in:
2026-07-14 11:25:49 +02:00
parent a708eda421
commit 12fa87f3ad
17 changed files with 420 additions and 17 deletions
+1
View File
@@ -10,6 +10,7 @@
"@fortawesome/free-regular-svg-icons": "^7.1.0",
"@fortawesome/free-solid-svg-icons": "^7.1.0",
"@fortawesome/react-fontawesome": "^3.1.0",
"@sentry/react": "^10.65.0",
"@types/jest": "^30.0.0",
"@types/node": "^24.10.0",
"@types/react": "^19.2.2",
@@ -14,6 +14,7 @@ const STEPS = ['Přijato', 'Příprava', 'Vyzvedávání', 'Na cestě', 'Doruče
const ORDER_STATE_TO_STEP: Record<string, number> = {
created: 0,
pending: 0,
waiting_acceptance: 0,
accepted: 0,
waiting_preparation: 0,
preparing: 1,
@@ -31,6 +32,8 @@ const ORDER_STATE_TO_STEP: Record<string, number> = {
/** Stavy kurýra z Bolt API → index kroku. Kurýr u podniku ještě neznamená "na cestě". */
const COURIER_STATE_TO_STEP: Record<string, number> = {
matched: 0,
accepted: 0,
heading_to_provider: 1,
arrived_to_provider: 1,
picked_up: 3,
@@ -49,6 +52,7 @@ function stepForOrderState(state: string): number | 'cancelled' {
if (s in ORDER_STATE_TO_STEP) return ORDER_STATE_TO_STEP[s];
if (/cancel|reject|fail/.test(s)) return 'cancelled';
if (/delivered|finished/.test(s)) return 4;
if (/accept|pending|created/.test(s)) return 0;
if (/^waiting|prepar|ready|cook/.test(s)) return 1;
if (/picked|delivering|heading_to_client|transport/.test(s)) return 3;
return 0;
+31 -5
View File
@@ -6,20 +6,38 @@ import './index.css';
import AppRoutes from './AppRoutes';
import { BrowserRouter } from 'react-router';
import { client } from '../../types/gen/client.gen';
import { getConfig } from '../../types/gen/sdk.gen';
import { getToken } from './Utils';
import { toast } from 'react-toastify';
import * as Sentry from '@sentry/react';
client.setConfig({
auth: () => getToken(),
baseUrl: '/api', // openapi-ts si to z nějakého důvodu neumí převzít z api.yml
});
// Sentry se inicializuje až podle runtime konfigurace ze serveru (bez DSN zůstává vypnuté).
// Klient je statický build, takže DSN nejde zapéct při buildu — server ho zná z env.
getConfig().then(({ data }) => {
if (data?.sentry.dsn) {
Sentry.init({
dsn: data.sentry.dsn,
environment: data.sentry.environment,
enableLogs: true,
});
}
}).catch(() => { /* config endpoint nedostupný — běžíme bez Sentry */ });
// Interceptor na vyhození toasteru při chybě
client.interceptors.response.use(async response => {
// TODO opravit - login je zatím výjimka, voláme ho "naprázdno" abychom zjistili, zda nás nepřihlásily trusted headers
if (!response.ok && !response.url.includes("/login")) {
const json = await response.json();
toast.error(json.error, { theme: "colored" });
// Serverové chyby hlásíme do Sentry; 4xx jsou očekávané (chyby uživatele)
if (response.status >= 500) {
Sentry.captureMessage(`API ${response.status}: ${new URL(response.url).pathname}${json.error}`, 'error');
}
}
return response;
});
@@ -29,10 +47,18 @@ const root = ReactDOM.createRoot(
);
root.render(
<React.StrictMode>
<BrowserRouter>
<ProvideAuth>
<AppRoutes />
</ProvideAuth>
</BrowserRouter>
<Sentry.ErrorBoundary fallback={
<div className="text-center p-5">
<h4>Něco se pokazilo 😕</h4>
<p>Chyba byla nahlášena. Zkuste stránku načíst znovu.</p>
<button className="btn btn-primary" onClick={() => window.location.reload()}>Načíst znovu</button>
</div>
}>
<BrowserRouter>
<ProvideAuth>
<AppRoutes />
</ProvideAuth>
</BrowserRouter>
</Sentry.ErrorBoundary>
</React.StrictMode>
);
+4
View File
@@ -6,6 +6,10 @@ export default defineConfig({
// depending on your application, base can also be "/"
base: '',
plugins: [react(), viteTsconfigPaths()],
build: {
// Veřejné sourcemapy, aby Sentry umělo přeložit minifikované stack traces
sourcemap: true,
},
server: {
open: true,
host: '0.0.0.0',
+64
View File
@@ -854,6 +854,70 @@
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.55.1.tgz#f79437939020b83057faf07e98365b1fa51c458b"
integrity sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw==
"@sentry/browser-utils@10.65.0":
version "10.65.0"
resolved "https://registry.yarnpkg.com/@sentry/browser-utils/-/browser-utils-10.65.0.tgz#e3b67c6b9dd05dad2ad7eee82ad873bfc9f8ab12"
integrity sha512-4J0mkfNJAGUOkpg1ZggizyftFTn9N20b+Jl87UnWsDUkNG0Ic1l/FIzMPTVxXrAnhBGu0ULO0TFWMoQ5s3QtZw==
dependencies:
"@sentry/conventions" "^0.15.1"
"@sentry/core" "10.65.0"
"@sentry/browser@10.65.0":
version "10.65.0"
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-10.65.0.tgz#e2734a2effe051a9005afdd914fc07c68262f627"
integrity sha512-XUDDsx0qxzeIlcOu1fDEqTcDl0eiOqghsgV+ReuuNP4jYjZ9kUQxE3rXWM5mlT1pBi4VaQ4FHqvQZZrRXy+oDw==
dependencies:
"@sentry/browser-utils" "10.65.0"
"@sentry/conventions" "^0.15.1"
"@sentry/core" "10.65.0"
"@sentry/feedback" "10.65.0"
"@sentry/replay" "10.65.0"
"@sentry/replay-canvas" "10.65.0"
"@sentry/conventions@^0.15.1":
version "0.15.1"
resolved "https://registry.yarnpkg.com/@sentry/conventions/-/conventions-0.15.1.tgz#5b8888862d1e444678938f5afdd7842bca42f7cc"
integrity sha512-ZLP8bRdMON3prWE2tJyImuYscCxdcJeIPIhrOs/rgyFm3C1nCh1B6gdvPj3AZ5zW08oSFFCsq7T+tYEW3h8MNA==
"@sentry/core@10.65.0":
version "10.65.0"
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-10.65.0.tgz#a98cfd6e138d9658dd42d050902837ff591452e8"
integrity sha512-3aqtmM5NgNGo45BNaaBzi0LPQZAw//NEL4HKS5fXm12pJMa4KEkze8DEKnkTEIrGnWaOJKamecHKlnNg/Mqf/Q==
dependencies:
"@sentry/conventions" "^0.15.1"
"@sentry/feedback@10.65.0":
version "10.65.0"
resolved "https://registry.yarnpkg.com/@sentry/feedback/-/feedback-10.65.0.tgz#a9418cc77c835e3a74cdc3fad2fb1098ecbc3887"
integrity sha512-ck8h7wgd3F3bYNk0v1OgohmyLBeXcKxqlfBJRtQq4k6KZUq+pXimOG7ckNguVMYjCo3PEfuG+ckKc21yqotKug==
dependencies:
"@sentry/core" "10.65.0"
"@sentry/react@^10.65.0":
version "10.65.0"
resolved "https://registry.yarnpkg.com/@sentry/react/-/react-10.65.0.tgz#83552b6e136d827782915bf4002ff5916dc11ccc"
integrity sha512-fvHxpuvid0wt9/1N3itcKDyKOjqmYHw3MBSt5Pki3Iz4CL2CmgQp9ZFv/CA7UhMnEvn2Gd+Qc2UKxujZWd8FLg==
dependencies:
"@sentry/browser" "10.65.0"
"@sentry/conventions" "^0.15.1"
"@sentry/core" "10.65.0"
"@sentry/replay-canvas@10.65.0":
version "10.65.0"
resolved "https://registry.yarnpkg.com/@sentry/replay-canvas/-/replay-canvas-10.65.0.tgz#5ff20832ae8acacd38fe7f154dd7ac6c6cad1af2"
integrity sha512-A7X3RVk1Gk+knK8Ip/2EjejckNCLgCfRZo6eGlsy6qyz904KBpYmys1a0o7QkzFRjhIndjHAfcVxwt6jSLJlrQ==
dependencies:
"@sentry/core" "10.65.0"
"@sentry/replay" "10.65.0"
"@sentry/replay@10.65.0":
version "10.65.0"
resolved "https://registry.yarnpkg.com/@sentry/replay/-/replay-10.65.0.tgz#8ed1255349e32faeac786e4ca2f4c2f56b98e64e"
integrity sha512-aW988CcQBNArbOMzOFOziipHz6uQyXSa4i5CPWsu+nhVPTJHafosi5Lv9n6NM/icDX5e23VdnX6mZd8SyJuo8A==
dependencies:
"@sentry/browser-utils" "10.65.0"
"@sentry/core" "10.65.0"
"@sinclair/typebox@^0.34.0":
version "0.34.41"
resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.34.41.tgz#aa51a6c1946df2c5a11494a2cdb9318e026db16c"