re-vibecode

This commit is contained in:
Debian
2026-03-30 13:56:47 +00:00
parent 42b50b78cc
commit 09d76c044b
17 changed files with 1030 additions and 1135 deletions
+190 -71
View File
@@ -1,77 +1,196 @@
import { useState } from "react";
import type { ProfileSettings } from "@internal-mail/shared";
import { LoginPage } from "./pages/LoginPage";
import { MailboxPage } from "./pages/MailboxPage";
import { SettingsPage } from "./pages/SettingsPage";
import { useSession } from "./hooks/useSession";
import { useEffect, useState } from "react";
import type { AdminSession, CreateUserResponse, MailUser } from "@internal-mail/shared";
import { api } from "./api/client";
import DashboardPage from "./pages/DashboardPage";
import LoginPage from "./pages/LoginPage";
import ResetPasswordPage from "./pages/ResetPasswordPage";
type View = "inbox" | "compose" | "settings";
const anonymousSession: AdminSession = {
authenticated: false,
adminEmail: null
};
const AppShell = () => {
const { session, logout } = useSession();
const [currentView, setCurrentView] = useState<View>("inbox");
const [profileOverride, setProfileOverride] = useState<ProfileSettings | null>(null);
const getPath = (): string => window.location.pathname;
const user = profileOverride ?? session.user;
export default function App() {
const [path, setPath] = useState(getPath());
const [session, setSession] = useState<AdminSession | null>(null);
const [users, setUsers] = useState<MailUser[]>([]);
const [busy, setBusy] = useState(false);
const [loginError, setLoginError] = useState<string | null>(null);
const [feedback, setFeedback] = useState<string | null>(null);
const [errorMessage, setErrorMessage] = useState<string | null>(null);
useEffect(() => {
const handlePopstate = () => setPath(getPath());
window.addEventListener("popstate", handlePopstate);
return () => window.removeEventListener("popstate", handlePopstate);
}, []);
const resetToken = new URLSearchParams(window.location.search).get("token") ?? "";
const loadUsers = async () => {
const response = await api.listUsers();
setUsers(response.items);
};
useEffect(() => {
if (path.startsWith("/reset-password")) {
return;
}
api
.getSession()
.then((response) => {
setSession(response.session);
if (!response.session.authenticated) {
return;
}
loadUsers().catch((error) => {
setErrorMessage(error instanceof Error ? error.message : "Could not load users.");
});
})
.catch(() => {
setSession(anonymousSession);
});
}, [path]);
const handleLogin = async (payload: { email: string; password: string }) => {
setBusy(true);
setLoginError(null);
try {
const response = await api.login(payload);
setSession(response.session);
setErrorMessage(null);
await loadUsers().catch((error) => {
setErrorMessage(error instanceof Error ? error.message : "Signed in, but could not load users.");
});
} catch (error) {
setLoginError(error instanceof Error ? error.message : "Could not sign in.");
} finally {
setBusy(false);
}
};
const handleLogout = async () => {
setBusy(true);
try {
const response = await api.logout();
setSession(response.session);
setUsers([]);
setFeedback(null);
setErrorMessage(null);
} finally {
setBusy(false);
}
};
const handleCreateUser = async (email: string): Promise<CreateUserResponse> => {
setBusy(true);
setFeedback(null);
setErrorMessage(null);
try {
const result = await api.createUser({ email });
await loadUsers();
return result;
} finally {
setBusy(false);
}
};
const handleUpdatePassword = async (email: string, password: string) => {
setBusy(true);
setFeedback(null);
setErrorMessage(null);
try {
await api.updatePassword(email, { password });
await loadUsers();
} finally {
setBusy(false);
}
};
const handleDeleteUser = async (email: string) => {
setBusy(true);
setFeedback(null);
setErrorMessage(null);
try {
await api.deleteUser(email);
await loadUsers();
} finally {
setBusy(false);
}
};
const handleTriggerReset = async (email: string) => {
setBusy(true);
setFeedback(null);
setErrorMessage(null);
try {
const result = await api.triggerPasswordReset(email);
return result.expiresAt;
} finally {
setBusy(false);
}
};
const handleRefresh = async () => {
setBusy(true);
try {
await loadUsers();
setErrorMessage(null);
} catch (error) {
setErrorMessage(error instanceof Error ? error.message : "Could not refresh users.");
} finally {
setBusy(false);
}
};
if (path.startsWith("/reset-password")) {
return <ResetPasswordPage token={resetToken} />;
}
if (session === null) {
return (
<main className="auth-shell">
<section className="auth-card">
<div className="eyebrow">Mail Admin</div>
<h1>Loading</h1>
<p className="auth-copy">Checking your admin session.</p>
</section>
</main>
);
}
if (!session || !session.authenticated || !session.adminEmail) {
return (
<LoginPage
busy={busy}
error={loginError}
onSubmit={handleLogin}
/>
);
}
return (
<main className="app-shell">
<aside className="sidebar">
<div className="brand-block">
<div className="brand-block__overline">Zero-trust posture</div>
<h1>Secure Webmail</h1>
<p>Docker-managed mailboxes with a web layer designed to fail closed.</p>
</div>
<nav className="nav-stack">
<button type="button" className={currentView === "inbox" ? "nav-button nav-button--active" : "nav-button"} onClick={() => setCurrentView("inbox")}>
Inbox
</button>
<button type="button" className={currentView === "compose" ? "nav-button nav-button--active" : "nav-button"} onClick={() => setCurrentView("compose")}>
Compose
</button>
<button type="button" className={currentView === "settings" ? "nav-button nav-button--active" : "nav-button"} onClick={() => setCurrentView("settings")}>
Settings
</button>
</nav>
<div className="sidebar__footer">
<div className="identity-card">
{user?.avatarUrl ? <img src={user.avatarUrl} alt="" className="avatar-mini" /> : <div className="avatar-fallback">{user?.displayName.slice(0, 1) ?? "?"}</div>}
<div>
<strong>{user?.displayName}</strong>
<p>{user?.email}</p>
<small>{user?.totpEnabled ? "2FA enabled" : "2FA optional"}</small>
</div>
</div>
<button type="button" className="ghost-button" onClick={() => void logout()}>
Sign out
</button>
</div>
</aside>
<section className="content-shell">
{currentView === "settings" ? (
<SettingsPage onProfileUpdate={(profile) => setProfileOverride(profile)} />
) : (
<MailboxPage currentView={currentView === "compose" ? "compose" : "inbox"} />
)}
</section>
</main>
<DashboardPage
adminEmail={session.adminEmail}
users={users}
busy={busy}
feedback={feedback}
errorMessage={errorMessage}
onCreateUser={handleCreateUser}
onUpdatePassword={handleUpdatePassword}
onDeleteUser={handleDeleteUser}
onTriggerReset={handleTriggerReset}
onRefresh={handleRefresh}
onLogout={handleLogout}
/>
);
};
export const App = () => {
const { loading, authBusy, error, login, session } = useSession();
if (loading) {
return <main className="loading-screen">Loading secure session context...</main>;
}
if (!session.authenticated) {
return <LoginPage busy={authBusy} error={error} onLogin={login} />;
}
return <AppShell />;
};
}