fix: 2026-06-09 full-codebase audit hardening

Validation: shared NaN-guarded Zod coercion helpers in schemas/common.ts replace
the raw number|string transform idiom across every schema (the root-cause NaN bug
class); emailOrEmpty + lenient isoDateString/timeString.

Security: roles privilege-escalation closed; refresh-token family revocation on
reuse; TOTP uses config params; read endpoints permission-guarded; received-invoices
gross VAT on all paths; orders-pdf custom-items authz.

Concurrency: $queryRaw SELECT...FOR UPDATE locks in ascending-id order (warehouse
confirm/cancel, attendance lockUserRow); uniqueness checks moved into create
transactions (TOCTOU -> 409); deterministic id tiebreak on second-precision
timestamp ordering (plan resolveCell/resolveGrid, warehouse FIFO).

Frontend: Rules-of-Hooks fixed across ~14 pages + PlanCellModal; UTC-date persisted
fields; dashboard invalidation gaps; stale-closure confirm bugs.

Tooling/tests: ESLint flat config (react-hooks/rules-of-hooks = error) + Prettier;
tsconfig.test.json so tsc -b type-checks the tests; removed 3 dead deps; npm audit
fix (8 -> 3). Suite 195 -> 247 (happy-path auth, FIFO oldest-first, flakiness fixes),
isolated on app_test via .env.test with a hard-throw setup guard.

Gates: tsc 0 | build 0 | vitest 247/247 | eslint 0 errors.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
BOHA
2026-06-09 06:45:26 +02:00
parent c454d1a3fc
commit 519edce373
179 changed files with 7179 additions and 2844 deletions

View File

@@ -2,6 +2,7 @@ import {
createContext,
useContext,
useState,
useEffect,
useLayoutEffect,
type ReactNode,
} from "react";
@@ -12,22 +13,40 @@ interface ThemeContextValue {
toggleTheme: () => void;
}
export type ThemeMode = "light" | "dark";
/**
* The first-paint theme fallback used before any stored preference is read.
*
* SINGLE SOURCE OF TRUTH for the default mode. `MuiProvider`'s
* `<ThemeProvider defaultMode="…">` MUST use this same value (import it from
* here) — if the two defaults drift, MUI and this context pick different modes
* on the first frame and the page/toggle desync until a reload. The two
* `localStorage.getItem("mui-mode")` fallbacks below both reference this so
* they can never diverge from each other either.
*/
export const DEFAULT_THEME_MODE: ThemeMode = "dark";
const ThemeContext = createContext<ThemeContextValue | null>(null);
function readStoredTheme(): ThemeMode {
// Single source of truth = MUI's own mode key. MUI's cssVariables provider
// reads `mui-mode` on mount and sets `data-theme` from it, so we read/write
// that SAME key (no separate boha-theme) — page and toggle stay in sync
// across reloads. Fall back to the legacy key for pre-existing sessions.
const stored =
localStorage.getItem("mui-mode") || localStorage.getItem("boha-theme");
return stored === "light" || stored === "dark" ? stored : DEFAULT_THEME_MODE;
}
type ViewTransitionDocument = Document & {
startViewTransition?: (callback: () => void) => unknown;
};
export function ThemeProvider({ children }: { children: ReactNode }) {
const [theme, setTheme] = useState(() => {
if (typeof window === "undefined") return "dark";
// Single source of truth = MUI's own mode key. MUI's cssVariables provider
// reads `mui-mode` on mount and sets `data-theme` from it, so we read/write
// that SAME key (no separate boha-theme) — page and toggle stay in sync
// across reloads. Fall back to the legacy key for pre-existing sessions.
const stored =
localStorage.getItem("mui-mode") || localStorage.getItem("boha-theme");
return stored === "light" || stored === "dark" ? stored : "dark";
const [theme, setTheme] = useState<ThemeMode>(() => {
if (typeof window === "undefined") return DEFAULT_THEME_MODE;
return readStoredTheme();
});
// Apply synchronously (layout effect) so the `data-theme` attribute flips
@@ -43,6 +62,22 @@ export function ThemeProvider({ children }: { children: ReactNode }) {
?.setAttribute("content", themeColor);
}, [theme]);
// Cross-tab sync: when another tab toggles the theme it writes `mui-mode`,
// which fires a `storage` event here. Mirror the new value (instant — no
// View Transition, since this tab didn't initiate the change) so all open
// tabs stay visually consistent without a reload.
useEffect(() => {
const onStorage = (e: StorageEvent) => {
if (e.key !== "mui-mode") return;
const next = e.newValue;
if (next === "light" || next === "dark") {
setTheme((prev) => (prev === next ? prev : next));
}
};
window.addEventListener("storage", onStorage);
return () => window.removeEventListener("storage", onStorage);
}, []);
const toggleTheme = () => {
const apply = () =>
setTheme((prev) => (prev === "dark" ? "light" : "dark"));