From 5e9c27b7ef613adf55d077fe26144c91cb61cbec Mon Sep 17 00:00:00 2001 From: Fimeg Date: Fri, 31 Oct 2025 17:34:05 -0400 Subject: [PATCH] fix: handle NULL reboot_reason values from database The reboot_reason field was defined as string instead of *string, causing database scan failures when the column contains NULL values. This broke agent list loading on existing installations after migration. - Changed reboot_reason to *string in both Agent and AgentWithLastScan structs - Added DEFAULT empty string to migration for new installations - Added README section for full server reinstall procedure --- README.md | 35 +++++++++++++++++++ .../migrations/013_add_reboot_tracking.up.sql | 2 +- aggregator-server/internal/models/agent.go | 8 ++--- 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 238e336..7367d3d 100644 --- a/README.md +++ b/README.md @@ -109,6 +109,41 @@ Get registration tokens from the web dashboard under **Settings → Token Manage --- +### Full Reinstall (Nuclear Option) + +If things get really broken or you want to start completely fresh: + +```bash +docker-compose down -v --remove-orphans && \ + rm config/.env && \ + docker-compose build --no-cache && \ + cp config/.env.bootstrap.example config/.env && \ + docker-compose up -d +``` + +**What this does:** +- `down -v` - Stops containers and **wipes all data** (including the database) +- `--remove-orphans` - Cleans up leftover containers +- `rm config/.env` - Removes old server config +- `build --no-cache` - Rebuilds images from scratch +- `cp config/.env.bootstrap.example` - Resets to bootstrap mode for setup wizard +- `up -d` - Starts fresh in background + +**Warning:** This deletes everything - all agents, update history, configurations. You'll need to handle existing agents: + +**Option 1 - Re-register agents:** +- Remove agent config: `sudo rm /etc/aggregator/config.json` (Linux) or `C:\ProgramData\RedFlag\config.json` (Windows) +- Re-run the one-liner installer with new registration token +- Scripts handle override/update automatically (one agent per OS install) + +**Option 2 - Clean uninstall/reinstall:** +- Uninstall agent completely first +- Then run installer with new token + +The one-liner scripts should work for updates unless there are breaking changes between versions. + +--- + ## Key Features ✓ **Secure by Default** - Registration tokens, JWT auth, rate limiting diff --git a/aggregator-server/internal/database/migrations/013_add_reboot_tracking.up.sql b/aggregator-server/internal/database/migrations/013_add_reboot_tracking.up.sql index c624ec1..c5c48ea 100644 --- a/aggregator-server/internal/database/migrations/013_add_reboot_tracking.up.sql +++ b/aggregator-server/internal/database/migrations/013_add_reboot_tracking.up.sql @@ -2,7 +2,7 @@ ALTER TABLE agents ADD COLUMN reboot_required BOOLEAN DEFAULT FALSE, ADD COLUMN last_reboot_at TIMESTAMP, -ADD COLUMN reboot_reason TEXT; +ADD COLUMN reboot_reason TEXT DEFAULT ''; -- Add index for efficient querying of agents needing reboot CREATE INDEX idx_agents_reboot_required ON agents(reboot_required) WHERE reboot_required = TRUE; diff --git a/aggregator-server/internal/models/agent.go b/aggregator-server/internal/models/agent.go index 5d686cf..75dd62d 100644 --- a/aggregator-server/internal/models/agent.go +++ b/aggregator-server/internal/models/agent.go @@ -23,8 +23,8 @@ type Agent struct { Status string `json:"status" db:"status"` Metadata JSONB `json:"metadata" db:"metadata"` RebootRequired bool `json:"reboot_required" db:"reboot_required"` - LastRebootAt *time.Time `json:"last_reboot_at" db:"last_reboot_at"` - RebootReason string `json:"reboot_reason" db:"reboot_reason"` + LastRebootAt *time.Time `json:"last_reboot_at,omitempty" db:"last_reboot_at"` + RebootReason *string `json:"reboot_reason,omitempty" db:"reboot_reason"` CreatedAt time.Time `json:"created_at" db:"created_at"` UpdatedAt time.Time `json:"updated_at" db:"updated_at"` } @@ -44,8 +44,8 @@ type AgentWithLastScan struct { Status string `json:"status" db:"status"` Metadata JSONB `json:"metadata" db:"metadata"` RebootRequired bool `json:"reboot_required" db:"reboot_required"` - LastRebootAt *time.Time `json:"last_reboot_at" db:"last_reboot_at"` - RebootReason string `json:"reboot_reason" db:"reboot_reason"` + LastRebootAt *time.Time `json:"last_reboot_at,omitempty" db:"last_reboot_at"` + RebootReason *string `json:"reboot_reason,omitempty" db:"reboot_reason"` CreatedAt time.Time `json:"created_at" db:"created_at"` UpdatedAt time.Time `json:"updated_at" db:"updated_at"` LastScan *time.Time `json:"last_scan" db:"last_scan"`