feat: migrate from static config to database and add authentication

- Replaced static hosts.json and staticConfig.ts with SQLite database (Prisma)

- Implemented JWT authentication and Login UI

- Added dynamic API routes for hosts, topology, and settings

- Updated UI components to fetch and manage state dynamically

- Added Settings interface for managing hosts and topology nodes
This commit is contained in:
2026-02-25 14:07:11 -08:00
parent df02542c26
commit 0910c966a5
37 changed files with 1884 additions and 645 deletions

View File

@@ -17,6 +17,8 @@ import CommandPalette from './components/CommandPalette';
import StaleWarning from './components/StaleWarning';
import TerminalPanel from './components/TerminalPanel';
import MetricsBar from './components/Dashboard/MetricsBar';
import Login from './components/Login';
import SettingsOverlay from './components/Settings/SettingsOverlay';
const API_BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:3001';
@@ -60,6 +62,7 @@ function App() {
setLastSuccessfulDiscovery: s.setLastSuccessfulDiscovery,
})));
const token = useTopologyStore((s) => s.token);
const setConnectionStatus = useTopologyStore((s) => s.setConnectionStatus);
const {
@@ -69,6 +72,8 @@ function App() {
pollInterval,
terminalOpen,
terminalHost,
toggleLeftPanel,
toggleRightPanel,
} = useTopologyStore(useShallow((s) => ({
leftPanelOpen: s.leftPanelOpen,
rightPanelOpen: s.rightPanelOpen,
@@ -76,6 +81,8 @@ function App() {
pollInterval: s.pollInterval,
terminalOpen: s.terminalOpen,
terminalHost: s.terminalHost,
toggleLeftPanel: s.toggleLeftPanel,
toggleRightPanel: s.toggleRightPanel,
})));
const toggleCommandPalette = useTopologyStore((s) => s.toggleCommandPalette);
@@ -99,7 +106,10 @@ function App() {
try {
const response = await fetch(`${API_BASE_URL}/api/discover`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' }
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${useTopologyStore.getState().token}`
}
});
if (response.ok) {
@@ -253,6 +263,10 @@ function App() {
};
}, [setConnectionStatus, setNodes, setEdges, setLastUpdated]);
if (!token) {
return <Login />;
}
return (
<ReactFlowProvider>
<div className="h-screen w-screen flex flex-col bg-slate-900">
@@ -265,22 +279,37 @@ function App() {
<Header onRefresh={loadData} isLoading={isLoading} />
<MetricsBar />
<div className="flex-1 flex overflow-hidden" role="main" id="main-content" tabIndex={-1}>
<div className="flex-1 flex overflow-hidden relative" role="main" id="main-content" tabIndex={-1}>
{leftPanelOpen && (
<LeftPanel />
<>
<div
className="absolute inset-0 bg-black/60 backdrop-blur-sm z-10 md:hidden animate-in fade-in duration-300"
onClick={toggleLeftPanel}
aria-hidden="true"
/>
<LeftPanel />
</>
)}
<div className="flex-1">
<div className="flex-1 relative z-0">
<TopologyGraph />
</div>
{rightPanelOpen && (
<RightPanel />
<>
<div
className="absolute inset-0 bg-black/60 backdrop-blur-sm z-10 md:hidden animate-in fade-in duration-300"
onClick={toggleRightPanel}
aria-hidden="true"
/>
<RightPanel />
</>
)}
</div>
<Footer />
<CommandPalette />
<SettingsOverlay />
{terminalOpen && terminalHost && (
<TerminalPanel host={terminalHost} onClose={closeTerminal} />
)}