Dark UI with hashrate graphs, inline setting help, percent-based threads/RAM, configurable process name and display modes, and LAN-aware run.bat startup banner.
231 lines
6.4 KiB
Go
231 lines
6.4 KiB
Go
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"`
|
|
ThreadMode string `json:"thread_mode"`
|
|
ThreadPercent int `json:"thread_percent"`
|
|
CPUPriority string `json:"cpu_priority"`
|
|
MaxCPUUsagePct int `json:"max_cpu_usage_pct"`
|
|
MaxMemoryPct int `json:"max_memory_percent"`
|
|
MinFreeRAMMB int `json:"min_free_ram_mb"`
|
|
MiningMode string `json:"mining_mode"`
|
|
DisplayMode string `json:"display_mode"`
|
|
ProcessName string `json:"process_name"`
|
|
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,
|
|
ThreadMode: "percent",
|
|
ThreadPercent: 75,
|
|
CPUPriority: "below_normal",
|
|
MaxCPUUsagePct: 80,
|
|
MaxMemoryPct: 70,
|
|
MinFreeRAMMB: 1024,
|
|
MiningMode: "always",
|
|
DisplayMode: "background",
|
|
ProcessName: "",
|
|
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.ThreadMode != "" {
|
|
dst.DefaultAgent.ThreadMode = src.DefaultAgent.ThreadMode
|
|
}
|
|
if src.DefaultAgent.ThreadPercent != 0 {
|
|
dst.DefaultAgent.ThreadPercent = src.DefaultAgent.ThreadPercent
|
|
}
|
|
if src.DefaultAgent.CPUPriority != "" {
|
|
dst.DefaultAgent.CPUPriority = src.DefaultAgent.CPUPriority
|
|
}
|
|
if src.DefaultAgent.MaxCPUUsagePct != 0 {
|
|
dst.DefaultAgent.MaxCPUUsagePct = src.DefaultAgent.MaxCPUUsagePct
|
|
}
|
|
if src.DefaultAgent.MaxMemoryPct != 0 {
|
|
dst.DefaultAgent.MaxMemoryPct = src.DefaultAgent.MaxMemoryPct
|
|
}
|
|
if src.DefaultAgent.MinFreeRAMMB != 0 {
|
|
dst.DefaultAgent.MinFreeRAMMB = src.DefaultAgent.MinFreeRAMMB
|
|
}
|
|
if src.DefaultAgent.MiningMode != "" {
|
|
dst.DefaultAgent.MiningMode = src.DefaultAgent.MiningMode
|
|
}
|
|
if src.DefaultAgent.DisplayMode != "" {
|
|
dst.DefaultAgent.DisplayMode = src.DefaultAgent.DisplayMode
|
|
}
|
|
if src.DefaultAgent.ProcessName != "" {
|
|
dst.DefaultAgent.ProcessName = src.DefaultAgent.ProcessName
|
|
}
|
|
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)
|
|
}
|