Sign panel sessions and isolate test runs
Some checks failed
Deploy to Production / test (push) Failing after 29s

- Replace in-memory auth sessions with signed cookies and signed OAuth state
- Add auth route coverage and update panel/web server wiring
- Switch test script to per-file Bun processes and clean up type checks
This commit is contained in:
syntaxbullet
2026-04-09 21:44:05 +02:00
parent 6abbd4652a
commit 25a0bd3431
25 changed files with 354 additions and 157 deletions

View File

@@ -19,11 +19,12 @@ export function useAuth(): AuthState & { logout: () => Promise<void> } {
useEffect(() => {
fetch("/auth/me", { credentials: "same-origin" })
.then((r) => r.json())
.then((data: { authenticated: boolean; enrolled: boolean; user?: AuthUser }) => {
.then((data) => {
const auth = data as { authenticated: boolean; enrolled: boolean; user?: AuthUser };
setState({
loading: false,
user: data.authenticated ? data.user! : null,
enrolled: data.enrolled ?? true,
user: auth.authenticated ? auth.user ?? null : null,
enrolled: auth.enrolled ?? true,
});
})
.catch(() => setState({ loading: false, user: null, enrolled: true }));

View File

@@ -28,11 +28,15 @@ export function useGameRoom(roomId: string, userId: string, role?: string, prefe
const { send, subscribe, connected } = useWebSocket();
const navigate = useNavigate();
const navigateRef = useRef(navigate);
const errorTimerRef = useRef<ReturnType<typeof setTimeout>>();
const errorTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
useEffect(() => {
navigateRef.current = navigate;
}, [navigate]);
useEffect(() => () => clearTimeout(errorTimerRef.current), []);
useEffect(() => () => {
if (errorTimerRef.current !== null) {
clearTimeout(errorTimerRef.current);
}
}, []);
const [state, setState] = useState<GameRoomState>({
gameState: null,
@@ -153,7 +157,9 @@ export function useGameRoom(roomId: string, userId: string, role?: string, prefe
setTimeout(() => navigateRef.current("/games"), 2000);
} else {
setState(prev => ({ ...prev, error: msg.message }));
clearTimeout(errorTimerRef.current);
if (errorTimerRef.current !== null) {
clearTimeout(errorTimerRef.current);
}
errorTimerRef.current = setTimeout(() => {
setState(prev => ({ ...prev, error: null }));
}, 5000);

View File

@@ -44,7 +44,7 @@ function rgbToHex(r: number, g: number, b: number): string {
function hexToRgb(hex: string): [number, number, number] | null {
const m = /^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(hex.trim());
if (!m) return null;
return [parseInt(m[1], 16), parseInt(m[2], 16), parseInt(m[3], 16)];
return [parseInt(m[1]!, 16), parseInt(m[2]!, 16), parseInt(m[3]!, 16)];
}
// ---------------------------------------------------------------------------
@@ -380,7 +380,7 @@ function AiRemoveTab({ imageFile, imageSrc, onClear }: {
</div>
<div className="bg-card rounded-xl overflow-hidden">
{resultUrl ? (
<div style={BG_PRESETS[bgPreset].style}>
<div style={BG_PRESETS[bgPreset]!.style}>
<img src={resultUrl} className="w-full block" alt="Result" />
</div>
) : (
@@ -528,7 +528,7 @@ export function BackgroundRemoval() {
const x = Math.floor((e.clientX - rect.left) * scaleX);
const y = Math.floor((e.clientY - rect.top) * scaleY);
const px = canvas.getContext("2d")!.getImageData(x, y, 1, 1).data;
setKeyColor([px[0], px[1], px[2]]);
setKeyColor([px[0]!, px[1]!, px[2]!]);
};
const handleHexInput = (v: string) => {
@@ -832,7 +832,7 @@ export function BackgroundRemoval() {
</div>
</div>
<div className="bg-card rounded-xl overflow-hidden">
<div style={BG_PRESETS[bgPreset].style} className={cn("w-full", !hasResult && "hidden")}>
<div style={BG_PRESETS[bgPreset]!.style} className={cn("w-full", !hasResult && "hidden")}>
<canvas ref={glCanvasRef} className="w-full block" />
</div>
{!hasResult && (

View File

@@ -468,7 +468,7 @@ export function CanvasTool() {
</div>
</div>
<div className="bg-card rounded-xl overflow-hidden">
<div style={BG_PRESETS[bgPreset].style} className={cn("w-full", !imageReady && "hidden")}>
<div style={BG_PRESETS[bgPreset]!.style} className={cn("w-full", !imageReady && "hidden")}>
<canvas ref={previewCanvasRef} className="w-full block" />
</div>
{!imageReady && (

View File

@@ -261,7 +261,7 @@ export function CropTool() {
let minX = width, minY = height, maxX = -1, maxY = -1;
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
if (data[(y * width + x) * 4 + 3] > 0) {
if (data[(y * width + x) * 4 + 3]! > 0) {
if (x < minX) minX = x;
if (x > maxX) maxX = x;
if (y < minY) minY = y;
@@ -287,7 +287,7 @@ export function CropTool() {
let minX = width, minY = height, maxX = -1, maxY = -1;
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
if (data[(y * width + x) * 4 + 3] > 0) {
if (data[(y * width + x) * 4 + 3]! > 0) {
if (x < minX) minX = x;
if (x > maxX) maxX = x;
if (y < minY) minY = y;