Commit Graph
34 Commits
Author SHA1 Message Date
2GT-RichandClaude Sonnet 4.6 fc27f2a4a2 fix: resolve CodeQL security alerts (v0.11.10 Beta)
Log injection (3 errors fixed):
- errorHandler.ts: sanitize req.path before logging (strip \r\n)
- clients.ts: sanitize mac param in two Flux error log lines

Dismissed as false positives or won't-fix with justifications:
- Insecure Helmet config: CSP intentionally disabled (self-hosted,
  Vite/React inline styles require complex allowlist)
- Permissive CORS: wildcard only active when admin explicitly sets
  CORS_ORIGIN=*, documented as unsafe
- Network data to file (cert.ts x2): intentional cert upload feature
- File system race (oui.ts): trusted startup URL, no attack surface
- Insecure temp file (oui.ts): server-side OUI DB, not user-accessible
- Remote property injection (DeviceCollector.ts): data from trusted
  RouterOS API on admin-credentialed device, not user HTTP input
- Rate limiting (19): all routes behind JWT; login already rate-limited

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 15:55:11 -07:00
2GT-Rich 3eb85087f2 fix: SNMP status table no longer vanishes after back/return navigation (v0.11.9 Beta)
Root cause: backend filtered switches by status='online', so a mid-poll-cycle
device (briefly 'unknown') caused the endpoint to return [] — and the frontend
rendered nothing when the array was empty.

- Backend: query ALL switch devices (not just online ones); unreachable devices
  already fall through to the Promise.allSettled error path and render with an
  error badge, so no data is lost
- Frontend: replace silent `length > 0 &&` guard with a three-way ternary that
  shows a "No managed switches found" message when the array is empty, so the
  user always sees feedback instead of a blank section
- Frontend: add staleTime: 60_000 to the SNMP query to prevent the hair-trigger
  background refetch that raced with the poller on every navigation
2026-05-17 20:04:33 -07:00
2GT-Rich b1e4944823 feat: version update check — notify when newer version is on GitHub (v0.11.8 Beta)
Backend: GET /api/system/version-check fetches frontend/package.json from
the main branch on GitHub, caches result in app_settings for 24 hours,
and compares semver against the running version. Fails silently (no banner)
for air-gapped or firewalled deployments.

Frontend: dismissible blue update banner in SecurityBanners (rendered
between TopBar and main content) — only visible when a newer version is
detected. Shows target version and docker compose update command.
2026-05-17 19:38:25 -07:00
2GT-Rich bf62b9e10f docs: update README for v0.11.7 Beta — all new features documented
Added sections for: Device Tags, Availability Tracking, Network Topology
improvements (LLDP-authoritative, manual links, orphan detection), Device
Network Tools table (Ping/Traceroute/IP Scan/WoL/Packet Capture/Bandwidth
Test), Scheduled Backups, Maintenance Windows, Audit Log, Configuration
Templates, and TOTP 2FA. Alert delivery channels listed. Project structure
updated to note audit middleware. Version bumped to 0.11.7 Beta.
2026-05-16 16:56:42 -07:00
2GT-RichandClaude Sonnet 4.6 8d24b0b998 feat: bandwidth test auto-manages server on target managed device
Backend: accept target_device_id — connect to target, enable btest
server, run test from source using target credentials, disable server
in finally (even on failure). Manual IP mode preserved as fallback.

Frontend: device picker dropdown (all managed devices except self) as
default mode; toggle to Manual IP for non-managed targets with a warning
that the server must be pre-enabled. Results show target IP in labels.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 16:42:24 -07:00
2GT-RichandClaude Sonnet 4.6 64ab6ccf27 fix: show real capture error + step-by-step backend logging
- Frontend: blob error responses (from responseType:'blob' calls) are now
  decoded back to text/JSON so the actual backend error message surfaces
  in the UI instead of generic "Request failed with status code 500"
- Backend: step-by-step console.log through capture flow so the exact
  failure point is visible in server logs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 16:28:40 -07:00
2GT-RichandClaude Sonnet 4.6 63a7f4d785 feat: improve network topology — LLDP-authoritative, manual links, orphan handling (v0.11.6 Beta)
- Backend: 7-step pipeline — per-neighbor best-protocol dedup, bidirectional
  LLDP pair merging, LLDP-covered set suppression of noisy CDP/MNDP links,
  shared-segment heuristic limited to genuinely unresolved non-LLDP links
- Backend: manual_topology_links table + POST/DELETE endpoints for user-drawn connections
- Frontend: ManualEdge custom edge type (purple dashed + × delete button)
- Frontend: Connect Mode toggle — blue crosshair handles, onConnect creates DB-persisted link
- Frontend: orphan nodes in dedicated bottom row with orange warning banner
- Frontend: legend updated, canvas height increased

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 16:13:20 -07:00
2GT-RichandClaude Sonnet 4.6 624c9bc1cc fix: escape apostrophe in TOTP settings UI (lint)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 15:51:53 -07:00
2GT-RichandClaude Sonnet 4.6 64bb8b1b93 feat: packet capture, bandwidth test (v0.11.5 Beta)
- Packet capture: RouterOS sniffer + SFTP download to PCAP for Wireshark analysis
- Bandwidth test: RouterOS /tool/bandwidth-test with TX/RX Mbps result display
- Both exposed in the Tools tab on device detail pages

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 15:50:31 -07:00
2GT-RichandClaude Sonnet 4.6 a1ca4d0a44 feat: 2FA/TOTP, config templates (v0.11.4 Beta)
- 2FA: TOTP setup wizard in Settings, QR code enrollment, login second-step, password-confirmed disable
- Config templates: reusable DNS/NTP/syslog config sets pushed to selected devices via RouterOS API

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 15:44:23 -07:00
2GT-RichandClaude Sonnet 4.6 49ab1b002e feat: audit log, device tags, maintenance windows (v0.11.3 Beta)
- Audit log: middleware captures all write operations, paginated admin UI in Settings
- Device tags: full CRUD, device assignment, filter bar and chips on Devices page
- Maintenance windows: alert suppression during planned downtime, Settings management UI

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 15:33:15 -07:00
2GT-RichandClaude Sonnet 4.6 6a4ab1fc5f feat: scheduled backups, availability tracking, PoE power metrics (v0.11.2 Beta)
P1 — Scheduled Backups
- BackupService.createBackup() now accepts a `type` param (manual/scheduled)
- PollerService: cronMatchesNow() parser + runScheduledBackups() + Redis 1h gate
- Settings page: backup schedule toggle + cron expression input with examples

P2 — Uptime/Availability Tracking
- DB: device_availability table (went_offline_at, came_back_online_at, duration_seconds)
- PollerService: inserts outage rows on offline detection, closes them on recovery
- GET /api/metrics/device/:id/availability?range=30d endpoint
- /api/metrics/summary now includes fleet 30-day uptime %
- DeviceDetailPage: 30-day availability card (uptime %, outage count, longest outage)
- DashboardPage HealthBar: Uptime 30d mini-stat

P3 — PoE Power Draw Metrics
- DeviceCollector.collectPoePower() queries /interface/ethernet/poe/monitor for switches
- Writes poe_power measurement to InfluxDB (tags: device_id, port; fields: watts, current_ma, voltage_v)
- GET /api/metrics/device/:id/poe endpoint (current per-port + 1h history)
- SwitchPortDiagram: amber watt badge on PoE-active ports
- HardwareTab: PoE budget section showing active ports and total watts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 15:19:58 -07:00
2GT-RichandClaude Sonnet 4.6 e692a1d7cd fix: syslog coverage count exceeding device total + remove decorative live indicator (v0.11.1 Beta)
- aggregateRules/aggregateActions: deduplicate coverage per device using a per-device seenKeys set,
  preventing a device with duplicate topic|action rules from inflating the coverage count above total
- executePush (add path): skip devices that already have an identical rule/action before pushing to
  all devices, preventing duplicate RouterOS logging entries from being created in the first place
- TopBar: remove hardcoded decorative 'live' indicator that had no backend connection logic

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 16:39:07 -07:00
2GT-Rich 733d16a01f fix: resolve CI lint errors in CopyVlanModal
- Replace ternary-as-statement with if/else (no-unused-expressions)
- Escape apostrophe in JSX string (react/no-unescaped-entities)
- Wrap targetPorts in useMemo to stabilise useMemo deps
- Remove unused setPortDirect helper
2026-05-15 14:47:40 -07:00
2GT-Rich d3aa72c488 feat: Copy VLANs wizard + CPU display fix for hardware switches (v0.11.0 Beta)
Add 'Copy VLANs from Another Switch' — a 3-step modal available on the
VLANs tab of any switch device. Step 1 selects a source switch and which
VLAN IDs to copy (VLAN 1 deselected by default with a management risk
warning). Step 2 lets the user assign interfaces on the target device
per VLAN: clicking a port chip cycles it through None → Tagged → Untagged
→ None, with a live summary and bridge picker per VLAN. Conflict detection
identifies VLANs already present on the target and offers per-VLAN
Skip/Overwrite with the port grid disabling itself when Skip is active.
Step 3 shows the full plan before a single apply call executes all
operations over one device connection and reports per-VLAN results.

Also fix CPU load display on the Devices page for hardware switches that
report 0% CPU via ASIC offloading — the previous filter (v > 0) silently
dropped all valid zero readings; now filters on field presence instead.
2026-05-15 14:43:53 -07:00
2GT-RichandClaude Sonnet 4.6 17e989da99 fix: resolve all CI lint errors and security audit failures (v0.10.9 Beta)
- DevicesPage: add statusFilter + typeFilter to filtered useMemo deps (was a real bug — filter chips had no effect until unrelated re-render)
- DevicesPage: refactor cpuHistories useMemo to pure Object.fromEntries form
- DashboardPage: suppress react-hooks/purity for intentional Date.now() chart domain
- Sidebar: move GroupHeader from inline arrow fn to module-scope named function (fixes react-hooks/static-components)
- axios: bump ^1.6.5 → ^1.16.0 to clear high-severity CVEs (prototype pollution, CRLF injection, NO_PROXY bypass)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 16:58:23 -07:00
2GT-Rich 3a7ac1d023 Minor UI Rework + Introduction of Operations view. 2026-05-11 16:46:36 -07:00
2GT-RichandClaude Sonnet 4.6 176c3d85ec fix: escape unescaped quotes in SyslogPage JSX (lint error)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 08:45:17 -07:00
2GT-Rich e5f630ef2f 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.
2026-04-24 15:40:20 -07:00
2GT-Rich ff768b8217 fix: move ref sync to useEffect to satisfy react-hooks/refs lint rule 2026-04-23 16:23:48 -07:00
2GT-Rich ac39598257 feat: add configurable refresh interval to Clients page (closes #32)
Add a right-justified refresh interval dropdown above the client table
with options for Not Updating, 30 s, 1 min, and 3 min. The chosen
interval controls both React Query polling and WebSocket-triggered
invalidations — selecting Not Updating fully freezes the list until
page reload. Preference persists via localStorage.
2026-04-23 16:20:22 -07:00
2GT-Rich 68d88d0306 feat: RouterBOOT upgrade support, update UI overhaul, discovery fixes (v0.10.6 Beta)
RouterOS vs RouterBOOT distinction:
- Rename "Firmware Updates" → "RouterOS Updates" everywhere (Config tab,
  dashboard banner, devices list tooltip) to accurately reflect what is
  being updated
- Add RouterBOOT bootloader upgrade support as a distinct operation:
  new DeviceCollector methods checkRouterboardUpgrade() and
  installRouterboardUpgrade() using /system/routerboard/print and
  /system/routerboard/upgrade
- New API endpoints: POST /api/devices/:id/check-routerboard and
  POST /api/devices/:id/install-routerboard
- Daily poller now also checks RouterBOOT upgrade status while already
  connected during firmware check cycle, with first-discovery alerts

Config tab update panel:
- Combine RouterOS and RouterBOOT into a single "Software & Firmware
  Updates" card with two subsections separated by a divider
- Pre-populate both sections from stored DB state on load — no longer
  requires a manual "Check" click to see a known pending update
- Enforce update order: when both are pending, show a blue info banner
  explaining RouterOS must be installed first, and disable the
  RouterBOOT "Upgrade & Reboot" button until the RouterOS update clears
- Fix GET /api/devices/:id to include firmware_update_available,
  latest_ros_version, routerboard_upgrade_available, upgrade_firmware_version
  (these were missing from the detail endpoint)

Dashboard & devices list:
- Combined amber banner now shows two distinct rows: RouterOS (N) with
  device links and RouterBOOT (N) with device links — one dismiss for both
- Devices list: amber ArrowUpCircle = RouterOS update, blue Cpu icon =
  RouterBOOT upgrade, each with distinct tooltips
- Fix GET /api/devices (list) to include routerboard_upgrade_available and
  upgrade_firmware_version (these were missing, causing RouterBOOT
  notifications to never appear on the dashboard)

DB migration:
- Add routerboard_upgrade_available BOOLEAN DEFAULT FALSE
- Add upgrade_firmware_version VARCHAR(20)

Discovery system fixes:
- Fix duplicate entries: replace single-key (mac || ip || identity) dedup
  with three cross-referenced indexes so a neighbor seen via LLDP by one
  device and via MNDP by another always merges into one row
- Fix offline devices persisting: handleDeviceFailure now deletes
  topology_links for the offline device (same pattern as clients)
- Add periodic stale-link sweep every 15 min that removes topology_links
  rows older than 20 min as a safety net for devices that go offline
  without cleanly triggering handleDeviceFailure
2026-04-23 13:46:20 -07:00
2GT-Rich 138d7a935b chore: bump version to v0.10.5 Beta
Follows security fixes from #33: trust proxy, login rate limiting,
and default secret banners.
2026-04-22 09:31:01 -07:00
a336b0e207 security: trust proxy fix, login rate limiting, default secret banners (#33)
* fix(security): default-secret banners, login rate limiting, real-IP passthrough

- app.set('trust proxy', 1) so req.ip resolves to real client IP behind nginx
- loginRateLimit() middleware on POST /api/auth/login; limits read from
  app_settings (login_rate_limit_max / login_rate_limit_window_sec) with
  60-second cache; falls back to 10/60s when Redis/DB unavailable
- GET /api/auth/security-status returns warnings for default JWT_SECRET,
  ENCRYPTION_KEY, and admin/admin credential — non-breaking for dev
- SecurityBanners component in AppLayout: yellow banner for default secrets,
  red banner + change-password link for default admin password
- Login rate limit settings exposed in Settings > General tab

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(security): rate-limit /security-status, suppress CodeQL false positive on /login

- Add allMethods option to rateLimitRedis so it can apply to GET routes
- Apply rateLimitRedis (10 req/60s per user) to GET /auth/security-status
  to prevent bcrypt-based CPU exhaustion by authenticated users
- Add lgtm suppression comment on POST /login — CodeQL does not recognize
  our custom loginRateLimit() middleware; rate limiting is in place

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Denis Evers <git@evers.sh>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 09:30:23 -07:00
2GT-RichandClaude Sonnet 4.6 0350a76232 chore: bump version to v0.10.4 Beta and update docs for PR #31 features
Documents CORS_ORIGIN env var, credential preset operator access control,
and server-side bulk device add job in README. Bumps version across all
four locations.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 19:57:47 -07:00
DenisandCursor Agent a12f52ebc7 feat: non-blocking bulk-add, CORS hardening, credential preset access control (#31)
* devices: hide discovered neighbors that are already managed

Discovered Devices showed every unresolved MikroTik neighbor, even when
the neighbor was obviously the same device a managed node is reporting
through a different link / MAC / alias. Since we already know every
managed device interface MAC, primary IP, and system identity, we can
flag those rows.

- /api/devices/discovered now annotates each row with duplicate_of_device_id
  and duplicate_of_device_name when the neighbor matches a managed device
  by interface MAC, primary IP, or system identity.
- Add a Hide duplicates of managed devices toggle (default on) above the
  table, with a count of how many matches were found. When enabled, the
  list hides matched rows; when disabled, matches stay visible but are
  dimmed and tagged Already managed so the origin is clear.
- Suppress the Add to Manager action for rows that are already a
  managed device, since adding them would create a second record for the
  same hardware.

Co-authored-by: Denis <denis-ev@users.noreply.github.com>

* devices: safer duplicate handling, try-all safeguards, and timezone/firmware fixes

Co-authored-by: Denis <denis-ev@users.noreply.github.com>

* Non-blocking follow-ups: parsePort util, tests, bulk-add queue, preset rate limit

Co-authored-by: Denis <denis-ev@users.noreply.github.com>

* fix(security): tighten CORS, Redis rate limits, and preset access controls

Require explicit CORS origins in production, align Socket.IO CORS with HTTP,
default Docker Compose to local HTTPS origins, and remove the in-memory
mutation limiter in favor of Redis. Also add operator-use flags for credential
presets in the UI and harden bulk-add job access and client error messages.

Co-authored-by: Denis <denis-ev@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-04-21 19:54:35 -07:00
Denis 30bf691596 feature(devices,topology,tz): devices bulk-add, duplicate safety, and firmware/timezone fixes
Squash-merge of PR #28 from @denis-ev. All three review blockers addressed: encryption key management documented, string truncation replaced with skip+warn validation, and undefined return on duplicate merge fixed with proper 500. Local test stack verified against cloned prod DB — migrations, new features, and backend all clean.
2026-04-21 16:52:04 -07:00
2GT-RichandClaude Sonnet 4.6 c203011494 Fix all ESLint errors so CI passes on every branch
- Move nested components outside parent functions (GraphsTab, ClientDetailPage, NetworkServicesDHCPPage)
- Add eslint-disable comments for intentional setState-in-effect patterns (form sync in DNS, NTP, Wireless pages)
- Fix unescaped quote entities in JSX (SystemConfigTab, GlobalSearch, TopologyPage)
- Fix empty catch block in main.tsx
- Convert ternary-as-statement to if/else in RadiosTab
- Move Date.now() calls into useMemo in DashboardPage (react-hooks/purity)
- Fix DOM mutation in event handler in TerminalModal (react-hooks/immutability)
- Wrap ref assignment in useLayoutEffect in useSocket (react-hooks/refs)
- Update TypeScript devDependency version pin to match installed version

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 10:20:03 -07:00
2GT-Rich ab9572122e missed point release update. 2026-04-15 09:33:03 -07:00
2GT-Rich 0a1ae97523 Fixed NTP & DNS server activation bug. 2026-04-15 08:48:18 -07:00
2GT-RichandClaude Sonnet 4.6 d992a8b5a6 Align Wireless Clients zebra striping with app-wide table-zebra pattern
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 08:43:03 -07:00
2GT-Rich 9ce62c954b updated version. 2026-04-14 08:40:24 -07:00
2GT-Rich ca414c528e Zebra table change to Wireless > Clients table 2026-04-14 08:35:32 -07:00
Rich Teslow 6b0fc9fdf1 Initial commit baby! 2026-04-13 18:15:41 -07:00