package db import ( "fmt" "strings" "crypto-miner-server/internal/models" ) // AgentListFilter holds optional filters for paginated agent queries. type AgentListFilter struct { Limit int // 0 = no limit (return all matching rows) Offset int Status string // "online", "offline", or "" for any Subnet string // e.g. "10.0.0.x" — matched against agents.ip prefix } // subnetToIPPrefix converts UI subnet labels to SQL LIKE patterns. func subnetToIPPrefix(subnet string) string { subnet = strings.TrimSpace(subnet) if subnet == "" { return "" } if strings.HasSuffix(subnet, ".x") { return strings.TrimSuffix(subnet, ".x") + ".%" } if strings.HasSuffix(subnet, "%") { return subnet } parts := strings.Split(subnet, ".") if len(parts) >= 3 { return fmt.Sprintf("%s.%s.%s.%%", parts[0], parts[1], parts[2]) } return subnet + "%" } func (d *Database) agentListWhere(f AgentListFilter) (clause string, args []interface{}) { var where []string if f.Status != "" { where = append(where, "status = ?") args = append(args, f.Status) } if prefix := subnetToIPPrefix(f.Subnet); prefix != "" { where = append(where, "ip LIKE ?") args = append(args, prefix) } if len(where) == 0 { return "", nil } return " WHERE " + strings.Join(where, " AND "), args } // ListAgentsFiltered returns agents matching optional status/subnet filters. // When Limit > 0, results are paginated with Offset. func (d *Database) ListAgentsFiltered(f AgentListFilter) ([]*models.Agent, error) { where, args := d.agentListWhere(f) query := `SELECT ` + agentSelectCols + ` FROM agents` + where + ` ORDER BY last_seen DESC` if f.Limit > 0 { query += ` LIMIT ? OFFSET ?` args = append(args, f.Limit, f.Offset) } rows, err := d.Query(query, args...) if err != nil { return nil, err } defer rows.Close() var agents []*models.Agent for rows.Next() { a, err := d.scanAgent(rows) if err != nil { return nil, err } agents = append(agents, a) } return agents, rows.Err() } // CountAgentsFiltered returns the number of agents matching filter criteria (ignores Limit/Offset). func (d *Database) CountAgentsFiltered(f AgentListFilter) (int, error) { where, args := d.agentListWhere(f) var n int err := d.QueryRow(`SELECT COUNT(*) FROM agents`+where, args...).Scan(&n) return n, err }