feat: scaffold matrix_admin_panel v0.1.0 (issue 0163)
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.
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user