13 KiB
Bug Fix: Post-Deploy Issues (update.sh, Node CIDR, Config Visibility)
TL;DR
Quick Summary: Perbaiki 3 post-deployment bugs — update.sh tidak
downsebelum restart, label Interface Address membingungkan + backfill untuk node lama tanpa InterfaceAddress, dan config wg tidak muncul/tidak ada error feedback di PeerConfigModal.Deliverables:
- update.sh restart dengan
down+build+up- Label "Interface Address (CIDR)" + backfill InterfaceAddress untuk existing node
- PeerConfigModal loading state + error visibility + admin guard
Estimated Effort: Quick (4 tasks) Parallel Execution: YES — 2 waves Critical Path: Task 1-4 parallel → Task 5-6 independent
Context
Original Request
3 bugs user setelah deploy:
- update.sh:
docker compose build+up -dtanpadown— container tidak proper restart. Perubahan .env tidak teraplikasi. - Node WG tidak punya IP default: Label "Interface IP / Prefix" membingungkan (seharusnya CIDR). Node lama tanpa InterfaceAddress → IPAM bisa alokasikan IP server ke peer (IP collision risk).
- Config wg tidak muncul: PeerConfigModal tidak menunjukkan config. Error loading silent —
loadConfigcatch block tidak setconfigError.
Metis Review — Critical Findings
- IPAM collision risk (CRITICAL):
ipam/manager.go:92-94queryWHERE interface_address IS NOT NULL AND interface_address != ''. Jika InterfaceAddress kosong, server IP tidak dikecualikan → peer bisa dapat IP server. Backfill InterfaceAddress bukan hanya cosmetic. - Admin intentional:
GET /devices/:id/configsengaja admin-only. Frontend harus hidden untuk non-admin. - Silent error chain: 403 dari config endpoint → catch block di
loadConfigseteditableConfig = ''tapi tidak setconfigError→ user lihat textarea kosong tanpa error.
Work Objectives
Core Objective
Fix 3 post-deployment bugs yang menghalangi user menggunakan NexusGuard setelah update.
Concrete Deliverables
- update.sh: Restart logic —
downsebelumbuild && up -d - Servers.vue: Label "Interface Address (CIDR)"
- Migration: Backfill InterfaceAddress untuk existing node
- PeerConfigModal.vue: Loading state + error visibility
- Devices.vue + DeviceDetail.vue: Admin guard di Config button
Must Have
- update.sh:
docker compose downsebelum build - Label form: dari "Interface IP / Prefix" → "Interface Address (CIDR)"
- Semua existing node dengan
interface_address = ''ter-backfill setelah migration - PeerConfigModal tampilkan "Loading..." saat fetching config
- PeerConfigModal tampilkan error merah jika load gagal
- Non-admin tidak melihat Config button
Must NOT Have (Guardrails)
- JANGAN
docker compose down -v(jangan hapus volume) - JANGAN buka endpoint config/QR/share ke non-admin
- JANGAN refactor IPAM, migration framework, atau buat endpoint baru
- JANGAN ubah behavior WireGuard (config-in-memory tetap)
- JANGAN log plaintext keys
Verification Strategy
ZERO HUMAN INTERVENTION — ALL verification is agent-executed.
Test Decision
- Infrastructure exists: YES
- Automated tests: NO (bug fix, verification via QA scenarios)
- Framework: vitest (existing)
QA Policy
- update.sh: Simulasi dengan bash command
- Frontend: Playwright untuk cek label + button visibility
- Backend: Bash (curl) untuk endpoint verification
- DB: Bash (docker exec psql) untuk backfill verification
Execution Strategy
Parallel Execution Waves
Wave 1 (All parallel — independent fixes):
├── Task 1: Fix update.sh — down before build+up [quick]
├── Task 2: Fix form label — "Interface Address (CIDR)" [quick]
├── Task 3: Fix PeerConfigModal — loading + error visibility [quick]
└── Task 4: Add admin guard to Config button [quick]
Wave 2 (Backend backfill — depends on nothing):
├── Task 5: Create InterfaceAddress backfill migration [quick]
Wave FINAL:
├── F1: Plan Compliance + F2: Code Quality + F3: QA + F4: Scope
Dependency Matrix
- 1-4: None — all can run in parallel
- 5: None — independent
- F1-F4: All tasks complete
TODOs
-
1. Fix update.sh — add
docker compose downbefore build+upWhat to do:
D:\www-project\NexusGuard\update.sh:28-31- Tambah
docker compose downSEBELUMdocker compose build - Urutan baru:
down→build→up -d - Jangan tambah
-vflag (volume data harus aman)
Must NOT do:
- JANGAN tambah
-vatau--volumes - JANGAN hapus migration step
- JANGAN ubah Makefile atau docker-compose.yml
Recommended Agent Profile:
- Category:
quick - Skills: []
References:
D:\www-project\NexusGuard\update.sh:28-31— Current build+up linesD:\www-project\NexusGuard\Makefile:7—docker compose downpattern (safe, no -v)
Acceptance Criteria:
docker compose downruns BEFOREdocker compose build- Volume pgdata tetap ada setelah update
QA Scenarios:
Scenario: Verify down runs before up Tool: Bash Preconditions: update.sh exists Steps: 1. Read update.sh lines 28-32 2. Verify `docker compose down` appears before `docker compose build` 3. Verify NO `-v` flag on down Expected Result: Correct order with safe flags Evidence: .sisyphus/evidence/task-1-update-sh.txt -
2. Fix form label — "Interface Address (CIDR)"
What to do:
apps/dashboard-ui/src/views/Servers.vue:- Cari label "Interface IP / Prefix" di create form dan edit form
- Ganti ke "Interface Address (CIDR)"
- Jangan ubah placeholder (
10.172.20.1/24) — sudah benar
Must NOT do:
- JANGAN ubah logika parseIpInput atau validasi
- JANGAN ubah struktur form
Parallelization:
- Can Run In Parallel: YES
- Parallel Group: Wave 1 (with Tasks 1, 3, 4)
References:
apps/dashboard-ui/src/views/Servers.vue:49— Create form labelapps/dashboard-ui/src/views/Servers.vue:187— Edit form label
Acceptance Criteria:
- Create form label: "Interface Address (CIDR)"
- Edit form label: "Interface Address (CIDR)"
- Placeholder tetap "10.172.20.1/24"
-
3. Fix PeerConfigModal — loading state + error visibility
What to do:
apps/dashboard-ui/src/components/PeerConfigModal.vue:
A. Loading state:
- Tambah ref:
const configLoading = ref(false) - Di
loadConfig(): setconfigLoading = truesebelum API call,configLoading = falsesetelah - Di template, di atas textarea (atau di dalamnya):
<div v-if="configLoading" class="text-gray-400 text-sm py-4">Loading configuration...</div> <textarea v-else v-model="editableConfig" ...></textarea>
B. Error visibility (CRITICAL):
- Di
loadConfig()catch block (line 87-94):configError.value = err.response?.data?.error || err.message || 'Failed to load device configuration' - Pastikan
configErrorsudah di-reset ke''di awal loadConfig (sebelum try) configErrorsudah ada di template line 36:<p v-if="configError" class="text-xs text-red-400 mt-2">{{ configError }}</p>
C. Same fix for QR error:
loadQR()catch block juga — set configError atau qrError jika ada
Must NOT do:
- JANGAN refactor struktur modal atau tambah fitur baru
- JANGAN ubah endpoint backend
Parallelization:
- Can Run In Parallel: YES
- Parallel Group: Wave 1 (with Tasks 1, 2, 4)
References:
apps/dashboard-ui/src/components/PeerConfigModal.vue:85-95— loadConfig functionapps/dashboard-ui/src/components/PeerConfigModal.vue:27— textareaapps/dashboard-ui/src/components/PeerConfigModal.vue:36— configError template (already exists)apps/dashboard-ui/src/components/PeerConfigModal.vue:97-103— loadQR (same pattern)apps/dashboard-ui/src/components/PeerConfigModal.vue:155-163— saveConfig catch block (correct pattern to follow)
Acceptance Criteria:
- Textarea menampilkan "Loading configuration..." saat fetch
- Load gagal (403, network error) → textarea hilang, error merah muncul
- Load sukses → textarea muncul dengan config
- QR juga handle error dengan baik
-
4. Add admin guard to Config button
What to do:
-
apps/dashboard-ui/src/views/Devices.vue:39:- Ubah
v-if="device.InternalIP"→v-if="device.InternalIP && authStore.isAdmin" - Import/akses authStore:
const authStore = useAuthStore()(cek existing usage)
- Ubah
-
apps/dashboard-ui/src/views/DeviceDetail.vue:98:- Ubah
v-if="device.InternalIP"→v-if="device.InternalIP && authStore.isAdmin"
- Ubah
Must NOT do:
- JANGAN buka endpoint ke non-admin
- JANGAN buat fallback untuk non-admin (tidak usah tampilkan "login as admin")
Parallelization:
- Can Run In Parallel: YES
- Parallel Group: Wave 1 (with Tasks 1, 2, 3)
References:
apps/dashboard-ui/src/views/Devices.vue:39— Config buttonapps/dashboard-ui/src/views/DeviceDetail.vue:98— Config buttonapps/dashboard-ui/src/views/Devices.vue:1— Import pattern untuk store
Acceptance Criteria:
- Admin: Config button visible
- Non-admin: Config button hidden
-
-
5. Backfill InterfaceAddress untuk existing node
What to do:
-
Buat Go CLI flag
-backfill-interfacediapps/server-core/main.go:- Query semua server dengan
interface_address IS NULL OR interface_address = '' - Untuk setiap server, panggil
calcInterfaceAddress(server.IPPoolCIDR) - Update
interface_addressjikaIPPoolCIDRtidak kosong - Skip jika
IPPoolCIDRjuga kosong
- Query semua server dengan
-
Atau lebih simple: SQL migration file
- Tapi Go lebih aman karena reuse
calcInterfaceAddresslogic
- Tapi Go lebih aman karena reuse
-
Pattern: Ikuti existing CLI flag pattern (
-create-admin,-migrate-prod)main.go:44-60— CLI flag handling- Gunakan
db.Model(&models.WgServer{})seperti dicalcInterfaceAddress
Must NOT do:
- JANGAN buat migration framework baru
- JANGAN trigger IPAM re-allocation
- JANGAN ubah IP yang sudah valid — hanya isi yang kosong
Parallelization:
- Can Run In Parallel: YES (independent)
References:
apps/server-core/main.go:44-60— CLI flag patternapps/server-core/api/servers.go:114-129— calcInterfaceAddress functionapps/server-core/internal/ipam/manager.go:92-94— Query that exposes the bugapps/server-core/api/servers.go:162-171— Create handler auto-calc (pattern to follow)
Acceptance Criteria:
./server-core -backfill-interfaceexits 0- Semua row dengan
interface_address = ''terisi - Row dengan
IPPoolCIDR = ''tetap kosong (skip) - IPAM tidak bisa alokasikan server IP ke peer setelah backfill
QA Scenarios:
Scenario: Backfill empty InterfaceAddress Tool: Bash Preconditions: DB has server with IPPoolCIDR="10.0.0.0/24" and InterfaceAddress="" Steps: 1. docker compose run --rm server-core ./server-core -backfill-interface 2. docker exec db psql -c "SELECT interface_address FROM wg_servers WHERE interface_address IS NULL OR interface_address = ''" Expected Result: Query returns 0 rows Evidence: .sisyphus/evidence/task-5-backfill.txt Scenario: IPAM no longer allocates server IP Tool: Bash (curl) Preconditions: Server with IPPoolCIDR="10.0.0.0/24", InterfaceAddress="10.0.0.1/24" Steps: 1. curl -s -X POST /api/v1/peers -H "Authorization: Bearer $TOKEN" -d '{"name":"test","wg_server_id":"$ID"}' 2. Parse response, check InternalIP Expected Result: InternalIP != "10.0.0.1" Evidence: .sisyphus/evidence/task-5-ipam-fix.json -
Final Verification Wave
-
F1. Plan Compliance Audit —
oracleVerify: update.sh restructured, label fixed, backfill done, config error visible, admin guard in place -
F2. Code Quality Review —
unspecified-highRungo build ./...,npm run build, check for unused imports, console.log -
F3. Real Manual QA —
unspecified-highExecute all QA scenarios from all 5 tasks -
F4. Scope Fidelity Check —
deepMust Have checklist complete, Must NOT compliance
Commit Strategy
- 1:
fix(ops): add docker compose down before build+up in update.sh - 2:
fix(ui): rename Interface IP/Prefix label to Interface Address (CIDR) - 3:
fix(ui): add loading state and error visibility to PeerConfigModal - 4:
fix(ui): hide config button for non-admin users - 5:
fix(core): add backfill-interface CLI flag for existing nodes
Success Criteria
Final Checklist
- update.sh:
docker compose downruns before build+up - Label form: "Interface Address (CIDR)"
- Semua existing node dengan InterfaceAddress kosong terbackfill
- PeerConfigModal tampilkan loading + error
- Non-admin tidak lihat Config button
- IPAM tidak alokasikan server IP ke peer