0e3c5f5e84
Wails + React + Mantine v7 admin panel for Matrix/Synapse. Replaces the removed synapse-admin container. MAS OIDC PKCE login (loopback :8766) + Synapse Admin API (users/rooms/sessions). - MAS client: XSFD2SWA394DXRVJFTREAMY6J6 (public PKCE, no auth method). - Backend: AdminService (Go) with Login/SetAdminToken/ListUsers/ DeactivateUser/ResetUserPassword/ListRooms/DeleteRoom/GetUserDevices. - Vendored helpers in internal/infra/ from registry: mas_oidc_loopback_go_infra, keyring_token_store_go_infra, synapse_admin_client_go_infra. - Frontend: AppShell + sidebar tabs (Users/Rooms/Sessions). Sessions placeholder pending MAS admin API. - Build verified: Linux + Windows.
92 lines
2.5 KiB
TypeScript
92 lines
2.5 KiB
TypeScript
import { useEffect, useState } from "react";
|
|
import { Box, LoadingOverlay } from "@mantine/core";
|
|
import LoginScreen from "./LoginScreen";
|
|
import HomeScreen from "./HomeScreen";
|
|
import AdminTokenModal from "./AdminTokenModal";
|
|
import { GetSession } from "../wailsjs/go/main/AdminService";
|
|
|
|
const LAST_USER_KEY = "matrix_admin_panel.last_user_id";
|
|
|
|
interface Session {
|
|
user_id: string;
|
|
homeserver_url: string;
|
|
has_oidc_token: boolean;
|
|
has_admin_token: boolean;
|
|
expires_at?: string;
|
|
}
|
|
|
|
export default function App() {
|
|
const [userID, setUserID] = useState<string | null>(null);
|
|
const [session, setSession] = useState<Session | null>(null);
|
|
const [loading, setLoading] = useState(true);
|
|
const [tokenModalOpen, setTokenModalOpen] = useState(false);
|
|
|
|
useEffect(() => {
|
|
const last = localStorage.getItem(LAST_USER_KEY);
|
|
if (!last) {
|
|
setLoading(false);
|
|
return;
|
|
}
|
|
GetSession(last)
|
|
.then((s) => {
|
|
const sess = s as Session | null;
|
|
if (sess && sess.has_oidc_token) {
|
|
setUserID(sess.user_id);
|
|
setSession(sess);
|
|
if (!sess.has_admin_token) {
|
|
setTokenModalOpen(true);
|
|
}
|
|
}
|
|
})
|
|
.finally(() => setLoading(false));
|
|
}, []);
|
|
|
|
async function refreshSession(uid: string) {
|
|
const s = (await GetSession(uid)) as Session | null;
|
|
if (s) setSession(s);
|
|
}
|
|
|
|
const handleLogin = async (uid: string) => {
|
|
localStorage.setItem(LAST_USER_KEY, uid);
|
|
setUserID(uid);
|
|
await refreshSession(uid);
|
|
// After OIDC login, ALWAYS prompt for admin token unless already saved.
|
|
const s = (await GetSession(uid)) as Session | null;
|
|
if (s && !s.has_admin_token) setTokenModalOpen(true);
|
|
};
|
|
|
|
const handleLogout = () => {
|
|
localStorage.removeItem(LAST_USER_KEY);
|
|
setUserID(null);
|
|
setSession(null);
|
|
};
|
|
|
|
const handleTokenSaved = async () => {
|
|
setTokenModalOpen(false);
|
|
if (userID) await refreshSession(userID);
|
|
};
|
|
|
|
return (
|
|
<Box pos="relative" mih="100vh">
|
|
<LoadingOverlay visible={loading} />
|
|
{userID ? (
|
|
<>
|
|
<HomeScreen
|
|
userID={userID}
|
|
session={session}
|
|
onLogout={handleLogout}
|
|
onRequestAdminToken={() => setTokenModalOpen(true)}
|
|
/>
|
|
<AdminTokenModal
|
|
opened={tokenModalOpen}
|
|
onClose={() => setTokenModalOpen(false)}
|
|
onSaved={handleTokenSaved}
|
|
/>
|
|
</>
|
|
) : (
|
|
<LoginScreen onLogin={handleLogin} />
|
|
)}
|
|
</Box>
|
|
);
|
|
}
|