Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Persist recon scans in SQLite with history diff and export routes; wire WebSocket streaming test coverage and relay path_tracer_hints in fleet reports.
112 lines
2.9 KiB
Go
112 lines
2.9 KiB
Go
package db
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"time"
|
|
|
|
"crypto-miner-server/internal/recon"
|
|
)
|
|
|
|
func (d *Database) ensureReconScansTable() error {
|
|
_, err := d.Exec(`CREATE TABLE IF NOT EXISTS recon_scans (
|
|
scan_id TEXT PRIMARY KEY,
|
|
host TEXT NOT NULL,
|
|
profile TEXT NOT NULL DEFAULT '',
|
|
status TEXT NOT NULL DEFAULT 'complete',
|
|
scanned_at DATETIME NOT NULL,
|
|
report_json TEXT NOT NULL
|
|
)`)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = d.Exec(`CREATE INDEX IF NOT EXISTS idx_recon_scans_host ON recon_scans(host, scanned_at DESC)`)
|
|
return err
|
|
}
|
|
|
|
func (d *Database) InsertReconScan(report *recon.ScanReport) error {
|
|
if d == nil || report == nil || report.ScanID == "" {
|
|
return fmt.Errorf("invalid recon scan row")
|
|
}
|
|
if err := d.ensureReconScansTable(); err != nil {
|
|
return err
|
|
}
|
|
raw, err := json.Marshal(report)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
status := report.Status
|
|
if status == "" {
|
|
status = "complete"
|
|
}
|
|
_, err = d.Exec(`INSERT INTO recon_scans (scan_id, host, profile, status, scanned_at, report_json) VALUES (?, ?, ?, ?, ?, ?)`,
|
|
report.ScanID, report.Host, report.Profile, status, report.ScannedAt.UTC().Format(time.RFC3339Nano), string(raw))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return d.PruneReconScansForHost(report.Host, 10)
|
|
}
|
|
|
|
func (d *Database) GetReconScan(scanID string) (*recon.ScanReport, error) {
|
|
if err := d.ensureReconScansTable(); err != nil {
|
|
return nil, err
|
|
}
|
|
var host, profile, status, scannedAt, raw string
|
|
err := d.QueryRow(`SELECT host, profile, status, scanned_at, report_json FROM recon_scans WHERE scan_id = ?`, scanID).Scan(&host, &profile, &status, &scannedAt, &raw)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var report recon.ScanReport
|
|
if err := json.Unmarshal([]byte(raw), &report); err != nil {
|
|
return nil, err
|
|
}
|
|
if report.ScanID == "" {
|
|
report.ScanID = scanID
|
|
}
|
|
if report.Host == "" {
|
|
report.Host = host
|
|
}
|
|
if report.Profile == "" {
|
|
report.Profile = profile
|
|
}
|
|
if report.Status == "" {
|
|
report.Status = status
|
|
}
|
|
return &report, nil
|
|
}
|
|
|
|
func (d *Database) ListReconScansByHost(host string, limit int) ([]*recon.ScanReport, error) {
|
|
if err := d.ensureReconScansTable(); err != nil {
|
|
return nil, err
|
|
}
|
|
if limit <= 0 {
|
|
limit = 10
|
|
}
|
|
rows, err := d.Query(`SELECT report_json FROM recon_scans WHERE host = ? ORDER BY scanned_at ASC LIMIT ?`, host, limit)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var out []*recon.ScanReport
|
|
for rows.Next() {
|
|
var raw string
|
|
if err := rows.Scan(&raw); err != nil {
|
|
return nil, err
|
|
}
|
|
var report recon.ScanReport
|
|
if err := json.Unmarshal([]byte(raw), &report); err != nil {
|
|
return nil, err
|
|
}
|
|
out = append(out, &report)
|
|
}
|
|
return out, rows.Err()
|
|
}
|
|
|
|
func (d *Database) PruneReconScansForHost(host string, keep int) error {
|
|
if keep <= 0 {
|
|
keep = 10
|
|
}
|
|
_, err := d.Exec(`DELETE FROM recon_scans WHERE host = ? AND scan_id NOT IN (SELECT scan_id FROM recon_scans WHERE host = ? ORDER BY scanned_at DESC LIMIT ?)`, host, host, keep)
|
|
return err
|
|
}
|