feat: add Syslog configuration to Network Services (v0.10.8 Beta)
New Syslog page under Network Services exposes RouterOS logging actions and routing rules with full CRUD. Logging actions define destinations (remote syslog server, memory, disk, echo); logging rules define which topics route to which action. Single Device and All Devices modes are functionally identical — All Devices applies changes across every online device in parallel and shows per-entry coverage badges indicating which devices have each action or rule. Toggle enable/disable works in both modes. Remote actions display address:port with facility/severity in a single Destination column; memory/disk/echo rows show no irrelevant remote fields. Syslog KPI card added to the Network Services overview.
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
A self-hosted, full-stack network management platform for MikroTik devices. Monitor, configure, and manage your entire MikroTik infrastructure — routers, switches, and wireless access points — from a single web interface.
|
A self-hosted, full-stack network management platform for MikroTik devices. Monitor, configure, and manage your entire MikroTik infrastructure — routers, switches, and wireless access points — from a single web interface.
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
@@ -139,6 +139,7 @@ Each service supports multi-device management with conflict detection:
|
|||||||
| **DNS** | Upstream servers, static records (A/AAAA/CNAME/MX/NS/PTR/TXT/SRV), cache flush, DoH |
|
| **DNS** | Upstream servers, static records (A/AAAA/CNAME/MX/NS/PTR/TXT/SRV), cache flush, DoH |
|
||||||
| **NTP** | Server (broadcast/manycast), client (unicast/multicast), sync status |
|
| **NTP** | Server (broadcast/manycast), client (unicast/multicast), sync status |
|
||||||
| **WireGuard** | Interface management, peer configuration, public key display, RX/TX stats |
|
| **WireGuard** | Interface management, peer configuration, public key display, RX/TX stats |
|
||||||
|
| **Syslog** | Logging actions (remote/memory/disk/echo) and routing rules; single-device or push-to-all with per-entry coverage; enable/disable rules |
|
||||||
|
|
||||||
### Network Topology
|
### Network Topology
|
||||||
- Auto-discovered network map using LLDP, CDP, and MNDP neighbor data
|
- Auto-discovered network map using LLDP, CDP, and MNDP neighbor data
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "mikrotik-manager-backend",
|
"name": "mikrotik-manager-backend",
|
||||||
"version": "0.10.7-beta",
|
"version": "0.10.8-beta",
|
||||||
"description": "Mikrotik Network Management Platform - Backend API",
|
"description": "Mikrotik Network Management Platform - Backend API",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -44,19 +44,21 @@ router.get('/overview', async (_req: Request, res: Response) => {
|
|||||||
const collector = new DeviceCollector(device);
|
const collector = new DeviceCollector(device);
|
||||||
try {
|
try {
|
||||||
await collector.connect();
|
await collector.connect();
|
||||||
const [dhcpV4, dhcpV6, dns, ntp, wg] = await Promise.allSettled([
|
const [dhcpV4, dhcpV6, dns, ntp, wg, syslogActions] = await Promise.allSettled([
|
||||||
collector.getDhcpServers('ipv4'),
|
collector.getDhcpServers('ipv4'),
|
||||||
collector.getDhcpServers('ipv6'),
|
collector.getDhcpServers('ipv6'),
|
||||||
collector.getDnsSettings(),
|
collector.getDnsSettings(),
|
||||||
collector.getNtpSettings(),
|
collector.getNtpSettings(),
|
||||||
collector.getWireGuardInterfaces(),
|
collector.getWireGuardInterfaces(),
|
||||||
|
collector.getSyslogActions(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const v4Servers = dhcpV4.status === 'fulfilled' ? dhcpV4.value : [];
|
const v4Servers = dhcpV4.status === 'fulfilled' ? dhcpV4.value : [];
|
||||||
const v6Servers = dhcpV6.status === 'fulfilled' ? dhcpV6.value : [];
|
const v6Servers = dhcpV6.status === 'fulfilled' ? dhcpV6.value : [];
|
||||||
const dnsRow = dns.status === 'fulfilled' ? dns.value : null;
|
const dnsRow = dns.status === 'fulfilled' ? dns.value : null;
|
||||||
const ntpRow = ntp.status === 'fulfilled' ? ntp.value : null;
|
const ntpRow = ntp.status === 'fulfilled' ? ntp.value : null;
|
||||||
const wgIfaces = wg.status === 'fulfilled' ? wg.value : [];
|
const wgIfaces = wg.status === 'fulfilled' ? wg.value : [];
|
||||||
|
const syslogActs = syslogActions.status === 'fulfilled' ? syslogActions.value : [];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: device.id, name: device.name, ip_address: device.ip_address,
|
id: device.id, name: device.name, ip_address: device.ip_address,
|
||||||
@@ -65,10 +67,11 @@ router.get('/overview', async (_req: Request, res: Response) => {
|
|||||||
dns: dnsRow ? { allow_remote: dnsRow['allow-remote-requests'] === 'yes', servers: dnsRow['servers'] || '' } : null,
|
dns: dnsRow ? { allow_remote: dnsRow['allow-remote-requests'] === 'yes', servers: dnsRow['servers'] || '' } : null,
|
||||||
ntp: ntpRow ? { server_enabled: ntpRow.server['enabled'] === 'yes', client_enabled: ntpRow.client['enabled'] === 'yes' } : null,
|
ntp: ntpRow ? { server_enabled: ntpRow.server['enabled'] === 'yes', client_enabled: ntpRow.client['enabled'] === 'yes' } : null,
|
||||||
wireguard: { total: wgIfaces.length, running: wgIfaces.filter(i => i['running'] === 'true').length },
|
wireguard: { total: wgIfaces.length, running: wgIfaces.filter(i => i['running'] === 'true').length },
|
||||||
|
syslog: { remote_count: syslogActs.filter(a => a['type'] === 'remote').length },
|
||||||
};
|
};
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return { id: device.id, name: device.name, ip_address: device.ip_address,
|
return { id: device.id, name: device.name, ip_address: device.ip_address,
|
||||||
dhcp_v4: null, dhcp_v6: null, dns: null, ntp: null, wireguard: null,
|
dhcp_v4: null, dhcp_v6: null, dns: null, ntp: null, wireguard: null, syslog: null,
|
||||||
error: (err as Error).message };
|
error: (err as Error).message };
|
||||||
} finally {
|
} finally {
|
||||||
collector.disconnect();
|
collector.disconnect();
|
||||||
@@ -398,4 +401,91 @@ router.delete('/wireguard/peer/:id', requireWrite, async (req: Request, res: Res
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ─── Syslog ───────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
// GET /api/network-services/syslog?deviceId=X
|
||||||
|
router.get('/syslog', async (req: Request, res: Response) => {
|
||||||
|
const deviceId = deviceIdParam(req, res); if (!deviceId) return;
|
||||||
|
await withDevice(deviceId, res, async (collector) => {
|
||||||
|
const [actions, rules] = await Promise.allSettled([
|
||||||
|
collector.getSyslogActions(),
|
||||||
|
collector.getSyslogRules(),
|
||||||
|
]);
|
||||||
|
res.json({
|
||||||
|
actions: actions.status === 'fulfilled' ? actions.value : [],
|
||||||
|
rules: rules.status === 'fulfilled' ? rules.value : [],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// POST /api/network-services/syslog/action?deviceId=X
|
||||||
|
router.post('/syslog/action', requireWrite, async (req: Request, res: Response) => {
|
||||||
|
const deviceId = deviceIdParam(req, res); if (!deviceId) return;
|
||||||
|
await withDevice(deviceId, res, async (collector) => {
|
||||||
|
await collector.addSyslogAction(req.body);
|
||||||
|
const updated = await collector.getSyslogActions();
|
||||||
|
res.json(updated);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// PUT /api/network-services/syslog/action/:id?deviceId=X
|
||||||
|
router.put('/syslog/action/:id', requireWrite, async (req: Request, res: Response) => {
|
||||||
|
const deviceId = deviceIdParam(req, res); if (!deviceId) return;
|
||||||
|
await withDevice(deviceId, res, async (collector) => {
|
||||||
|
await collector.updateSyslogAction(req.params.id, req.body);
|
||||||
|
const updated = await collector.getSyslogActions();
|
||||||
|
res.json(updated);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// DELETE /api/network-services/syslog/action/:id?deviceId=X
|
||||||
|
router.delete('/syslog/action/:id', requireWrite, async (req: Request, res: Response) => {
|
||||||
|
const deviceId = deviceIdParam(req, res); if (!deviceId) return;
|
||||||
|
await withDevice(deviceId, res, async (collector) => {
|
||||||
|
await collector.removeSyslogAction(req.params.id);
|
||||||
|
res.json({ success: true });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// PUT /api/network-services/syslog/rule/toggle?deviceId=X — must be before /:id
|
||||||
|
router.put('/syslog/rule/toggle', requireWrite, async (req: Request, res: Response) => {
|
||||||
|
const deviceId = deviceIdParam(req, res); if (!deviceId) return;
|
||||||
|
const { ruleId, disabled } = req.body;
|
||||||
|
if (!ruleId) return res.status(400).json({ error: 'ruleId is required' });
|
||||||
|
if (typeof disabled !== 'boolean') return res.status(400).json({ error: 'disabled (boolean) is required' });
|
||||||
|
await withDevice(deviceId, res, async (collector) => {
|
||||||
|
await collector.toggleSyslogRule(ruleId, disabled);
|
||||||
|
res.json({ success: true });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// POST /api/network-services/syslog/rule?deviceId=X
|
||||||
|
router.post('/syslog/rule', requireWrite, async (req: Request, res: Response) => {
|
||||||
|
const deviceId = deviceIdParam(req, res); if (!deviceId) return;
|
||||||
|
await withDevice(deviceId, res, async (collector) => {
|
||||||
|
await collector.addSyslogRule(req.body);
|
||||||
|
const updated = await collector.getSyslogRules();
|
||||||
|
res.json(updated);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// PUT /api/network-services/syslog/rule/:id?deviceId=X
|
||||||
|
router.put('/syslog/rule/:id', requireWrite, async (req: Request, res: Response) => {
|
||||||
|
const deviceId = deviceIdParam(req, res); if (!deviceId) return;
|
||||||
|
await withDevice(deviceId, res, async (collector) => {
|
||||||
|
await collector.updateSyslogRule(req.params.id, req.body);
|
||||||
|
const updated = await collector.getSyslogRules();
|
||||||
|
res.json(updated);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// DELETE /api/network-services/syslog/rule/:id?deviceId=X
|
||||||
|
router.delete('/syslog/rule/:id', requireWrite, async (req: Request, res: Response) => {
|
||||||
|
const deviceId = deviceIdParam(req, res); if (!deviceId) return;
|
||||||
|
await withDevice(deviceId, res, async (collector) => {
|
||||||
|
await collector.removeSyslogRule(req.params.id);
|
||||||
|
res.json({ success: true });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|||||||
@@ -2476,6 +2476,44 @@ export class DeviceCollector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Syslog (logging actions + rules) ───────────────────────────────────────────
|
||||||
|
|
||||||
|
async getSyslogActions(): Promise<Record<string, string>[]> {
|
||||||
|
return this.client.execute('/system/logging/action/print', { detail: '' }).catch(() => [] as Record<string, string>[]);
|
||||||
|
}
|
||||||
|
|
||||||
|
async addSyslogAction(params: Record<string, string>): Promise<void> {
|
||||||
|
await this.client.execute('/system/logging/action/add', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateSyslogAction(id: string, params: Record<string, string>): Promise<void> {
|
||||||
|
await this.client.execute('/system/logging/action/set', { '.id': id, ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeSyslogAction(id: string): Promise<void> {
|
||||||
|
await this.client.execute('/system/logging/action/remove', { '.id': id });
|
||||||
|
}
|
||||||
|
|
||||||
|
async getSyslogRules(): Promise<Record<string, string>[]> {
|
||||||
|
return this.client.execute('/system/logging/print', { detail: '' }).catch(() => [] as Record<string, string>[]);
|
||||||
|
}
|
||||||
|
|
||||||
|
async addSyslogRule(params: Record<string, string>): Promise<void> {
|
||||||
|
await this.client.execute('/system/logging/add', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateSyslogRule(id: string, params: Record<string, string>): Promise<void> {
|
||||||
|
await this.client.execute('/system/logging/set', { '.id': id, ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeSyslogRule(id: string): Promise<void> {
|
||||||
|
await this.client.execute('/system/logging/remove', { '.id': id });
|
||||||
|
}
|
||||||
|
|
||||||
|
async toggleSyslogRule(id: string, disabled: boolean): Promise<void> {
|
||||||
|
await this.client.execute('/system/logging/set', { '.id': id, 'disabled': disabled ? 'yes' : 'no' });
|
||||||
|
}
|
||||||
|
|
||||||
// WireGuard ───────────────────────────────────────────────────────────────────
|
// WireGuard ───────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
async getWireGuardInterfaces(): Promise<Record<string, string>[]> {
|
async getWireGuardInterfaces(): Promise<Record<string, string>[]> {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "mikrotik-manager-frontend",
|
"name": "mikrotik-manager-frontend",
|
||||||
"version": "0.10.7-beta",
|
"version": "0.10.8-beta",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import NetworkServicesDHCPPage from './pages/NetworkServicesDHCPPage';
|
|||||||
import NetworkServicesDNSPage from './pages/NetworkServicesDNSPage';
|
import NetworkServicesDNSPage from './pages/NetworkServicesDNSPage';
|
||||||
import NetworkServicesNTPPage from './pages/NetworkServicesNTPPage';
|
import NetworkServicesNTPPage from './pages/NetworkServicesNTPPage';
|
||||||
import NetworkServicesWireGuardPage from './pages/NetworkServicesWireGuardPage';
|
import NetworkServicesWireGuardPage from './pages/NetworkServicesWireGuardPage';
|
||||||
|
import NetworkServicesSyslogPage from './pages/NetworkServicesSyslogPage';
|
||||||
|
|
||||||
function ProtectedRoute({ children }: { children: React.ReactNode }) {
|
function ProtectedRoute({ children }: { children: React.ReactNode }) {
|
||||||
const token = useAuthStore((s) => s.token);
|
const token = useAuthStore((s) => s.token);
|
||||||
@@ -65,6 +66,7 @@ export default function App() {
|
|||||||
<Route path="network-services/dns" element={<NetworkServicesDNSPage />} />
|
<Route path="network-services/dns" element={<NetworkServicesDNSPage />} />
|
||||||
<Route path="network-services/ntp" element={<NetworkServicesNTPPage />} />
|
<Route path="network-services/ntp" element={<NetworkServicesNTPPage />} />
|
||||||
<Route path="network-services/wireguard" element={<NetworkServicesWireGuardPage />} />
|
<Route path="network-services/wireguard" element={<NetworkServicesWireGuardPage />} />
|
||||||
|
<Route path="network-services/syslog" element={<NetworkServicesSyslogPage />} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="*" element={<Navigate to="/dashboard" replace />} />
|
<Route path="*" element={<Navigate to="/dashboard" replace />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { useQuery } from '@tanstack/react-query';
|
|||||||
import {
|
import {
|
||||||
LayoutDashboard, Router, Users, Bell, GitBranch, HardDrive,
|
LayoutDashboard, Router, Users, Bell, GitBranch, HardDrive,
|
||||||
Settings, Network, ChevronLeft, ChevronRight, Layers, ChevronDown, SlidersHorizontal, X, Wifi,
|
Settings, Network, ChevronLeft, ChevronRight, Layers, ChevronDown, SlidersHorizontal, X, Wifi,
|
||||||
Server, Globe, Clock, Shield,
|
Server, Globe, Clock, Shield, FileText,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { APP_VERSION } from '../../version';
|
import { APP_VERSION } from '../../version';
|
||||||
@@ -40,7 +40,8 @@ const networkServicesSubItems = [
|
|||||||
{ to: '/network-services/dhcp', icon: Server, label: 'DHCP' },
|
{ to: '/network-services/dhcp', icon: Server, label: 'DHCP' },
|
||||||
{ to: '/network-services/dns', icon: Globe, label: 'DNS' },
|
{ to: '/network-services/dns', icon: Globe, label: 'DNS' },
|
||||||
{ to: '/network-services/ntp', icon: Clock, label: 'NTP' },
|
{ to: '/network-services/ntp', icon: Clock, label: 'NTP' },
|
||||||
{ to: '/network-services/wireguard', icon: Shield, label: 'WireGuard' },
|
{ to: '/network-services/wireguard', icon: Shield, label: 'WireGuard' },
|
||||||
|
{ to: '/network-services/syslog', icon: FileText, label: 'Syslog' },
|
||||||
];
|
];
|
||||||
|
|
||||||
interface SidebarProps {
|
interface SidebarProps {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { useQuery } from '@tanstack/react-query';
|
|||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
Server, RefreshCw, CheckCircle, XCircle, MinusCircle,
|
Server, RefreshCw, CheckCircle, XCircle, MinusCircle,
|
||||||
Globe, Clock, Shield, Wifi,
|
Globe, Clock, Shield, Wifi, FileText,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { networkServicesApi } from '../services/api';
|
import { networkServicesApi } from '../services/api';
|
||||||
@@ -16,6 +16,7 @@ interface DeviceServiceRow {
|
|||||||
dns: { allow_remote: boolean; servers: string } | null;
|
dns: { allow_remote: boolean; servers: string } | null;
|
||||||
ntp: { server_enabled: boolean; client_enabled: boolean } | null;
|
ntp: { server_enabled: boolean; client_enabled: boolean } | null;
|
||||||
wireguard: { total: number; running: number } | null;
|
wireguard: { total: number; running: number } | null;
|
||||||
|
syslog: { remote_count: number } | null;
|
||||||
error?: string;
|
error?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,7 +59,8 @@ export default function NetworkServicesOverviewPage() {
|
|||||||
{ key: 'dhcp_v6', label: 'DHCP v6', icon: Server, href: '/network-services/dhcp' },
|
{ key: 'dhcp_v6', label: 'DHCP v6', icon: Server, href: '/network-services/dhcp' },
|
||||||
{ key: 'dns', label: 'DNS', icon: Globe, href: '/network-services/dns' },
|
{ key: 'dns', label: 'DNS', icon: Globe, href: '/network-services/dns' },
|
||||||
{ key: 'ntp', label: 'NTP', icon: Clock, href: '/network-services/ntp' },
|
{ key: 'ntp', label: 'NTP', icon: Clock, href: '/network-services/ntp' },
|
||||||
{ key: 'wireguard', label: 'WireGuard', icon: Shield, href: '/network-services/wireguard' },
|
{ key: 'wireguard', label: 'WireGuard', icon: Shield, href: '/network-services/wireguard' },
|
||||||
|
{ key: 'syslog', label: 'Syslog', icon: FileText, href: '/network-services/syslog' },
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
// KPI: for each service, how many devices have it active?
|
// KPI: for each service, how many devices have it active?
|
||||||
@@ -72,6 +74,7 @@ export default function NetworkServicesOverviewPage() {
|
|||||||
if (col.key === 'dns') return (svc as { allow_remote: boolean }).allow_remote;
|
if (col.key === 'dns') return (svc as { allow_remote: boolean }).allow_remote;
|
||||||
if (col.key === 'ntp') return (svc as { server_enabled: boolean }).server_enabled || (svc as { client_enabled: boolean }).client_enabled;
|
if (col.key === 'ntp') return (svc as { server_enabled: boolean }).server_enabled || (svc as { client_enabled: boolean }).client_enabled;
|
||||||
if (col.key === 'wireguard') return (svc as { running: number }).running > 0;
|
if (col.key === 'wireguard') return (svc as { running: number }).running > 0;
|
||||||
|
if (col.key === 'syslog') return (svc as { remote_count: number }).remote_count > 0;
|
||||||
return false;
|
return false;
|
||||||
}).length;
|
}).length;
|
||||||
return { ...col, active, total: data.length };
|
return { ...col, active, total: data.length };
|
||||||
@@ -101,6 +104,10 @@ export default function NetworkServicesOverviewPage() {
|
|||||||
if (s.total === 0) return <ServiceCell active={null} label="None" />;
|
if (s.total === 0) return <ServiceCell active={null} label="None" />;
|
||||||
return <ServiceCell active={s.running > 0} label={`${s.running}/${s.total} up`} />;
|
return <ServiceCell active={s.running > 0} label={`${s.running}/${s.total} up`} />;
|
||||||
}
|
}
|
||||||
|
if (key === 'syslog') {
|
||||||
|
const s = svc as { remote_count: number };
|
||||||
|
return <ServiceCell active={s.remote_count > 0} label={s.remote_count > 0 ? `${s.remote_count} remote` : 'No remote'} />;
|
||||||
|
}
|
||||||
return <ServiceCell active={null} />;
|
return <ServiceCell active={null} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -684,6 +684,24 @@ export const networkServicesApi = {
|
|||||||
}) =>
|
}) =>
|
||||||
api.put<{ server: NS; client: NS }>('/network-services/ntp', settings, { params: { deviceId } }),
|
api.put<{ server: NS; client: NS }>('/network-services/ntp', settings, { params: { deviceId } }),
|
||||||
|
|
||||||
|
// ── Syslog ────────────────────────────────────────────────────────────────
|
||||||
|
getSyslog: (deviceId: number) =>
|
||||||
|
api.get<{ actions: NS[]; rules: NS[] }>('/network-services/syslog', { params: { deviceId } }),
|
||||||
|
addSyslogAction: (deviceId: number, body: NS) =>
|
||||||
|
api.post<NS[]>('/network-services/syslog/action', body, { params: { deviceId } }),
|
||||||
|
updateSyslogAction: (deviceId: number, id: string, body: NS) =>
|
||||||
|
api.put<NS[]>(`/network-services/syslog/action/${encodeURIComponent(id)}`, body, { params: { deviceId } }),
|
||||||
|
deleteSyslogAction: (deviceId: number, id: string) =>
|
||||||
|
api.delete(`/network-services/syslog/action/${encodeURIComponent(id)}`, { params: { deviceId } }),
|
||||||
|
addSyslogRule: (deviceId: number, body: NS) =>
|
||||||
|
api.post<NS[]>('/network-services/syslog/rule', body, { params: { deviceId } }),
|
||||||
|
updateSyslogRule: (deviceId: number, id: string, body: NS) =>
|
||||||
|
api.put<NS[]>(`/network-services/syslog/rule/${encodeURIComponent(id)}`, body, { params: { deviceId } }),
|
||||||
|
deleteSyslogRule: (deviceId: number, id: string) =>
|
||||||
|
api.delete(`/network-services/syslog/rule/${encodeURIComponent(id)}`, { params: { deviceId } }),
|
||||||
|
toggleSyslogRule: (deviceId: number, ruleId: string, disabled: boolean) =>
|
||||||
|
api.put('/network-services/syslog/rule/toggle', { ruleId, disabled }, { params: { deviceId } }),
|
||||||
|
|
||||||
// ── WireGuard ─────────────────────────────────────────────────────────────
|
// ── WireGuard ─────────────────────────────────────────────────────────────
|
||||||
getWireGuard: (deviceId: number) =>
|
getWireGuard: (deviceId: number) =>
|
||||||
api.get<{ interfaces: NS[]; peers: NS[] }>('/network-services/wireguard', { params: { deviceId } }),
|
api.get<{ interfaces: NS[]; peers: NS[] }>('/network-services/wireguard', { params: { deviceId } }),
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
export const APP_VERSION = 'v0.10.7 Beta';
|
export const APP_VERSION = 'v0.10.8 Beta';
|
||||||
|
|||||||
Reference in New Issue
Block a user