improver error handling
All checks were successful
Build and Deploy MITM Webserver / build (push) Successful in 8s

This commit is contained in:
2025-12-18 21:45:43 +01:00
parent 9afe49857a
commit a2a017f900
7 changed files with 52 additions and 38 deletions

View File

@@ -353,7 +353,7 @@ def full_state(
@router.post("/bridge/create")
def create_bridge(req: BridgeCreateRequest, ip: IPRoute = Depends(get_iproute)):
if bridge_exists(req.name, ip):
raise HTTPException(400, f"Bridge {req.name} already exists")
raise HTTPException(400, detail=f"Bridge {req.name} already exists")
# Bridge erzeugen
ip.link("add", ifname=req.name, kind="bridge")

View File

@@ -4,10 +4,3 @@
padding: 2rem;
text-align: center;
}
.drawer-pages {
display: flex;
justify-content: flex-start;
align-items: center;
gap: 10px;
}

View File

@@ -9,10 +9,10 @@ import '../theme/layout.less';
const { Sider } = Layout;
const menuItems = [
{ key: PATHS.HOME, icon: <HomeOutlined />, label: 'Home' },
{ key: PATHS.NETWORK, icon: <ApartmentOutlined />, label: 'Network' },
{ key: '/about', icon: <InfoCircleOutlined />, label: 'About' },
{ key: '/settings', icon: <SettingOutlined />, label: 'Settings' },
{ key: PATHS.HOME, icon: <HomeOutlined style={{ fontSize: '18px' }} />, label: 'Home' },
{ key: PATHS.NETWORK, icon: <ApartmentOutlined style={{ fontSize: '18px' }} />, label: 'Network' },
{ key: '/about', icon: <InfoCircleOutlined style={{ fontSize: '18px' }} />, label: 'About' },
{ key: '/settings', icon: <SettingOutlined style={{ fontSize: '18px' }} />, label: 'Settings' },
];
interface SidebarProps {

View File

@@ -17,6 +17,22 @@ export const api = axios.create({
timeout: 10000,
});
// 🔹 Normalize FastAPI errors here
api.interceptors.response.use(
(response) => response,
(error) => {
// FastAPI HTTPException format
const detail =
error?.response?.data?.detail ??
error?.response?.data?.message ??
error.message ??
"Unknown error";
// Always reject with a standard Error
return Promise.reject(new Error(detail));
}
);
// Queries
export const fetchInterfaces = async (): Promise<InterfaceInfo[]> => {
const res = await api.get<InterfaceInfo[]>("/network/interfaces");

View File

@@ -1,8 +1,8 @@
// src/AppRouter.tsx
import { Navigate, Route, Routes } from 'react-router-dom';
import App from './App'; // your layout component (has <Outlet />)
import Bridges from './pages/Bridges';
import Home from './pages/Home';
import Network from './pages/Network';
import { PATHS } from './routes';
export default function AppRouter() {
@@ -15,7 +15,7 @@ export default function AppRouter() {
{/* Child routes - these render inside App's <Outlet /> */}
<Route path={PATHS.HOME.slice(1)} element={<Home />} />
<Route path={PATHS.NETWORK.slice(1)} element={<Bridges />} />
<Route path={PATHS.NETWORK.slice(1)} element={<Network />} />
{/* Fallback (renders inside layout too) */}
<Route path="*" element={<NotFound />} />

View File

@@ -8,7 +8,7 @@ import type { BridgeInfo, InterfaceInfo } from '../types/network';
const { Title, Paragraph } = Typography;
export default function NetworkManager() {
export default function Network() {
const {
interfacesQuery,
bridgesQuery,
@@ -59,7 +59,7 @@ export default function NetworkManager() {
key: 'addresses',
render: (addrs: any[]) =>
addrs?.length ? (
<Space direction="vertical">
<Space orientation="vertical">
{addrs.map((a) => (
<span key={`${a.address}/${a.prefixlen}`}>
{a.address}/{a.prefixlen} ({a.family})
@@ -79,7 +79,7 @@ export default function NetworkManager() {
() => [
{ title: 'IfIndex', dataIndex: 'ifindex', key: 'ifindex', width: 90 },
{ title: 'Name', dataIndex: 'ifname', key: 'ifname' },
{ title: 'State', dataIndex: 'state', key: 'state', render: (s) => s ?? '—' },
{ title: 'State', dataIndex: 'state', key: 'state', render: (s) => <Tag>{s ?? '—'}</Tag> },
{
title: 'Members',
dataIndex: 'members',
@@ -106,36 +106,36 @@ export default function NetworkManager() {
],
[isRemoving],
);
async function handleCreateBridge(values: { name: string; interfaces?: string[] }) {
const ifaceList = values.interfaces ?? [];
try {
await createBridge({ name: values.name, interfaces: ifaceList });
createBridge({ name: values.name, interfaces: ifaceList })
.then(() => {
message.success(`Bridge ${values.name} created`);
setBridgeModalVisible(false);
bridgeForm.resetFields();
} catch (err) {
})
.catch((err) => {
console.error(err);
message.error((err as Error)?.message ?? 'Failed to create bridge');
}
message.error((err as Error).message ?? 'Failed to create bridge');
});
}
async function handleRemoveBridge(name: string) {
try {
await removeBridge({ name });
function handleRemoveBridge(name: string) {
removeBridge({ name })
.then(() => {
message.success(`Bridge ${name} removed`);
} catch (err) {
})
.catch((err) => {
console.error(err);
message.error((err as Error)?.message ?? 'Failed to remove bridge');
}
message.error((err as Error).message ?? 'Failed to remove bridge');
});
}
return (
<div style={{ padding: 16 }}>
<Row justify="space-between" align="middle" style={{ marginBottom: 12 }}>
<Col>
<Title level={4}>Bridges & Interfaces</Title>
<Title level={2}> Network Management</Title>
<Paragraph type="secondary">View system interfaces and manage network bridges.</Paragraph>
</Col>
<Col>

View File

@@ -46,6 +46,10 @@ body {
li span {
font-size: @font-size-slider;
}
.ant-menu-title-content{
text-align: left;
padding-left: 10px;
}
}
/* Inner layout (app-main) is a column flex that occupies the remaining width */
@@ -79,6 +83,7 @@ body {
/* this makes the main content scrollable */
background: @component-background;
padding: 24px;
text-align: left;
}
/* Footer: fixed at bottom of app-main */