fix: agent acknowledgment recursion and subsystem UI improvements

- Fix recursive call in reportLogWithAck that caused infinite loop
- Add machine binding and security API endpoints
- Enhance AgentScanners component with security status display
- Update scheduler and timeout service reliability
- Remove deprecated install.sh script
- Add subsystem configuration and logging improvements
This commit is contained in:
Fimeg
2025-11-03 21:02:57 -05:00
parent d0f13e5da7
commit 57be3754c6
19 changed files with 665 additions and 409 deletions

View File

@@ -23,14 +23,19 @@ func (q *AgentQueries) CreateAgent(agent *models.Agent) error {
query := `
INSERT INTO agents (
id, hostname, os_type, os_version, os_architecture,
agent_version, last_seen, status, metadata
agent_version, current_version, machine_id, public_key_fingerprint,
last_seen, status, metadata
) VALUES (
:id, :hostname, :os_type, :os_version, :os_architecture,
:agent_version, :last_seen, :status, :metadata
:agent_version, :current_version, :machine_id, :public_key_fingerprint,
:last_seen, :status, :metadata
)
`
_, err := q.db.NamedExec(query, agent)
return err
if err != nil {
return fmt.Errorf("failed to create agent %s (version %s): %w", agent.Hostname, agent.CurrentVersion, err)
}
return nil
}
// GetAgentByID retrieves an agent by ID

View File

@@ -2,6 +2,7 @@ package queries
import (
"fmt"
"strings"
"time"
"github.com/Fimeg/RedFlag/aggregator-server/internal/models"
@@ -31,13 +32,14 @@ func (q *CommandQueries) CreateCommand(cmd *models.AgentCommand) error {
}
// GetPendingCommands retrieves pending commands for an agent
// Only returns 'pending' status - 'sent' commands are handled by timeout service
func (q *CommandQueries) GetPendingCommands(agentID uuid.UUID) ([]models.AgentCommand, error) {
var commands []models.AgentCommand
query := `
SELECT * FROM agent_commands
WHERE agent_id = $1 AND status = 'pending'
ORDER BY created_at ASC
LIMIT 10
LIMIT 100
`
err := q.db.Select(&commands, query, agentID)
return commands, err
@@ -338,6 +340,23 @@ func (q *CommandQueries) ClearAllFailedCommands(days int) (int64, error) {
return result.RowsAffected()
}
// ClearAllFailedCommandsRegardlessOfAge archives ALL failed/timed_out commands regardless of age
// This is used when all_failed=true is passed to truly clear all failed commands
func (q *CommandQueries) ClearAllFailedCommandsRegardlessOfAge() (int64, error) {
query := `
UPDATE agent_commands
SET status = 'archived_failed'
WHERE status IN ('failed', 'timed_out')
`
result, err := q.db.Exec(query)
if err != nil {
return 0, fmt.Errorf("failed to archive all failed commands regardless of age: %w", err)
}
return result.RowsAffected()
}
// CountPendingCommandsForAgent returns the number of pending commands for a specific agent
// Used by scheduler for backpressure detection
func (q *CommandQueries) CountPendingCommandsForAgent(agentID uuid.UUID) (int, error) {
@@ -373,16 +392,30 @@ func (q *CommandQueries) VerifyCommandsCompleted(commandIDs []string) ([]string,
return []string{}, nil
}
// Convert UUIDs back to strings for SQL query
uuidStrs := make([]string, len(uuidIDs))
for i, id := range uuidIDs {
uuidStrs[i] = id.String()
}
// Query for commands that are completed or failed
query := `
// Use ANY with proper array literal for PostgreSQL
placeholders := make([]string, len(uuidStrs))
args := make([]interface{}, len(uuidStrs))
for i, id := range uuidStrs {
placeholders[i] = fmt.Sprintf("$%d", i+1)
args[i] = id
}
query := fmt.Sprintf(`
SELECT id
FROM agent_commands
WHERE id = ANY($1)
AND status IN ('completed', 'failed')
`
WHERE id::text = ANY(%s)
AND status IN ('completed', 'failed', 'timed_out')
`, fmt.Sprintf("ARRAY[%s]", strings.Join(placeholders, ",")))
var completedUUIDs []uuid.UUID
err := q.db.Select(&completedUUIDs, query, uuidIDs)
err := q.db.Select(&completedUUIDs, query, args...)
if err != nil {
return nil, fmt.Errorf("failed to verify command completion: %w", err)
}