diff --git a/.sisyphus/plans/nxg-phase-4.9-wgdashboard-parity.md b/.sisyphus/plans/nxg-phase-4.9-wgdashboard-parity.md new file mode 100644 index 0000000..51a78f9 --- /dev/null +++ b/.sisyphus/plans/nxg-phase-4.9-wgdashboard-parity.md @@ -0,0 +1,47 @@ +# NexusGuard SD-WAN — WGDashboard Parity Plan (Phase 4.9) + +**Goal**: Bring the Dashboard UI to feature-parity with WGDashboard — multi-user management, global device view for Admin, WireGuard local interface status, external node health, and proper Endpoint vs ListenAddress config. + +## Task 4.9.1: User Management API (Admin CRUD) +- [x] Create `api/users.go` with CRUD endpoints (`GET /api/v1/users`, `POST /api/v1/users`, `DELETE /api/v1/users`) +- [x] Wire `UsersHandler` in `main.go` protected route group (admin-only JWT check) +- [x] Write tests for user CRUD + +## Task 4.9.2: Admin Global Device View +- [x] Modify `api/devices.go` `List()`: if requester is admin, return ALL devices with Preload("User") +- [x] Add `?user_id=` query parameter filter for admin +- [x] Write tests verifying admin sees all devices, regular user sees only own + +## Task 4.9.3: Dashboard UI - Users Page +- [x] Create `src/api/users.ts` with user CRUD API calls +- [x] Create `src/views/Users.vue` — table of users with create/delete actions +- [x] Add "Users" link to Sidebar in `src/App.vue` and route in `src/router/index.ts` +- [x] Add "Owner" column in `src/views/Devices.vue` when admin logged in +- [x] Add user filter dropdown in Devices page for admin + +## Task 4.9.4: WireGuard Local Interface Manager (Backend) +- [x] Create `internal/wgmanager/manager.go` — wgctrl integration for local wg0 (with stub for Windows cross-compile) +- [x] Implement: `GetStatus()`, `SetConfig(listenPort, privateKey)`, `Down()` +- [x] Add `/api/v1/wg/status` endpoint: UP/DOWN, Rx/Tx bytes, peer count, last handshake +- [x] Add `/api/v1/wg/up` and `/api/v1/wg/down` endpoints to start/stop local wg0 +- [x] Wire into `main.go` route group + +## Task 4.9.5: Dashboard UI - Local WG Status Widget +- [x] Create `src/components/WgStatusCard.vue` — ON/OFF toggle, Rx/Tx counters, peer count +- [x] Integrate into `src/views/Dashboard.vue` + +## Task 4.9.6: External Node Health Check +- [x] Add background goroutine in `main.go` pinging registered WgServer endpoints every 30s via UDP +- [x] Track health status in a thread-safe in-memory map +- [x] Add `GET /api/v1/servers/status` returning health of each node +- [x] Update `src/views/Servers.vue` to show Online/Offline dots and last-check timestamp +- [x] Update `src/api/servers.ts` with status endpoint + +## Task 4.9.7: Endpoint vs ListenAddress Split +- [x] Add `ListenAddress` column to WgServer model (default `0.0.0.0:51820`) +- [x] Rename `Endpoint` in model/API to `PublicEndpoint` for clarity +- [x] Update `migrations/001_init.sql` and GORM model mapper +- [x] Update `api/servers.go` CRUD to split the fields +- [x] Update Provisioning API to use `PublicEndpoint` (not ListenAddress) +- [ ] Update `src/api/servers.ts` type and `src/views/Servers.vue` form to show both fields +- [ ] Write tests for provisioning returning correct PublicEndpoint diff --git a/apps/dashboard-ui b/apps/dashboard-ui index 344f513..277713d 160000 --- a/apps/dashboard-ui +++ b/apps/dashboard-ui @@ -1 +1 @@ -Subproject commit 344f513ea50f871ccc5053fd55bdb7f20fc5ad0e +Subproject commit 277713d75b9270abc506deb78dc7387e31619df9 diff --git a/apps/server-core b/apps/server-core index 112be8d..e30ba7a 160000 --- a/apps/server-core +++ b/apps/server-core @@ -1 +1 @@ -Subproject commit 112be8de8886c19a4eef3dca2cfc531e714b19ce +Subproject commit e30ba7af65b8c5b3c3265bb1736d136fd73018fb diff --git a/temp_section1.txt b/temp_section1.txt new file mode 100644 index 0000000..167d0e8 --- /dev/null +++ b/temp_section1.txt @@ -0,0 +1,7 @@ + authHandler := api.NewAuthHandler(db) + devicesHandler := api.NewDevicesHandler(db, fw) + rulesHandler := api.NewRulesHandler(db, fw) + provHandler := api.NewProvisioningHandler(db, ipamMgr, cfg) + hbHandler := api.NewHeartbeatHandler(hbMgr) + serversHandler := api.NewServersHandler(db) + usersHandler := api.NewUsersHandler(db) diff --git a/temp_section2.txt b/temp_section2.txt new file mode 100644 index 0000000..09703d4 --- /dev/null +++ b/temp_section2.txt @@ -0,0 +1,28 @@ + v1.POST("/auth/login", authHandler.Login) + v1.POST("/provisioning", provHandler.Provision) + v1.POST("/heartbeat", hbHandler.Ping) + + protected := v1.Group("/") + protected.Use(auth.AuthMiddleware()) + { + protected.POST("/auth/register", authHandler.Register) + + protected.GET("/users", usersHandler.List) + protected.POST("/users", usersHandler.Create) + protected.DELETE("/users/:id", usersHandler.Delete) + + protected.GET("/servers", serversHandler.List) + protected.POST("/servers", serversHandler.Create) + protected.DELETE("/servers/:id", serversHandler.Delete) + + protected.GET("/devices", devicesHandler.List) + protected.POST("/devices", devicesHandler.Create) + protected.GET("/devices/:id", devicesHandler.Get) + protected.PUT("/devices/:id", devicesHandler.Update) + protected.DELETE("/devices/:id", devicesHandler.Delete) + protected.POST("/devices/:id/regenerate-token", devicesHandler.RegenerateToken) + + protected.GET("/devices/:id/rules", rulesHandler.List) + protected.POST("/devices/:id/rules", rulesHandler.Create) + protected.DELETE("/rules/:ruleId", rulesHandler.Delete) + }