mirror of
https://github.com/0xWheatyz/handler.git
synced 2026-08-30 19:06:25 +00:00
Frontend: email sign-in, first-run setup, reset links, Users admin page
- AuthGate replaces the raw token prompt: first-run setup form (creates the admin) when no accounts exist, email/password sign-in with a forgot-password flow, and a collapsible raw-API-token fallback for legacy/script setups. - /reset is a public page where invite and password-reset links land; success stores the fresh session and enters the dashboard. - Users section (admin-only nav): invite by email (link always shown, emailed when SMTP is configured), admin/disable toggles, reset links, and delete with the shared-resources handoff spelled out. - Sidebar shows who is signed in; sign-out revokes the session server-side. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019ws7xj5Ej623hh4GXQCYYR
This commit is contained in:
@@ -35,6 +35,7 @@ const BADGES: Partial<Record<Section, { count: (s: Store) => number; accent?: (s
|
||||
// Draw the eye to it until Claude is logged in on the host this session.
|
||||
accent: (s) => s.claudeLogin.status !== "done",
|
||||
},
|
||||
users: { count: (s) => s.users.length },
|
||||
};
|
||||
|
||||
export function Shell({ onSignOut, children }: { onSignOut: () => void; children: React.ReactNode }) {
|
||||
@@ -55,7 +56,7 @@ export function Shell({ onSignOut, children }: { onSignOut: () => void; children
|
||||
<span className="logo" />
|
||||
Claude Monitor
|
||||
</div>
|
||||
{NAV_ROUTES.map((n) => {
|
||||
{NAV_ROUTES.filter((n) => n.key !== "users" || s.me?.is_admin).map((n) => {
|
||||
const badge = BADGES[n.key];
|
||||
const c = badge?.count(s) ?? 0;
|
||||
const isAccent = badge?.accent?.(s) ?? false;
|
||||
@@ -74,11 +75,30 @@ export function Shell({ onSignOut, children }: { onSignOut: () => void; children
|
||||
})}
|
||||
<div className="sidebar-spacer" />
|
||||
<div className="sidebar-foot">
|
||||
{s.me && (
|
||||
<div
|
||||
className="nav-item"
|
||||
style={{ cursor: "default", opacity: 0.8 }}
|
||||
title={s.me.kind === "token" ? "Authenticated with an API token" : "Signed in"}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
whiteSpace: "nowrap",
|
||||
fontSize: "var(--text-xs)",
|
||||
}}
|
||||
>
|
||||
{s.me.kind === "token" ? "API token" : s.me.email}
|
||||
{s.me.is_admin ? " · admin" : ""}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
<button className="nav-item" onClick={s.refresh} title="Refresh now">
|
||||
<span>Refresh</span>
|
||||
<span className="count">↻</span>
|
||||
</button>
|
||||
<button className="nav-item" onClick={onSignOut} title="Sign out / change token">
|
||||
<button className="nav-item" onClick={onSignOut} title="Sign out">
|
||||
<span>Sign out</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user