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
This commit is contained in:
Fimeg
2025-10-31 17:34:05 -04:00
parent e72e9fc16f
commit 5e9c27b7ef
3 changed files with 40 additions and 5 deletions

View File

@@ -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 ## Key Features
**Secure by Default** - Registration tokens, JWT auth, rate limiting **Secure by Default** - Registration tokens, JWT auth, rate limiting

View File

@@ -2,7 +2,7 @@
ALTER TABLE agents ALTER TABLE agents
ADD COLUMN reboot_required BOOLEAN DEFAULT FALSE, ADD COLUMN reboot_required BOOLEAN DEFAULT FALSE,
ADD COLUMN last_reboot_at TIMESTAMP, 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 -- Add index for efficient querying of agents needing reboot
CREATE INDEX idx_agents_reboot_required ON agents(reboot_required) WHERE reboot_required = TRUE; CREATE INDEX idx_agents_reboot_required ON agents(reboot_required) WHERE reboot_required = TRUE;

View File

@@ -23,8 +23,8 @@ type Agent struct {
Status string `json:"status" db:"status"` Status string `json:"status" db:"status"`
Metadata JSONB `json:"metadata" db:"metadata"` Metadata JSONB `json:"metadata" db:"metadata"`
RebootRequired bool `json:"reboot_required" db:"reboot_required"` RebootRequired bool `json:"reboot_required" db:"reboot_required"`
LastRebootAt *time.Time `json:"last_reboot_at" db:"last_reboot_at"` LastRebootAt *time.Time `json:"last_reboot_at,omitempty" db:"last_reboot_at"`
RebootReason string `json:"reboot_reason" db:"reboot_reason"` RebootReason *string `json:"reboot_reason,omitempty" db:"reboot_reason"`
CreatedAt time.Time `json:"created_at" db:"created_at"` CreatedAt time.Time `json:"created_at" db:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"` UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
} }
@@ -44,8 +44,8 @@ type AgentWithLastScan struct {
Status string `json:"status" db:"status"` Status string `json:"status" db:"status"`
Metadata JSONB `json:"metadata" db:"metadata"` Metadata JSONB `json:"metadata" db:"metadata"`
RebootRequired bool `json:"reboot_required" db:"reboot_required"` RebootRequired bool `json:"reboot_required" db:"reboot_required"`
LastRebootAt *time.Time `json:"last_reboot_at" db:"last_reboot_at"` LastRebootAt *time.Time `json:"last_reboot_at,omitempty" db:"last_reboot_at"`
RebootReason string `json:"reboot_reason" db:"reboot_reason"` RebootReason *string `json:"reboot_reason,omitempty" db:"reboot_reason"`
CreatedAt time.Time `json:"created_at" db:"created_at"` CreatedAt time.Time `json:"created_at" db:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"` UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
LastScan *time.Time `json:"last_scan" db:"last_scan"` LastScan *time.Time `json:"last_scan" db:"last_scan"`