release: v0.16.0

See CHANGELOG.md for the full v0.16.0 entry.

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: ralksta <ralksta@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Ralf <rohlenbo79@gmail.com>
Co-authored-by: Paul Thies <110185070+ImScheinox@users.noreply.github.com>
Co-authored-by: lancetm714 <108419425+lancetm714@users.noreply.github.com>
This commit is contained in:
Ralksta
2026-09-22 14:12:13 +02:00
committed by GitHub
parent c3f92ca4ec
commit a3a6319c29
120 changed files with 6818 additions and 975 deletions

View File

@@ -3,6 +3,7 @@
import { useState, useEffect, type ReactNode } from 'react';
import AdminLogin from './components/AdminLogin';
import AdminDashboard from './components/AdminDashboard';
import { SESSION_EXPIRED_EVENT } from './components/sessionExpiry';
import './admin.css';
/**
@@ -13,6 +14,13 @@ import './admin.css';
export default function AdminShell({ children }: { children: ReactNode }) {
const [authenticated, setAuthenticated] = useState<boolean | null>(null);
const [enabled, setEnabled] = useState(true);
// Sessions last 24 hours (lib/admin/auth.ts), but nothing re-checked
// authentication after the mount-time check below — leave a tab open
// overnight and every admin request answered 401 while the UI still looked
// signed in (#596). SESSION_EXPIRED_EVENT is raised by the fetch call sites
// that noticed; this is what turns that into dropping back to the login
// screen with an explanation instead of each of them failing quietly.
const [sessionExpired, setSessionExpired] = useState(false);
useEffect(() => {
async function checkAuth() {
@@ -28,6 +36,15 @@ export default function AdminShell({ children }: { children: ReactNode }) {
checkAuth();
}, []);
useEffect(() => {
function onSessionExpired() {
setAuthenticated(false);
setSessionExpired(true);
}
window.addEventListener(SESSION_EXPIRED_EVENT, onSessionExpired);
return () => window.removeEventListener(SESSION_EXPIRED_EVENT, onSessionExpired);
}, []);
if (authenticated === null) {
return (
<div className="admin-loading">
@@ -51,7 +68,15 @@ export default function AdminShell({ children }: { children: ReactNode }) {
}
if (!authenticated) {
return <AdminLogin onSuccess={() => setAuthenticated(true)} />;
return (
<AdminLogin
notice={sessionExpired ? 'Your session expired. Sign in again.' : undefined}
onSuccess={() => {
setSessionExpired(false);
setAuthenticated(true);
}}
/>
);
}
return <AdminDashboard onLogout={() => setAuthenticated(false)}>{children}</AdminDashboard>;