Fix zero UUID and storage metrics issues\n\n- Add ID generation in signAndCreateCommand to prevent zero UUIDs\n- Fix storage metrics metadata field (remove pq.Array)\n- Add IdempotencyKey field to AgentCommand model\n\nThese issues were causing duplicate key violations and storage page errors.
This commit is contained in:
@@ -32,6 +32,19 @@ func NewSubsystemHandler(sq *queries.SubsystemQueries, cq *queries.CommandQuerie
|
|||||||
|
|
||||||
// signAndCreateCommand signs a command if signing service is enabled, then stores it in the database
|
// signAndCreateCommand signs a command if signing service is enabled, then stores it in the database
|
||||||
func (h *SubsystemHandler) signAndCreateCommand(cmd *models.AgentCommand) error {
|
func (h *SubsystemHandler) signAndCreateCommand(cmd *models.AgentCommand) error {
|
||||||
|
// Generate ID if not set (prevents zero UUID issues)
|
||||||
|
if cmd.ID == uuid.Nil {
|
||||||
|
cmd.ID = uuid.New()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set timestamps if not set
|
||||||
|
if cmd.CreatedAt.IsZero() {
|
||||||
|
cmd.CreatedAt = time.Now()
|
||||||
|
}
|
||||||
|
if cmd.UpdatedAt.IsZero() {
|
||||||
|
cmd.UpdatedAt = time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
// Sign the command before storing
|
// Sign the command before storing
|
||||||
if h.signingService != nil && h.signingService.IsEnabled() {
|
if h.signingService != nil && h.signingService.IsEnabled() {
|
||||||
signature, err := h.signingService.SignCommand(cmd)
|
signature, err := h.signingService.SignCommand(cmd)
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ func (q *StorageMetricsQueries) InsertStorageMetric(ctx context.Context, metric
|
|||||||
metric.ID, metric.AgentID, metric.Mountpoint, metric.Device,
|
metric.ID, metric.AgentID, metric.Mountpoint, metric.Device,
|
||||||
metric.DiskType, metric.Filesystem, metric.TotalBytes,
|
metric.DiskType, metric.Filesystem, metric.TotalBytes,
|
||||||
metric.UsedBytes, metric.AvailableBytes, metric.UsedPercent,
|
metric.UsedBytes, metric.AvailableBytes, metric.UsedPercent,
|
||||||
metric.Severity, pq.Array(metric.Metadata), metric.CreatedAt,
|
metric.Severity, metric.Metadata, metric.CreatedAt,
|
||||||
)
|
)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ type AgentCommand struct {
|
|||||||
Status string `json:"status" db:"status"`
|
Status string `json:"status" db:"status"`
|
||||||
Source string `json:"source" db:"source"`
|
Source string `json:"source" db:"source"`
|
||||||
Signature string `json:"signature,omitempty" db:"signature"`
|
Signature string `json:"signature,omitempty" db:"signature"`
|
||||||
|
IdempotencyKey *string `json:"idempotency_key,omitempty" db:"idempotency_key"`
|
||||||
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"`
|
||||||
SentAt *time.Time `json:"sent_at,omitempty" db:"sent_at"`
|
SentAt *time.Time `json:"sent_at,omitempty" db:"sent_at"`
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
echo "=== RedFlag Database Recovery Script ==="
|
|
||||||
echo
|
|
||||||
|
|
||||||
echo "1. Stopping containers..."
|
|
||||||
docker-compose down
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo "2. Removing postgres volume to reset database (WARNING: This will delete all data)..."
|
|
||||||
docker volume rm redflag_postgres-data 2>/dev/null
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo "3. Starting containers with fixed configuration..."
|
|
||||||
docker-compose up -d
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo "4. Waiting for database to be ready..."
|
|
||||||
sleep 10
|
|
||||||
docker exec redflag-postgres pg_isready -U redflag
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo "5. Checking server logs (tail):"
|
|
||||||
echo "=========================="
|
|
||||||
docker logs redflag-server --tail 50
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo "=========================="
|
|
||||||
echo "If migrations ran successfully, you should see:"
|
|
||||||
echo "- Database migrations completed message"
|
|
||||||
echo "- Default security settings initialized"
|
|
||||||
echo "- Admin user ensured"
|
|
||||||
echo
|
|
||||||
echo "The server should now be accessible at http://localhost:8080"
|
|
||||||
echo "Login with admin / Qu@ntum21!"
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
## Summary: Why History Shows "SCAN" Generically
|
|
||||||
|
|
||||||
**The Confusion You See**:
|
|
||||||
- Each subsystem has its own "Scan" button (✅ correct)
|
|
||||||
- But history only shows generic "SCAN" (❌ confusing)
|
|
||||||
|
|
||||||
**The Implementation Flow**:
|
|
||||||
```
|
|
||||||
You click: "Scan Storage" button
|
|
||||||
→ UI passes: subsystem="storage" ✅
|
|
||||||
→ Backend creates: command_type="scan_storage" ✅
|
|
||||||
→ Agent runs: handleScanStorage() ✅
|
|
||||||
→ Results stored: updates=[4 items] ✅
|
|
||||||
→ History logged: action="scan" ❌ (should be "storage scan" or similar)
|
|
||||||
```
|
|
||||||
|
|
||||||
**Root Cause**:
|
|
||||||
The history table's `action` field stores only generic "scan" instead of including the subsystem context. Even though:
|
|
||||||
- Backend knows it's "scan_storage"
|
|
||||||
- UI sends subsystem parameter
|
|
||||||
- Results are subsystem-specific
|
|
||||||
|
|
||||||
**The Result**:
|
|
||||||
```
|
|
||||||
History shows unhelpful entries like:
|
|
||||||
[14:20] SCAN → Success → 4 updates found
|
|
||||||
[14:19] SCAN → Success → 461 updates found
|
|
||||||
|
|
||||||
Which subsystem found which updates? Unknown from history.
|
|
||||||
```
|
|
||||||
|
|
||||||
**This is a UX Issue, NOT a Bug**:
|
|
||||||
- ✅ Scans run for correct subsystems
|
|
||||||
- ✅ Results are accurate
|
|
||||||
- ✅ Backend distinguishes types ("scan_storage", "scan_system", "scan_docker")
|
|
||||||
- ❌ History display is generic "SCAN" instead of "Storage Scan", "System Scan", "Docker Scan"
|
|
||||||
|
|
||||||
**Why It Happened**:
|
|
||||||
- Early design had simple action types ("scan", "install", "upgrade")
|
|
||||||
- Later added docker/storage/system scans
|
|
||||||
- Database schema never evolved to include subsystem context
|
|
||||||
- History display just shows action field directly
|
|
||||||
|
|
||||||
**Files Involved**:
|
|
||||||
- ✅ Working: AgentHealth.tsx (per-subsystem scan buttons)
|
|
||||||
- ✅ Working: Backend API (creates "scan_storage", "scan_system", etc.)
|
|
||||||
- ❌ Broken: History logging (stores only "scan", not subsystem)
|
|
||||||
- ❌ Broken: History display (shows generic text, no subsystem parsing)
|
|
||||||
|
|
||||||
**Full Analysis**: `/home/casey/Projects/RedFlag/UX_ISSUE_ANALYSIS_scan_history.md`
|
|
||||||
149
sudo
149
sudo
@@ -1,149 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Parse command line arguments
|
|
||||||
TARGET="$1" # Optional target parameter
|
|
||||||
|
|
||||||
# Validate target if provided
|
|
||||||
if [[ -n "$TARGET" ]] && [[ ! "$TARGET" =~ ^(stable|latest|[0-9]+\.[0-9]+\.[0-9]+(-[^[:space:]]+)?)$ ]]; then
|
|
||||||
echo "Usage: $0 [stable|latest|VERSION]" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
GCS_BUCKET="https://storage.googleapis.com/claude-code-dist-86c565f3-f756-42ad-8dfa-d59b1c096819/claude-code-releases"
|
|
||||||
DOWNLOAD_DIR="$HOME/.claude/downloads"
|
|
||||||
|
|
||||||
# Check for required dependencies
|
|
||||||
DOWNLOADER=""
|
|
||||||
if command -v curl >/dev/null 2>&1; then
|
|
||||||
DOWNLOADER="curl"
|
|
||||||
elif command -v wget >/dev/null 2>&1; then
|
|
||||||
DOWNLOADER="wget"
|
|
||||||
else
|
|
||||||
echo "Either curl or wget is required but neither is installed" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check if jq is available (optional)
|
|
||||||
HAS_JQ=false
|
|
||||||
if command -v jq >/dev/null 2>&1; then
|
|
||||||
HAS_JQ=true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Download function that works with both curl and wget
|
|
||||||
download_file() {
|
|
||||||
local url="$1"
|
|
||||||
local output="$2"
|
|
||||||
|
|
||||||
if [ "$DOWNLOADER" = "curl" ]; then
|
|
||||||
if [ -n "$output" ]; then
|
|
||||||
curl -fsSL -o "$output" "$url"
|
|
||||||
else
|
|
||||||
curl -fsSL "$url"
|
|
||||||
fi
|
|
||||||
elif [ "$DOWNLOADER" = "wget" ]; then
|
|
||||||
if [ -n "$output" ]; then
|
|
||||||
wget -q -O "$output" "$url"
|
|
||||||
else
|
|
||||||
wget -q -O - "$url"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Simple JSON parser for extracting checksum when jq is not available
|
|
||||||
get_checksum_from_manifest() {
|
|
||||||
local json="$1"
|
|
||||||
local platform="$2"
|
|
||||||
|
|
||||||
# Normalize JSON to single line and extract checksum
|
|
||||||
json=$(echo "$json" | tr -d '\n\r\t' | sed 's/ \+/ /g')
|
|
||||||
|
|
||||||
# Extract checksum for platform using bash regex
|
|
||||||
if [[ $json =~ \"$platform\"[^}]*\"checksum\"[[:space:]]*:[[:space:]]*\"([a-f0-9]{64})\" ]]; then
|
|
||||||
echo "${BASH_REMATCH[1]}"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# Detect platform
|
|
||||||
case "$(uname -s)" in
|
|
||||||
Darwin) os="darwin" ;;
|
|
||||||
Linux) os="linux" ;;
|
|
||||||
*) echo "Windows is not supported" >&2; exit 1 ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
case "$(uname -m)" in
|
|
||||||
x86_64|amd64) arch="x64" ;;
|
|
||||||
arm64|aarch64) arch="arm64" ;;
|
|
||||||
*) echo "Unsupported architecture: $(uname -m)" >&2; exit 1 ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
# Check for musl on Linux and adjust platform accordingly
|
|
||||||
if [ "$os" = "linux" ]; then
|
|
||||||
if [ -f /lib/libc.musl-x86_64.so.1 ] || [ -f /lib/libc.musl-aarch64.so.1 ] || ldd /bin/ls 2>&1 | grep -q musl; then
|
|
||||||
platform="linux-${arch}-musl"
|
|
||||||
else
|
|
||||||
platform="linux-${arch}"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
platform="${os}-${arch}"
|
|
||||||
fi
|
|
||||||
mkdir -p "$DOWNLOAD_DIR"
|
|
||||||
|
|
||||||
# Always download stable version (which has the most up-to-date installer)
|
|
||||||
version=$(download_file "$GCS_BUCKET/stable")
|
|
||||||
|
|
||||||
# Download manifest and extract checksum
|
|
||||||
manifest_json=$(download_file "$GCS_BUCKET/$version/manifest.json")
|
|
||||||
|
|
||||||
# Use jq if available, otherwise fall back to pure bash parsing
|
|
||||||
if [ "$HAS_JQ" = true ]; then
|
|
||||||
checksum=$(echo "$manifest_json" | jq -r ".platforms[\"$platform\"].checksum // empty")
|
|
||||||
else
|
|
||||||
checksum=$(get_checksum_from_manifest "$manifest_json" "$platform")
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Validate checksum format (SHA256 = 64 hex characters)
|
|
||||||
if [ -z "$checksum" ] || [[ ! "$checksum" =~ ^[a-f0-9]{64}$ ]]; then
|
|
||||||
echo "Platform $platform not found in manifest" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Download and verify
|
|
||||||
binary_path="$DOWNLOAD_DIR/claude-$version-$platform"
|
|
||||||
if ! download_file "$GCS_BUCKET/$version/$platform/claude" "$binary_path"; then
|
|
||||||
echo "Download failed" >&2
|
|
||||||
rm -f "$binary_path"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Pick the right checksum tool
|
|
||||||
if [ "$os" = "darwin" ]; then
|
|
||||||
actual=$(shasum -a 256 "$binary_path" | cut -d' ' -f1)
|
|
||||||
else
|
|
||||||
actual=$(sha256sum "$binary_path" | cut -d' ' -f1)
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$actual" != "$checksum" ]; then
|
|
||||||
echo "Checksum verification failed" >&2
|
|
||||||
rm -f "$binary_path"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
chmod +x "$binary_path"
|
|
||||||
|
|
||||||
# Run claude install to set up launcher and shell integration
|
|
||||||
echo "Setting up Claude Code..."
|
|
||||||
"$binary_path" install ${TARGET:+"$TARGET"}
|
|
||||||
|
|
||||||
# Clean up downloaded file
|
|
||||||
rm -f "$binary_path"
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "✅ Installation complete!"
|
|
||||||
echo ""
|
|
||||||
BIN
test-binary
BIN
test-binary
Binary file not shown.
@@ -1,54 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Test script to verify the installation command generation fixes
|
|
||||||
|
|
||||||
echo "=== Testing RedFlag Agent Installation Command Generation ==="
|
|
||||||
echo
|
|
||||||
|
|
||||||
# Test 1: Verify the API endpoint exists and is accessible
|
|
||||||
echo "1. Testing API endpoint availability..."
|
|
||||||
if curl -sfL "http://localhost:8080/api/v1/install/linux" > /dev/null 2>&1; then
|
|
||||||
echo "✓ API endpoint /api/v1/install/linux is accessible"
|
|
||||||
else
|
|
||||||
echo "⚠ API endpoint not accessible (server may not be running)"
|
|
||||||
fi
|
|
||||||
echo
|
|
||||||
|
|
||||||
# Test 2: Show examples of corrected commands
|
|
||||||
echo "2. Corrected Installation Commands:"
|
|
||||||
echo "-----------------------------------"
|
|
||||||
echo
|
|
||||||
echo "For Registration Token API (when creating a new token):"
|
|
||||||
echo 'curl -sfL "http://localhost:8080/api/v1/install/linux?token=YOUR_TOKEN_HERE" | sudo bash'
|
|
||||||
echo
|
|
||||||
|
|
||||||
echo "For Agent Settings UI (Linux):"
|
|
||||||
echo 'curl -sfL "http://localhost:8080/api/v1/install/linux?token=YOUR_TOKEN_HERE" | sudo bash'
|
|
||||||
echo
|
|
||||||
|
|
||||||
echo "For Agent Settings UI (Windows PowerShell):"
|
|
||||||
echo 'iwr "http://localhost:8080/api/v1/install/windows?token=YOUR_TOKEN_HERE" -OutFile install.bat; .\install.bat'
|
|
||||||
echo
|
|
||||||
|
|
||||||
# Test 3: Verify template variables
|
|
||||||
echo "3. Template Variables Check:"
|
|
||||||
echo "-----------------------------"
|
|
||||||
echo "The following variables are now provided to the install template:"
|
|
||||||
echo "- AgentUser: redflag-agent"
|
|
||||||
echo "- AgentHome: /var/lib/redflag-agent"
|
|
||||||
echo "- ConfigDir: /etc/redflag"
|
|
||||||
echo "- LogDir: /var/log/redflag"
|
|
||||||
echo
|
|
||||||
|
|
||||||
echo "=== Summary of Fixes ==="
|
|
||||||
echo "✓ Fixed registration token API command generation"
|
|
||||||
echo "✓ Fixed agent settings UI command generation (both TokenManagement and AgentManagement)"
|
|
||||||
echo "✓ Fixed template error by adding .AgentUser and related variables"
|
|
||||||
echo "✓ Changed from command-line args to query parameters for token passing"
|
|
||||||
echo "✓ Added proper protocol handling (http:// for localhost)"
|
|
||||||
echo "✓ Added sudo to the bash command for proper permissions"
|
|
||||||
echo
|
|
||||||
|
|
||||||
echo "All installation commands now use the correct format:"
|
|
||||||
echo 'curl -sfL "http://localhost:8080/api/v1/install/linux?token=<TOKEN>" | sudo bash'
|
|
||||||
echo
|
|
||||||
Reference in New Issue
Block a user