improver error handling
All checks were successful
Build and Deploy MITM Webserver / build (push) Successful in 8s
All checks were successful
Build and Deploy MITM Webserver / build (push) Successful in 8s
This commit is contained in:
@@ -353,7 +353,7 @@ def full_state(
|
|||||||
@router.post("/bridge/create")
|
@router.post("/bridge/create")
|
||||||
def create_bridge(req: BridgeCreateRequest, ip: IPRoute = Depends(get_iproute)):
|
def create_bridge(req: BridgeCreateRequest, ip: IPRoute = Depends(get_iproute)):
|
||||||
if bridge_exists(req.name, ip):
|
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
|
# Bridge erzeugen
|
||||||
ip.link("add", ifname=req.name, kind="bridge")
|
ip.link("add", ifname=req.name, kind="bridge")
|
||||||
|
|||||||
@@ -4,10 +4,3 @@
|
|||||||
padding: 2rem;
|
padding: 2rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.drawer-pages {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-start;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ import '../theme/layout.less';
|
|||||||
const { Sider } = Layout;
|
const { Sider } = Layout;
|
||||||
|
|
||||||
const menuItems = [
|
const menuItems = [
|
||||||
{ key: PATHS.HOME, icon: <HomeOutlined />, label: 'Home' },
|
{ key: PATHS.HOME, icon: <HomeOutlined style={{ fontSize: '18px' }} />, label: 'Home' },
|
||||||
{ key: PATHS.NETWORK, icon: <ApartmentOutlined />, label: 'Network' },
|
{ key: PATHS.NETWORK, icon: <ApartmentOutlined style={{ fontSize: '18px' }} />, label: 'Network' },
|
||||||
{ key: '/about', icon: <InfoCircleOutlined />, label: 'About' },
|
{ key: '/about', icon: <InfoCircleOutlined style={{ fontSize: '18px' }} />, label: 'About' },
|
||||||
{ key: '/settings', icon: <SettingOutlined />, label: 'Settings' },
|
{ key: '/settings', icon: <SettingOutlined style={{ fontSize: '18px' }} />, label: 'Settings' },
|
||||||
];
|
];
|
||||||
|
|
||||||
interface SidebarProps {
|
interface SidebarProps {
|
||||||
|
|||||||
@@ -17,6 +17,22 @@ export const api = axios.create({
|
|||||||
timeout: 10000,
|
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
|
// Queries
|
||||||
export const fetchInterfaces = async (): Promise<InterfaceInfo[]> => {
|
export const fetchInterfaces = async (): Promise<InterfaceInfo[]> => {
|
||||||
const res = await api.get<InterfaceInfo[]>("/network/interfaces");
|
const res = await api.get<InterfaceInfo[]>("/network/interfaces");
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
// src/AppRouter.tsx
|
// src/AppRouter.tsx
|
||||||
import { Navigate, Route, Routes } from 'react-router-dom';
|
import { Navigate, Route, Routes } from 'react-router-dom';
|
||||||
import App from './App'; // your layout component (has <Outlet />)
|
import App from './App'; // your layout component (has <Outlet />)
|
||||||
import Bridges from './pages/Bridges';
|
|
||||||
import Home from './pages/Home';
|
import Home from './pages/Home';
|
||||||
|
import Network from './pages/Network';
|
||||||
import { PATHS } from './routes';
|
import { PATHS } from './routes';
|
||||||
|
|
||||||
export default function AppRouter() {
|
export default function AppRouter() {
|
||||||
@@ -15,7 +15,7 @@ export default function AppRouter() {
|
|||||||
|
|
||||||
{/* Child routes - these render inside App's <Outlet /> */}
|
{/* Child routes - these render inside App's <Outlet /> */}
|
||||||
<Route path={PATHS.HOME.slice(1)} element={<Home />} />
|
<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) */}
|
{/* Fallback (renders inside layout too) */}
|
||||||
<Route path="*" element={<NotFound />} />
|
<Route path="*" element={<NotFound />} />
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import type { BridgeInfo, InterfaceInfo } from '../types/network';
|
|||||||
|
|
||||||
const { Title, Paragraph } = Typography;
|
const { Title, Paragraph } = Typography;
|
||||||
|
|
||||||
export default function NetworkManager() {
|
export default function Network() {
|
||||||
const {
|
const {
|
||||||
interfacesQuery,
|
interfacesQuery,
|
||||||
bridgesQuery,
|
bridgesQuery,
|
||||||
@@ -59,7 +59,7 @@ export default function NetworkManager() {
|
|||||||
key: 'addresses',
|
key: 'addresses',
|
||||||
render: (addrs: any[]) =>
|
render: (addrs: any[]) =>
|
||||||
addrs?.length ? (
|
addrs?.length ? (
|
||||||
<Space direction="vertical">
|
<Space orientation="vertical">
|
||||||
{addrs.map((a) => (
|
{addrs.map((a) => (
|
||||||
<span key={`${a.address}/${a.prefixlen}`}>
|
<span key={`${a.address}/${a.prefixlen}`}>
|
||||||
{a.address}/{a.prefixlen} ({a.family})
|
{a.address}/{a.prefixlen} ({a.family})
|
||||||
@@ -79,7 +79,7 @@ export default function NetworkManager() {
|
|||||||
() => [
|
() => [
|
||||||
{ title: 'IfIndex', dataIndex: 'ifindex', key: 'ifindex', width: 90 },
|
{ title: 'IfIndex', dataIndex: 'ifindex', key: 'ifindex', width: 90 },
|
||||||
{ title: 'Name', dataIndex: 'ifname', key: 'ifname' },
|
{ 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',
|
title: 'Members',
|
||||||
dataIndex: 'members',
|
dataIndex: 'members',
|
||||||
@@ -106,36 +106,36 @@ export default function NetworkManager() {
|
|||||||
],
|
],
|
||||||
[isRemoving],
|
[isRemoving],
|
||||||
);
|
);
|
||||||
|
|
||||||
async function handleCreateBridge(values: { name: string; interfaces?: string[] }) {
|
async function handleCreateBridge(values: { name: string; interfaces?: string[] }) {
|
||||||
const ifaceList = values.interfaces ?? [];
|
const ifaceList = values.interfaces ?? [];
|
||||||
|
createBridge({ name: values.name, interfaces: ifaceList })
|
||||||
try {
|
.then(() => {
|
||||||
await createBridge({ name: values.name, interfaces: ifaceList });
|
message.success(`Bridge ${values.name} created`);
|
||||||
message.success(`Bridge ${values.name} created`);
|
setBridgeModalVisible(false);
|
||||||
setBridgeModalVisible(false);
|
bridgeForm.resetFields();
|
||||||
bridgeForm.resetFields();
|
})
|
||||||
} catch (err) {
|
.catch((err) => {
|
||||||
console.error(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) {
|
function handleRemoveBridge(name: string) {
|
||||||
try {
|
removeBridge({ name })
|
||||||
await removeBridge({ name });
|
.then(() => {
|
||||||
message.success(`Bridge ${name} removed`);
|
message.success(`Bridge ${name} removed`);
|
||||||
} catch (err) {
|
})
|
||||||
console.error(err);
|
.catch((err) => {
|
||||||
message.error((err as Error)?.message ?? 'Failed to remove bridge');
|
console.error(err);
|
||||||
}
|
message.error((err as Error).message ?? 'Failed to remove bridge');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ padding: 16 }}>
|
<div style={{ padding: 16 }}>
|
||||||
<Row justify="space-between" align="middle" style={{ marginBottom: 12 }}>
|
<Row justify="space-between" align="middle" style={{ marginBottom: 12 }}>
|
||||||
<Col>
|
<Col>
|
||||||
<Title level={4}>Bridges & Interfaces</Title>
|
<Title level={2}> Network Management</Title>
|
||||||
<Paragraph type="secondary">View system interfaces and manage network bridges.</Paragraph>
|
<Paragraph type="secondary">View system interfaces and manage network bridges.</Paragraph>
|
||||||
</Col>
|
</Col>
|
||||||
<Col>
|
<Col>
|
||||||
@@ -46,6 +46,10 @@ body {
|
|||||||
li span {
|
li span {
|
||||||
font-size: @font-size-slider;
|
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 */
|
/* Inner layout (app-main) is a column flex that occupies the remaining width */
|
||||||
@@ -79,6 +83,7 @@ body {
|
|||||||
/* this makes the main content scrollable */
|
/* this makes the main content scrollable */
|
||||||
background: @component-background;
|
background: @component-background;
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Footer: fixed at bottom of app-main */
|
/* Footer: fixed at bottom of app-main */
|
||||||
|
|||||||
Reference in New Issue
Block a user