# Fix Peers API 500 + Add Delete Device to Devices List ## TL;DR > **Quick Summary**: Fix `HWID` unique constraint violation causing 500 on POST `/api/v1/peers` by making `HWID` nullable; add delete button to Devices.vue list view > > **Deliverables**: > - Backend: HWID model change `string` → `*string`, fix CreatePeer/CreateDevice > - Frontend: Delete button in Devices.vue table rows > - Migration: SQL to convert existing empty HWIDs to NULL + container restart > > **Estimated Effort**: Short > **Parallel Execution**: YES ## Context ### Root Cause - `Device.HWID` is `string` with `uniqueIndex`. `CreatePeer` sets `HWID: ""` which collides with other devices having empty HWID → SQLSTATE 23505 - Fix: make HWID `*string` (nullable), PostgreSQL treats NULLs as distinct in unique constraints ### Delete Missing - Backend: `DELETE /devices/:id` exists at `main.go:176`, `devicesHandler.Delete` in `devices.go:235` - API: `deleteDevice(id)` exists in `devices.ts:50` - DeviceDetail: has delete button with confirm dialog - Devices.vue: Actions column only has View, Config, Firewall — no Delete ## TODOs - [ ] 1. Backend: Change HWID to `*string` in Device model **What to do**: - Edit `apps/server-core/internal/models/models.go` line 60: - Change `HWID string \`gorm:"column:hwid;uniqueIndex;size:64"\`` - To `HWID *string \`gorm:"column:hwid;uniqueIndex;size:64"\`` **Must NOT do**: - Don't change any other fields in the model **Recommended Agent Profile**: `unspecified-high` (precise Go struct edit) **QA Scenarios**: ``` Scenario: Verify build after model change Tool: Bash Steps: 1. cd apps/server-core && go build ./... Expected Result: No compilation errors Evidence: .sisyphus/evidence/task-1-build.txt ``` - [ ] 2. Backend: Fix CreatePeer to not set empty HWID **What to do**: - Edit `apps/server-core/api/peers.go` — remove `HWID: ""` line (line 96) - Since `HWID` is now `*string`, the zero value (nil) is correct — it won't violate unique constraint - Keep `RegTokenHash: ""` as-is (no unique index on that) **QA Scenarios**: ``` Scenario: Build passes after fix Tool: Bash Steps: 1. cd apps/server-core && go build ./... Expected Result: Clean build Evidence: .sisyphus/evidence/task-2-build.txt ``` - [ ] 3. Backend: Fix CreateDevice to not set empty HWID **What to do**: - Edit `apps/server-core/api/devices.go` — verify `Create` function (line 84-89) doesn't set HWID - It currently doesn't set HWID at all, so with `*string` it will default to nil — correct - Verify `Delete` function (line 235-255) already works — it does, no changes needed - [ ] 4. Migration: Convert existing empty HWIDs to NULL on server **What to do**: - SSH into 172.20.8.191 and run SQL against the postgres container: ```bash docker exec nexus-guard-suite-postgres-1 psql -U nexusguard -d nexusguard -c "UPDATE devices SET hwid = NULL WHERE hwid = '';" ``` - Then restart server-core container: ```bash docker restart nexus-guard-suite-server-core-1 ``` **QA Scenarios**: ``` Scenario: Verify empty HWIDs are now NULL Tool: Bash Steps: 1. docker exec nexus-guard-suite-postgres-1 psql -U nexusguard -d nexusguard -c "SELECT count(*) FROM devices WHERE hwid = '';" Expected Result: count = 0 Evidence: .sisyphus/evidence/task-4-sql.txt Scenario: Verify POST /api/v1/peers succeeds Tool: Bash Steps: 1. Obtain auth token via POST /api/v1/auth/login with admin credentials 2. POST /api/v1/peers with valid body (name, wg_server_id) Expected Result: 201 Created (not 500) Evidence: .sisyphus/evidence/task-4-peers-api.txt ``` - [ ] 5. Frontend: Add delete button to Devices.vue list **What to do**: - Edit `apps/dashboard-ui/src/views/Devices.vue` - Add delete button in the Actions column (line 34-39), after Firewall button: ```html ``` - Import `deleteDevice` from `../api/devices` - Add `handleDelete` function: ```typescript const handleDelete = async (id: string, name: string) => { if (!confirm(`Delete device "${name}"? This will destroy its tunnel and all firewall rules.`)) return try { await deleteDevice(id) store.loadDevices() } catch (err: any) { alert(err.response?.data?.error || 'Failed to delete device') } } ``` **Must NOT do**: - Don't remove the existing Config/Firewall buttons **Recommended Agent Profile**: `visual-engineering` (Vue UI) **QA Scenarios**: ``` Scenario: Build passes Tool: Bash Steps: 1. cd apps/dashboard-ui && npx vue-tsc --noEmit Expected Result: No type errors Evidence: .sisyphus/evidence/task-5-tsc.txt ``` - [ ] 6. Commit and push changes **What to do**: - Commit server-core changes - Commit dashboard-ui changes - Push both submodules - Commit/push root submodule pointer ## Success Criteria - [ ] `go build ./...` passes - [ ] `vue-tsc --noEmit` passes - [ ] POST `/api/v1/peers` returns 201, not 500 - [ ] Devices.vue has a delete button per row that works - [ ] Existing empty HWIDs migrated to NULL