v1.1.0: session guard on all game routes + register + sign-in screen (ISS-107)
CI / build + unit + e2e (pull_request) Failing after 26m20s
CI / build + unit + e2e (pull_request) Failing after 26m20s
This commit is contained in:
@@ -1,13 +1,12 @@
|
||||
'use client';
|
||||
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { clearStoredSession, sessionHeaders, storedSession, storeSession } from '../lib/authClient';
|
||||
|
||||
// v0.71.0 — content of the Admin modal (opened by the top-bar "Admin" button). Three views:
|
||||
// login form (no valid session), a "not an admin" notice, or the generic whole-database CRUD
|
||||
// grid behind /api/admin/*. This is a dev tool surface — plain and functional on purpose.
|
||||
|
||||
const SESSION_STORAGE_KEY = 'flight.session';
|
||||
|
||||
interface Me {
|
||||
username: string;
|
||||
admin: boolean;
|
||||
@@ -18,15 +17,7 @@ interface RowsData {
|
||||
rows: Array<Record<string, unknown>>;
|
||||
}
|
||||
|
||||
function readSession(): string | null {
|
||||
try {
|
||||
return window.localStorage.getItem(SESSION_STORAGE_KEY);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export default function AdminPanel() {
|
||||
export default function AdminPanel({ onLogout }: { onLogout?: () => void }) {
|
||||
const [session, setSession] = useState<string | null>(null);
|
||||
const [me, setMe] = useState<Me | null>(null); // null while checking /api/me on mount
|
||||
const [checking, setChecking] = useState(true);
|
||||
@@ -45,15 +36,12 @@ export default function AdminPanel() {
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const authHeaders = useCallback(
|
||||
(s: string) => ({ 'X-Flight-Session': s }),
|
||||
[],
|
||||
);
|
||||
const authHeaders = useCallback((s: string) => sessionHeaders(s), []);
|
||||
|
||||
// On mount: a stored session decides between the grid and the login form.
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
const stored = readSession();
|
||||
const stored = storedSession();
|
||||
if (!stored) {
|
||||
setChecking(false);
|
||||
return;
|
||||
@@ -61,7 +49,7 @@ export default function AdminPanel() {
|
||||
fetch('/api/me', { headers: authHeaders(stored) })
|
||||
.then(async (res) => {
|
||||
if (!res.ok || cancelled) {
|
||||
window.localStorage.removeItem(SESSION_STORAGE_KEY);
|
||||
clearStoredSession();
|
||||
setSession(null);
|
||||
return;
|
||||
}
|
||||
@@ -71,7 +59,7 @@ export default function AdminPanel() {
|
||||
})
|
||||
.catch(() => {
|
||||
if (!cancelled) {
|
||||
window.localStorage.removeItem(SESSION_STORAGE_KEY);
|
||||
clearStoredSession();
|
||||
setSession(null);
|
||||
}
|
||||
})
|
||||
@@ -161,7 +149,7 @@ export default function AdminPanel() {
|
||||
return;
|
||||
}
|
||||
const body = (await res.json()) as { sessionId: string; user: Me };
|
||||
window.localStorage.setItem(SESSION_STORAGE_KEY, body.sessionId);
|
||||
storeSession(body.sessionId);
|
||||
setSession(body.sessionId);
|
||||
setMe(body.user);
|
||||
setUsername('');
|
||||
@@ -170,7 +158,8 @@ export default function AdminPanel() {
|
||||
|
||||
const logout = async () => {
|
||||
if (session) await fetch('/api/logout', { method: 'POST', headers: authHeaders(session) }).catch(() => undefined);
|
||||
window.localStorage.removeItem(SESSION_STORAGE_KEY);
|
||||
clearStoredSession();
|
||||
onLogout?.(); // v1.1.0 (ISS-107): session gone → shell drops back to the sign-in screen
|
||||
setSession(null);
|
||||
setMe(null);
|
||||
setTable(null);
|
||||
|
||||
Reference in New Issue
Block a user