279 lines
6.2 KiB
Go
279 lines
6.2 KiB
Go
package database
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
)
|
|
|
|
// MinerSession represents a mining session
|
|
type MinerSession struct {
|
|
ID int64 `json:"id"`
|
|
MinerName string `json:"minerName"`
|
|
MinerType string `json:"minerType"`
|
|
StartedAt time.Time `json:"startedAt"`
|
|
StoppedAt *time.Time `json:"stoppedAt,omitempty"`
|
|
TotalShares int `json:"totalShares"`
|
|
RejectedShares int `json:"rejectedShares"`
|
|
AverageHashrate int `json:"averageHashrate"`
|
|
}
|
|
|
|
// StartSession records the start of a new mining session
|
|
func StartSession(minerName, minerType string) (int64, error) {
|
|
dbMu.RLock()
|
|
defer dbMu.RUnlock()
|
|
|
|
if db == nil {
|
|
return 0, nil
|
|
}
|
|
|
|
result, err := db.Exec(`
|
|
INSERT INTO miner_sessions (miner_name, miner_type, started_at)
|
|
VALUES (?, ?, ?)
|
|
`, minerName, minerType, time.Now())
|
|
if err != nil {
|
|
return 0, fmt.Errorf("failed to start session: %w", err)
|
|
}
|
|
|
|
return result.LastInsertId()
|
|
}
|
|
|
|
// EndSession marks a session as complete with final stats
|
|
func EndSession(sessionID int64, totalShares, rejectedShares, averageHashrate int) error {
|
|
dbMu.RLock()
|
|
defer dbMu.RUnlock()
|
|
|
|
if db == nil {
|
|
return nil
|
|
}
|
|
|
|
_, err := db.Exec(`
|
|
UPDATE miner_sessions
|
|
SET stopped_at = ?,
|
|
total_shares = ?,
|
|
rejected_shares = ?,
|
|
average_hashrate = ?
|
|
WHERE id = ?
|
|
`, time.Now(), totalShares, rejectedShares, averageHashrate, sessionID)
|
|
|
|
return err
|
|
}
|
|
|
|
// EndSessionByName marks the most recent session for a miner as complete
|
|
func EndSessionByName(minerName string, totalShares, rejectedShares, averageHashrate int) error {
|
|
dbMu.RLock()
|
|
defer dbMu.RUnlock()
|
|
|
|
if db == nil {
|
|
return nil
|
|
}
|
|
|
|
_, err := db.Exec(`
|
|
UPDATE miner_sessions
|
|
SET stopped_at = ?,
|
|
total_shares = ?,
|
|
rejected_shares = ?,
|
|
average_hashrate = ?
|
|
WHERE miner_name = ?
|
|
AND stopped_at IS NULL
|
|
`, time.Now(), totalShares, rejectedShares, averageHashrate, minerName)
|
|
|
|
return err
|
|
}
|
|
|
|
// GetSession retrieves a session by ID
|
|
func GetSession(sessionID int64) (*MinerSession, error) {
|
|
dbMu.RLock()
|
|
defer dbMu.RUnlock()
|
|
|
|
if db == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
var session MinerSession
|
|
var stoppedAt *time.Time
|
|
|
|
err := db.QueryRow(`
|
|
SELECT id, miner_name, miner_type, started_at, stopped_at,
|
|
total_shares, rejected_shares, average_hashrate
|
|
FROM miner_sessions
|
|
WHERE id = ?
|
|
`, sessionID).Scan(
|
|
&session.ID,
|
|
&session.MinerName,
|
|
&session.MinerType,
|
|
&session.StartedAt,
|
|
&stoppedAt,
|
|
&session.TotalShares,
|
|
&session.RejectedShares,
|
|
&session.AverageHashrate,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
session.StoppedAt = stoppedAt
|
|
return &session, nil
|
|
}
|
|
|
|
// GetActiveSessions retrieves all currently active (non-stopped) sessions
|
|
func GetActiveSessions() ([]MinerSession, error) {
|
|
dbMu.RLock()
|
|
defer dbMu.RUnlock()
|
|
|
|
if db == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
rows, err := db.Query(`
|
|
SELECT id, miner_name, miner_type, started_at, stopped_at,
|
|
total_shares, rejected_shares, average_hashrate
|
|
FROM miner_sessions
|
|
WHERE stopped_at IS NULL
|
|
ORDER BY started_at DESC
|
|
`)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
var sessions []MinerSession
|
|
for rows.Next() {
|
|
var session MinerSession
|
|
var stoppedAt *time.Time
|
|
if err := rows.Scan(
|
|
&session.ID,
|
|
&session.MinerName,
|
|
&session.MinerType,
|
|
&session.StartedAt,
|
|
&stoppedAt,
|
|
&session.TotalShares,
|
|
&session.RejectedShares,
|
|
&session.AverageHashrate,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
session.StoppedAt = stoppedAt
|
|
sessions = append(sessions, session)
|
|
}
|
|
|
|
return sessions, rows.Err()
|
|
}
|
|
|
|
// GetRecentSessions retrieves the most recent sessions for a miner
|
|
func GetRecentSessions(minerName string, limit int) ([]MinerSession, error) {
|
|
dbMu.RLock()
|
|
defer dbMu.RUnlock()
|
|
|
|
if db == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
rows, err := db.Query(`
|
|
SELECT id, miner_name, miner_type, started_at, stopped_at,
|
|
total_shares, rejected_shares, average_hashrate
|
|
FROM miner_sessions
|
|
WHERE miner_name = ?
|
|
ORDER BY started_at DESC
|
|
LIMIT ?
|
|
`, minerName, limit)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
var sessions []MinerSession
|
|
for rows.Next() {
|
|
var session MinerSession
|
|
var stoppedAt *time.Time
|
|
if err := rows.Scan(
|
|
&session.ID,
|
|
&session.MinerName,
|
|
&session.MinerType,
|
|
&session.StartedAt,
|
|
&stoppedAt,
|
|
&session.TotalShares,
|
|
&session.RejectedShares,
|
|
&session.AverageHashrate,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
session.StoppedAt = stoppedAt
|
|
sessions = append(sessions, session)
|
|
}
|
|
|
|
return sessions, rows.Err()
|
|
}
|
|
|
|
// GetSessionStats retrieves aggregated session statistics for a miner
|
|
type SessionStats struct {
|
|
MinerName string `json:"minerName"`
|
|
TotalSessions int `json:"totalSessions"`
|
|
TotalUptime time.Duration `json:"totalUptime"`
|
|
TotalShares int `json:"totalShares"`
|
|
TotalRejected int `json:"totalRejected"`
|
|
AvgSessionTime time.Duration `json:"avgSessionTime"`
|
|
AvgHashrate int `json:"avgHashrate"`
|
|
LastSessionAt time.Time `json:"lastSessionAt"`
|
|
}
|
|
|
|
func GetSessionStats(minerName string) (*SessionStats, error) {
|
|
dbMu.RLock()
|
|
defer dbMu.RUnlock()
|
|
|
|
if db == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
var stats SessionStats
|
|
stats.MinerName = minerName
|
|
|
|
// Get basic aggregates
|
|
err := db.QueryRow(`
|
|
SELECT
|
|
COUNT(*),
|
|
COALESCE(SUM(total_shares), 0),
|
|
COALESCE(SUM(rejected_shares), 0),
|
|
COALESCE(AVG(average_hashrate), 0),
|
|
MAX(started_at)
|
|
FROM miner_sessions
|
|
WHERE miner_name = ?
|
|
AND stopped_at IS NOT NULL
|
|
`, minerName).Scan(
|
|
&stats.TotalSessions,
|
|
&stats.TotalShares,
|
|
&stats.TotalRejected,
|
|
&stats.AvgHashrate,
|
|
&stats.LastSessionAt,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Calculate total uptime
|
|
rows, err := db.Query(`
|
|
SELECT started_at, stopped_at
|
|
FROM miner_sessions
|
|
WHERE miner_name = ?
|
|
AND stopped_at IS NOT NULL
|
|
`, minerName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
var totalSeconds int64
|
|
for rows.Next() {
|
|
var started, stopped time.Time
|
|
if err := rows.Scan(&started, &stopped); err != nil {
|
|
return nil, err
|
|
}
|
|
totalSeconds += int64(stopped.Sub(started).Seconds())
|
|
}
|
|
|
|
stats.TotalUptime = time.Duration(totalSeconds) * time.Second
|
|
if stats.TotalSessions > 0 {
|
|
stats.AvgSessionTime = stats.TotalUptime / time.Duration(stats.TotalSessions)
|
|
}
|
|
|
|
return &stats, nil
|
|
}
|