ax(batch): rename abbreviated variables to predictable names
Some checks are pending
Security Scan / security (push) Waiting to run
Test / test (push) Waiting to run

resp→response, db→database pattern across mining, logging packages.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude 2026-04-04 04:47:58 +01:00
parent 3bf5496fce
commit 1f9624279a
No known key found for this signature in database
GPG key ID: AF404715446AEB41
17 changed files with 225 additions and 190 deletions

34
AGENTS.md Normal file
View file

@ -0,0 +1,34 @@
# Repository Guidelines
## AX Standard
This repository is being upgraded toward the Core Agent Experience standard defined in `/home/claude/Code/core/plans/rfc/core/RFC-CORE-008-AGENT-EXPERIENCE.md`.
Apply these rules by default:
- Prefer explicit names over short names. Use `manager`, `service`, `config`, and `request` instead of repo-local abbreviations like `mgr`, `svc`, and `cfg`.
- Write comments as concrete usage examples with realistic values.
- Keep paths and filenames descriptive so intent is obvious before opening a file.
- Preserve one-way dependency flow: foundational packages in `pkg/` should not depend on CLI wrappers in `cmd/`.
- Make repeated shapes predictable. Reuse existing config structs, route patterns, and test helpers instead of inventing parallel forms.
## Go Project Shape
- `cmd/mining/` contains Cobra CLI entrypoints.
- `pkg/mining/` contains miner orchestration, REST API, profiles, auth, and service container logic.
- `pkg/node/` contains peer, protocol, transport, and worker logic.
- `pkg/database/` contains persistence and hashrate storage.
- `cmd/desktop/mining-desktop/` contains the Wails desktop app wrapper.
## Working Rules
- Keep changes behavioral-safe unless the task explicitly asks for feature work.
- Prefer focused AX upgrades in high-traffic files such as `pkg/mining/service.go`, `pkg/mining/manager.go`, and `cmd/mining/cmd/*.go`.
- Run `gofmt` on edited Go files.
- Validate targeted changes first with package-level tests before broader runs.
## Verification
- `go test ./pkg/mining/...`
- `go test ./cmd/mining/...`
- `make test-go`

View file

@ -106,7 +106,7 @@ func (logger *Logger) GetLevel() Level {
type Fields map[string]interface{}
// logger.log(LevelInfo, "started", Fields{"miner": "xmrig"})
func (logger *Logger) log(level Level, msg string, fields Fields) {
func (logger *Logger) log(level Level, message string, fields Fields) {
logger.mutex.Lock()
defer logger.mutex.Unlock()
@ -129,7 +129,7 @@ func (logger *Logger) log(level Level, msg string, fields Fields) {
}
builder.WriteString(" ")
builder.WriteString(msg)
builder.WriteString(message)
// Add fields if present
if len(fields) > 0 {
@ -147,23 +147,23 @@ func (logger *Logger) log(level Level, msg string, fields Fields) {
}
// logger.Debug("hashrate collected", logging.Fields{"rate": 1234})
func (logger *Logger) Debug(msg string, fields ...Fields) {
logger.log(LevelDebug, msg, mergeFields(fields))
func (logger *Logger) Debug(message string, fields ...Fields) {
logger.log(LevelDebug, message, mergeFields(fields))
}
// logger.Info("miner started", logging.Fields{"miner": "xmrig"})
func (logger *Logger) Info(msg string, fields ...Fields) {
logger.log(LevelInfo, msg, mergeFields(fields))
func (logger *Logger) Info(message string, fields ...Fields) {
logger.log(LevelInfo, message, mergeFields(fields))
}
// logger.Warn("hashrate drop", logging.Fields{"current": 500, "min": 1000})
func (logger *Logger) Warn(msg string, fields ...Fields) {
logger.log(LevelWarn, msg, mergeFields(fields))
func (logger *Logger) Warn(message string, fields ...Fields) {
logger.log(LevelWarn, message, mergeFields(fields))
}
// logger.Error("miner crashed", logging.Fields{"code": -1, "miner": "xmrig"})
func (logger *Logger) Error(msg string, fields ...Fields) {
logger.log(LevelError, msg, mergeFields(fields))
func (logger *Logger) Error(message string, fields ...Fields) {
logger.log(LevelError, message, mergeFields(fields))
}
// logger.Debugf("collected %d hashrate points for %s", len(points), minerName)
@ -232,23 +232,23 @@ func SetGlobalLevel(level Level) {
// Global convenience functions that use the global logger
// logging.Debug("hashrate collected", logging.Fields{"rate": 1234, "miner": "xmrig"})
func Debug(msg string, fields ...Fields) {
GetGlobal().Debug(msg, fields...)
func Debug(message string, fields ...Fields) {
GetGlobal().Debug(message, fields...)
}
// logging.Info("miner started", logging.Fields{"miner": "xmrig", "pool": "pool.lthn.io"})
func Info(msg string, fields ...Fields) {
GetGlobal().Info(msg, fields...)
func Info(message string, fields ...Fields) {
GetGlobal().Info(message, fields...)
}
// logging.Warn("hashrate dropped below threshold", logging.Fields{"current": 500, "min": 1000})
func Warn(msg string, fields ...Fields) {
GetGlobal().Warn(msg, fields...)
func Warn(message string, fields ...Fields) {
GetGlobal().Warn(message, fields...)
}
// logging.Error("miner process exited unexpectedly", logging.Fields{"code": -1})
func Error(msg string, fields ...Fields) {
GetGlobal().Error(msg, fields...)
func Error(message string, fields ...Fields) {
GetGlobal().Error(message, fields...)
}
// logging.Debugf("collected %d hashrate points for %s", len(points), minerName)

View file

@ -85,7 +85,7 @@ func (circuitBreaker *CircuitBreaker) State() CircuitState {
}
// result, err := circuitBreaker.Execute(func() (interface{}, error) { return fetchStats(ctx) })
func (circuitBreaker *CircuitBreaker) Execute(fn func() (interface{}, error)) (interface{}, error) {
func (circuitBreaker *CircuitBreaker) Execute(operation func() (interface{}, error)) (interface{}, error) {
if !circuitBreaker.allowRequest() {
circuitBreaker.mutex.RLock()
if circuitBreaker.cachedResult != nil && time.Since(circuitBreaker.lastCacheTime) < circuitBreaker.cacheDuration {
@ -101,7 +101,7 @@ func (circuitBreaker *CircuitBreaker) Execute(fn func() (interface{}, error)) (i
return nil, ErrCircuitOpen
}
result, err := fn()
result, err := operation()
if err != nil {
circuitBreaker.recordFailure()

View file

@ -251,20 +251,20 @@ func (b *BaseMiner) InstallFromURL(url string) error {
defer os.Remove(tmpfile.Name())
defer tmpfile.Close()
resp, err := getHTTPClient().Get(url)
response, err := getHTTPClient().Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
defer response.Body.Close()
if resp.StatusCode != http.StatusOK {
_, _ = io.Copy(io.Discard, resp.Body) // Drain body to allow connection reuse (error ignored intentionally)
return ErrInstallFailed(b.ExecutableName).WithDetails("unexpected status code " + strconv.Itoa(resp.StatusCode))
if response.StatusCode != http.StatusOK {
_, _ = io.Copy(io.Discard, response.Body) // Drain body to allow connection reuse (error ignored intentionally)
return ErrInstallFailed(b.ExecutableName).WithDetails("unexpected status code " + strconv.Itoa(response.StatusCode))
}
if _, err := io.Copy(tmpfile, resp.Body); err != nil {
if _, err := io.Copy(tmpfile, response.Body); err != nil {
// Drain remaining body to allow connection reuse (error ignored intentionally)
_, _ = io.Copy(io.Discard, resp.Body)
_, _ = io.Copy(io.Discard, response.Body)
return err
}
@ -397,12 +397,12 @@ func (b *BaseMiner) CheckInstallation() (*InstallationDetails, error) {
b.Path = filepath.Dir(binaryPath)
cmd := exec.Command(binaryPath, "--version")
var out bytes.Buffer
cmd.Stdout = &out
var output bytes.Buffer
cmd.Stdout = &output
if err := cmd.Run(); err != nil {
b.Version = "Unknown (could not run executable)"
} else {
fields := strings.Fields(out.String())
fields := strings.Fields(output.String())
if len(fields) >= 2 {
b.Version = fields[1]
} else {

View file

@ -4,7 +4,7 @@ import (
"testing"
)
func TestMinerFactory_Create(t *testing.T) {
func TestMinerFactory_Create_Good(t *testing.T) {
factory := NewMinerFactory()
tests := []struct {
@ -40,7 +40,7 @@ func TestMinerFactory_Create(t *testing.T) {
}
}
func TestMinerFactory_IsSupported(t *testing.T) {
func TestMinerFactory_IsSupported_Good(t *testing.T) {
factory := NewMinerFactory()
tests := []struct {
@ -63,7 +63,7 @@ func TestMinerFactory_IsSupported(t *testing.T) {
}
}
func TestMinerFactory_ListTypes(t *testing.T) {
func TestMinerFactory_ListTypes_Good(t *testing.T) {
factory := NewMinerFactory()
types := factory.ListTypes()
@ -73,8 +73,8 @@ func TestMinerFactory_ListTypes(t *testing.T) {
// Check that expected types are present
typeMap := make(map[string]bool)
for _, typ := range types {
typeMap[typ] = true
for _, minerType := range types {
typeMap[minerType] = true
}
expectedTypes := []string{"xmrig", "tt-miner"}
@ -85,7 +85,7 @@ func TestMinerFactory_ListTypes(t *testing.T) {
}
}
func TestMinerFactory_Register(t *testing.T) {
func TestMinerFactory_Register_Good(t *testing.T) {
factory := NewMinerFactory()
// Register a custom miner type
@ -108,7 +108,7 @@ func TestMinerFactory_Register(t *testing.T) {
}
}
func TestMinerFactory_RegisterAlias(t *testing.T) {
func TestMinerFactory_RegisterAlias_Good(t *testing.T) {
factory := NewMinerFactory()
// Register an alias for xmrig
@ -127,7 +127,7 @@ func TestMinerFactory_RegisterAlias(t *testing.T) {
}
}
func TestGlobalFactory_CreateMiner(t *testing.T) {
func TestGlobalFactory_CreateMiner_Good(t *testing.T) {
// Test global convenience functions
miner, err := CreateMiner("xmrig")
if err != nil {
@ -138,7 +138,7 @@ func TestGlobalFactory_CreateMiner(t *testing.T) {
}
}
func TestGlobalFactory_IsMinerSupported(t *testing.T) {
func TestGlobalFactory_IsMinerSupported_Good(t *testing.T) {
if !IsMinerSupported("xmrig") {
t.Error("xmrig should be supported")
}
@ -147,7 +147,7 @@ func TestGlobalFactory_IsMinerSupported(t *testing.T) {
}
}
func TestGlobalFactory_ListMinerTypes(t *testing.T) {
func TestGlobalFactory_ListMinerTypes_Good(t *testing.T) {
types := ListMinerTypes()
if len(types) < 2 {
t.Errorf("ListMinerTypes() returned %d types, expected at least 2", len(types))

View file

@ -82,7 +82,7 @@ type Config struct {
Keepalive bool `json:"keepalive,omitempty"`
Nicehash bool `json:"nicehash,omitempty"`
RigID string `json:"rigId,omitempty"`
TLSSingerprint string `json:"tlsFingerprint,omitempty"`
TLSFingerprint string `json:"tlsFingerprint,omitempty"`
Retries int `json:"retries,omitempty"`
RetryPause int `json:"retryPause,omitempty"`
UserAgent string `json:"userAgent,omitempty"`

View file

@ -9,8 +9,8 @@ import (
"github.com/gin-gonic/gin"
)
// ns, err := NewNodeService()
// ns.SetupRoutes(router.Group("/api/v1/mining"))
// nodeService, err := NewNodeService()
// nodeService.SetupRoutes(router.Group("/api/v1/mining"))
type NodeService struct {
nodeManager *node.NodeManager
peerRegistry *node.PeerRegistry
@ -19,7 +19,7 @@ type NodeService struct {
worker *node.Worker
}
// ns, err := NewNodeService() // initialises node manager, peer registry, transport, controller, worker
// nodeService, err := NewNodeService() // initialises node manager, peer registry, transport, controller, worker
func NewNodeService() (*NodeService, error) {
nodeManager, err := node.NewNodeManager()
if err != nil {
@ -46,53 +46,53 @@ func NewNodeService() (*NodeService, error) {
return nodeService, nil
}
// ns.SetupRoutes(router.Group("/api/v1/mining")) // registers /node, /peers, /remote route groups
func (ns *NodeService) SetupRoutes(router *gin.RouterGroup) {
// service.SetupRoutes(router.Group("/api/v1/mining")) // registers /node, /peers, /remote route groups
func (nodeService *NodeService) SetupRoutes(router *gin.RouterGroup) {
// Node identity endpoints
nodeGroup := router.Group("/node")
{
nodeGroup.GET("/info", ns.handleNodeInfo)
nodeGroup.POST("/init", ns.handleNodeInit)
nodeGroup.GET("/info", nodeService.handleNodeInfo)
nodeGroup.POST("/init", nodeService.handleNodeInit)
}
// Peer management endpoints
peerGroup := router.Group("/peers")
{
peerGroup.GET("", ns.handleListPeers)
peerGroup.POST("", ns.handleAddPeer)
peerGroup.GET("/:id", ns.handleGetPeer)
peerGroup.DELETE("/:id", ns.handleRemovePeer)
peerGroup.POST("/:id/ping", ns.handlePingPeer)
peerGroup.POST("/:id/connect", ns.handleConnectPeer)
peerGroup.POST("/:id/disconnect", ns.handleDisconnectPeer)
peerGroup.GET("", nodeService.handleListPeers)
peerGroup.POST("", nodeService.handleAddPeer)
peerGroup.GET("/:id", nodeService.handleGetPeer)
peerGroup.DELETE("/:id", nodeService.handleRemovePeer)
peerGroup.POST("/:id/ping", nodeService.handlePingPeer)
peerGroup.POST("/:id/connect", nodeService.handleConnectPeer)
peerGroup.POST("/:id/disconnect", nodeService.handleDisconnectPeer)
// Allowlist management
peerGroup.GET("/auth/mode", ns.handleGetAuthMode)
peerGroup.PUT("/auth/mode", ns.handleSetAuthMode)
peerGroup.GET("/auth/allowlist", ns.handleListAllowlist)
peerGroup.POST("/auth/allowlist", ns.handleAddToAllowlist)
peerGroup.DELETE("/auth/allowlist/:key", ns.handleRemoveFromAllowlist)
peerGroup.GET("/auth/mode", nodeService.handleGetAuthMode)
peerGroup.PUT("/auth/mode", nodeService.handleSetAuthMode)
peerGroup.GET("/auth/allowlist", nodeService.handleListAllowlist)
peerGroup.POST("/auth/allowlist", nodeService.handleAddToAllowlist)
peerGroup.DELETE("/auth/allowlist/:key", nodeService.handleRemoveFromAllowlist)
}
// Remote operations endpoints
remoteGroup := router.Group("/remote")
{
remoteGroup.GET("/stats", ns.handleRemoteStats)
remoteGroup.GET("/:peerId/stats", ns.handlePeerStats)
remoteGroup.POST("/:peerId/start", ns.handleRemoteStart)
remoteGroup.POST("/:peerId/stop", ns.handleRemoteStop)
remoteGroup.GET("/:peerId/logs/:miner", ns.handleRemoteLogs)
remoteGroup.GET("/stats", nodeService.handleRemoteStats)
remoteGroup.GET("/:peerId/stats", nodeService.handlePeerStats)
remoteGroup.POST("/:peerId/start", nodeService.handleRemoteStart)
remoteGroup.POST("/:peerId/stop", nodeService.handleRemoteStop)
remoteGroup.GET("/:peerId/logs/:miner", nodeService.handleRemoteLogs)
}
}
// if err := ns.StartTransport(); err != nil { log.Fatal(err) } // starts WebSocket listener on configured port
func (ns *NodeService) StartTransport() error {
return ns.transport.Start()
// if err := nodeService.StartTransport(); err != nil { log.Fatal(err) } // starts WebSocket listener on configured port
func (nodeService *NodeService) StartTransport() error {
return nodeService.transport.Start()
}
// defer ns.StopTransport() // gracefully shuts down WebSocket listener and closes peer connections
func (ns *NodeService) StopTransport() error {
return ns.transport.Stop()
// defer nodeService.StopTransport() // gracefully shuts down WebSocket listener and closes peer connections
func (nodeService *NodeService) StopTransport() error {
return nodeService.transport.Stop()
}
// response := NodeInfoResponse{HasIdentity: true, Identity: identity, RegisteredPeers: 3, ConnectedPeers: 1}
@ -110,15 +110,15 @@ type NodeInfoResponse struct {
// @Produce json
// @Success 200 {object} NodeInfoResponse
// @Router /node/info [get]
func (ns *NodeService) handleNodeInfo(c *gin.Context) {
func (nodeService *NodeService) handleNodeInfo(c *gin.Context) {
response := NodeInfoResponse{
HasIdentity: ns.nodeManager.HasIdentity(),
RegisteredPeers: ns.peerRegistry.Count(),
ConnectedPeers: len(ns.peerRegistry.GetConnectedPeers()),
HasIdentity: nodeService.nodeManager.HasIdentity(),
RegisteredPeers: nodeService.peerRegistry.Count(),
ConnectedPeers: len(nodeService.peerRegistry.GetConnectedPeers()),
}
if ns.nodeManager.HasIdentity() {
response.Identity = ns.nodeManager.GetIdentity()
if nodeService.nodeManager.HasIdentity() {
response.Identity = nodeService.nodeManager.GetIdentity()
}
c.JSON(http.StatusOK, response)
@ -139,20 +139,20 @@ type NodeInitRequest struct {
// @Param request body NodeInitRequest true "Node initialization parameters"
// @Success 200 {object} node.NodeIdentity
// @Router /node/init [post]
func (ns *NodeService) handleNodeInit(c *gin.Context) {
var req NodeInitRequest
if err := c.ShouldBindJSON(&req); err != nil {
func (nodeService *NodeService) handleNodeInit(c *gin.Context) {
var request NodeInitRequest
if err := c.ShouldBindJSON(&request); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if ns.nodeManager.HasIdentity() {
if nodeService.nodeManager.HasIdentity() {
c.JSON(http.StatusConflict, gin.H{"error": "node identity already exists"})
return
}
role := node.RoleDual
switch req.Role {
switch request.Role {
case "controller":
role = node.RoleController
case "worker":
@ -164,12 +164,12 @@ func (ns *NodeService) handleNodeInit(c *gin.Context) {
return
}
if err := ns.nodeManager.GenerateIdentity(req.Name, role); err != nil {
if err := nodeService.nodeManager.GenerateIdentity(request.Name, role); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, ns.nodeManager.GetIdentity())
c.JSON(http.StatusOK, nodeService.nodeManager.GetIdentity())
}
// handleListPeers godoc
@ -179,8 +179,8 @@ func (ns *NodeService) handleNodeInit(c *gin.Context) {
// @Produce json
// @Success 200 {array} node.Peer
// @Router /peers [get]
func (ns *NodeService) handleListPeers(c *gin.Context) {
peers := ns.peerRegistry.ListPeers()
func (nodeService *NodeService) handleListPeers(c *gin.Context) {
peers := nodeService.peerRegistry.ListPeers()
c.JSON(http.StatusOK, peers)
}
@ -199,22 +199,22 @@ type AddPeerRequest struct {
// @Param request body AddPeerRequest true "Peer information"
// @Success 201 {object} node.Peer
// @Router /peers [post]
func (ns *NodeService) handleAddPeer(c *gin.Context) {
var req AddPeerRequest
if err := c.ShouldBindJSON(&req); err != nil {
func (nodeService *NodeService) handleAddPeer(c *gin.Context) {
var request AddPeerRequest
if err := c.ShouldBindJSON(&request); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
peer := &node.Peer{
ID: "pending-" + req.Address, // Will be updated on handshake
Name: req.Name,
Address: req.Address,
ID: "pending-" + request.Address, // Will be updated on handshake
Name: request.Name,
Address: request.Address,
Role: node.RoleDual,
Score: 50,
}
if err := ns.peerRegistry.AddPeer(peer); err != nil {
if err := nodeService.peerRegistry.AddPeer(peer); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
@ -230,9 +230,9 @@ func (ns *NodeService) handleAddPeer(c *gin.Context) {
// @Param id path string true "Peer ID"
// @Success 200 {object} node.Peer
// @Router /peers/{id} [get]
func (ns *NodeService) handleGetPeer(c *gin.Context) {
func (nodeService *NodeService) handleGetPeer(c *gin.Context) {
peerID := c.Param("id")
peer := ns.peerRegistry.GetPeer(peerID)
peer := nodeService.peerRegistry.GetPeer(peerID)
if peer == nil {
c.JSON(http.StatusNotFound, gin.H{"error": "peer not found"})
return
@ -248,9 +248,9 @@ func (ns *NodeService) handleGetPeer(c *gin.Context) {
// @Param id path string true "Peer ID"
// @Success 200 {object} map[string]string
// @Router /peers/{id} [delete]
func (ns *NodeService) handleRemovePeer(c *gin.Context) {
func (nodeService *NodeService) handleRemovePeer(c *gin.Context) {
peerID := c.Param("id")
if err := ns.peerRegistry.RemovePeer(peerID); err != nil {
if err := nodeService.peerRegistry.RemovePeer(peerID); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
@ -266,9 +266,9 @@ func (ns *NodeService) handleRemovePeer(c *gin.Context) {
// @Success 200 {object} map[string]float64
// @Failure 404 {object} APIError "Peer not found"
// @Router /peers/{id}/ping [post]
func (ns *NodeService) handlePingPeer(c *gin.Context) {
func (nodeService *NodeService) handlePingPeer(c *gin.Context) {
peerID := c.Param("id")
rtt, err := ns.controller.PingPeer(peerID)
rtt, err := nodeService.controller.PingPeer(peerID)
if err != nil {
if containsStr(err.Error(), "not found") || containsStr(err.Error(), "not connected") {
respondWithError(c, http.StatusNotFound, "PEER_NOT_FOUND", "peer not found or not connected", err.Error())
@ -289,9 +289,9 @@ func (ns *NodeService) handlePingPeer(c *gin.Context) {
// @Success 200 {object} map[string]string
// @Failure 404 {object} APIError "Peer not found"
// @Router /peers/{id}/connect [post]
func (ns *NodeService) handleConnectPeer(c *gin.Context) {
func (nodeService *NodeService) handleConnectPeer(c *gin.Context) {
peerID := c.Param("id")
if err := ns.controller.ConnectToPeer(peerID); err != nil {
if err := nodeService.controller.ConnectToPeer(peerID); err != nil {
if containsStr(err.Error(), "not found") {
respondWithError(c, http.StatusNotFound, "PEER_NOT_FOUND", "peer not found", err.Error())
return
@ -310,9 +310,9 @@ func (ns *NodeService) handleConnectPeer(c *gin.Context) {
// @Param id path string true "Peer ID"
// @Success 200 {object} map[string]string
// @Router /peers/{id}/disconnect [post]
func (ns *NodeService) handleDisconnectPeer(c *gin.Context) {
func (nodeService *NodeService) handleDisconnectPeer(c *gin.Context) {
peerID := c.Param("id")
if err := ns.controller.DisconnectFromPeer(peerID); err != nil {
if err := nodeService.controller.DisconnectFromPeer(peerID); err != nil {
// Make disconnect idempotent - if peer not connected, still return success
if containsStr(err.Error(), "not connected") {
c.JSON(http.StatusOK, gin.H{"status": "disconnected"})
@ -331,8 +331,8 @@ func (ns *NodeService) handleDisconnectPeer(c *gin.Context) {
// @Produce json
// @Success 200 {object} map[string]node.StatsPayload
// @Router /remote/stats [get]
func (ns *NodeService) handleRemoteStats(c *gin.Context) {
stats := ns.controller.GetAllStats()
func (nodeService *NodeService) handleRemoteStats(c *gin.Context) {
stats := nodeService.controller.GetAllStats()
c.JSON(http.StatusOK, stats)
}
@ -344,9 +344,9 @@ func (ns *NodeService) handleRemoteStats(c *gin.Context) {
// @Param peerId path string true "Peer ID"
// @Success 200 {object} node.StatsPayload
// @Router /remote/{peerId}/stats [get]
func (ns *NodeService) handlePeerStats(c *gin.Context) {
func (nodeService *NodeService) handlePeerStats(c *gin.Context) {
peerID := c.Param("peerId")
stats, err := ns.controller.GetRemoteStats(peerID)
stats, err := nodeService.controller.GetRemoteStats(peerID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
@ -371,15 +371,15 @@ type RemoteStartRequest struct {
// @Param request body RemoteStartRequest true "Start parameters"
// @Success 200 {object} map[string]string
// @Router /remote/{peerId}/start [post]
func (ns *NodeService) handleRemoteStart(c *gin.Context) {
func (nodeService *NodeService) handleRemoteStart(c *gin.Context) {
peerID := c.Param("peerId")
var req RemoteStartRequest
if err := c.ShouldBindJSON(&req); err != nil {
var request RemoteStartRequest
if err := c.ShouldBindJSON(&request); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if err := ns.controller.StartRemoteMiner(peerID, req.MinerType, req.ProfileID, req.Config); err != nil {
if err := nodeService.controller.StartRemoteMiner(peerID, request.MinerType, request.ProfileID, request.Config); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
@ -401,15 +401,15 @@ type RemoteStopRequest struct {
// @Param request body RemoteStopRequest true "Stop parameters"
// @Success 200 {object} map[string]string
// @Router /remote/{peerId}/stop [post]
func (ns *NodeService) handleRemoteStop(c *gin.Context) {
func (nodeService *NodeService) handleRemoteStop(c *gin.Context) {
peerID := c.Param("peerId")
var req RemoteStopRequest
if err := c.ShouldBindJSON(&req); err != nil {
var request RemoteStopRequest
if err := c.ShouldBindJSON(&request); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if err := ns.controller.StopRemoteMiner(peerID, req.MinerName); err != nil {
if err := nodeService.controller.StopRemoteMiner(peerID, request.MinerName); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
@ -426,7 +426,7 @@ func (ns *NodeService) handleRemoteStop(c *gin.Context) {
// @Param lines query int false "Number of lines (max 10000)" default(100)
// @Success 200 {array} string
// @Router /remote/{peerId}/logs/{miner} [get]
func (ns *NodeService) handleRemoteLogs(c *gin.Context) {
func (nodeService *NodeService) handleRemoteLogs(c *gin.Context) {
peerID := c.Param("peerId")
minerName := c.Param("miner")
lines := 100
@ -440,7 +440,7 @@ func (ns *NodeService) handleRemoteLogs(c *gin.Context) {
}
}
logs, err := ns.controller.GetRemoteLogs(peerID, minerName, lines)
logs, err := nodeService.controller.GetRemoteLogs(peerID, minerName, lines)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
@ -460,8 +460,8 @@ type AuthModeResponse struct {
// @Produce json
// @Success 200 {object} AuthModeResponse
// @Router /peers/auth/mode [get]
func (ns *NodeService) handleGetAuthMode(c *gin.Context) {
mode := ns.peerRegistry.GetAuthMode()
func (nodeService *NodeService) handleGetAuthMode(c *gin.Context) {
mode := nodeService.peerRegistry.GetAuthMode()
modeStr := "open"
if mode == node.PeerAuthAllowlist {
modeStr = "allowlist"
@ -484,15 +484,15 @@ type SetAuthModeRequest struct {
// @Success 200 {object} AuthModeResponse
// @Failure 400 {object} APIError "Invalid mode"
// @Router /peers/auth/mode [put]
func (ns *NodeService) handleSetAuthMode(c *gin.Context) {
var req SetAuthModeRequest
if err := c.ShouldBindJSON(&req); err != nil {
func (nodeService *NodeService) handleSetAuthMode(c *gin.Context) {
var request SetAuthModeRequest
if err := c.ShouldBindJSON(&request); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
var mode node.PeerAuthMode
switch req.Mode {
switch request.Mode {
case "open":
mode = node.PeerAuthOpen
case "allowlist":
@ -502,8 +502,8 @@ func (ns *NodeService) handleSetAuthMode(c *gin.Context) {
return
}
ns.peerRegistry.SetAuthMode(mode)
c.JSON(http.StatusOK, AuthModeResponse{Mode: req.Mode})
nodeService.peerRegistry.SetAuthMode(mode)
c.JSON(http.StatusOK, AuthModeResponse{Mode: request.Mode})
}
// GET /peers/auth/allowlist → {"publicKeys": ["ed25519:abc...", "ed25519:def..."]}
@ -518,8 +518,8 @@ type AllowlistResponse struct {
// @Produce json
// @Success 200 {object} AllowlistResponse
// @Router /peers/auth/allowlist [get]
func (ns *NodeService) handleListAllowlist(c *gin.Context) {
keys := ns.peerRegistry.ListAllowedPublicKeys()
func (nodeService *NodeService) handleListAllowlist(c *gin.Context) {
keys := nodeService.peerRegistry.ListAllowedPublicKeys()
c.JSON(http.StatusOK, AllowlistResponse{PublicKeys: keys})
}
@ -538,19 +538,19 @@ type AddAllowlistRequest struct {
// @Success 201 {object} map[string]string
// @Failure 400 {object} APIError "Invalid request"
// @Router /peers/auth/allowlist [post]
func (ns *NodeService) handleAddToAllowlist(c *gin.Context) {
var req AddAllowlistRequest
if err := c.ShouldBindJSON(&req); err != nil {
func (nodeService *NodeService) handleAddToAllowlist(c *gin.Context) {
var request AddAllowlistRequest
if err := c.ShouldBindJSON(&request); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if len(req.PublicKey) < 16 {
if len(request.PublicKey) < 16 {
respondWithError(c, http.StatusBadRequest, "INVALID_KEY", "public key too short", "")
return
}
ns.peerRegistry.AllowPublicKey(req.PublicKey)
nodeService.peerRegistry.AllowPublicKey(request.PublicKey)
c.JSON(http.StatusCreated, gin.H{"status": "added"})
}
@ -562,13 +562,13 @@ func (ns *NodeService) handleAddToAllowlist(c *gin.Context) {
// @Param key path string true "Public key to remove (URL-encoded)"
// @Success 200 {object} map[string]string
// @Router /peers/auth/allowlist/{key} [delete]
func (ns *NodeService) handleRemoveFromAllowlist(c *gin.Context) {
func (nodeService *NodeService) handleRemoveFromAllowlist(c *gin.Context) {
key := c.Param("key")
if key == "" {
respondWithError(c, http.StatusBadRequest, "MISSING_KEY", "public key required", "")
return
}
ns.peerRegistry.RevokePublicKey(key)
nodeService.peerRegistry.RevokePublicKey(key)
c.JSON(http.StatusOK, gin.H{"status": "removed"})
}

View file

@ -17,7 +17,7 @@ type Repository[T any] interface {
Save(data T) error
// repo.Update(func(d *T) error { d.Field = value; return nil })
Update(fn func(*T) error) error
Update(modifier func(*T) error) error
}
// repo := NewFileRepository[MinersConfig](path, WithDefaults(defaultMinersConfig))
@ -32,9 +32,9 @@ type FileRepository[T any] struct {
type FileRepositoryOption[T any] func(*FileRepository[T])
// repo := NewFileRepository[MinersConfig](path, WithDefaults(defaultMinersConfig))
func WithDefaults[T any](fn func() T) FileRepositoryOption[T] {
func WithDefaults[T any](defaultsProvider func() T) FileRepositoryOption[T] {
return func(repo *FileRepository[T]) {
repo.defaults = fn
repo.defaults = defaultsProvider
}
}
@ -102,7 +102,7 @@ func (repository *FileRepository[T]) saveUnlocked(data T) error {
// configuration.Miners = append(configuration.Miners, entry)
// return nil
// })
func (repository *FileRepository[T]) Update(fn func(*T) error) error {
func (repository *FileRepository[T]) Update(modifier func(*T) error) error {
repository.mutex.Lock()
defer repository.mutex.Unlock()
@ -124,7 +124,7 @@ func (repository *FileRepository[T]) Update(fn func(*T) error) error {
}
// Apply modification
if err := fn(&data); err != nil {
if err := modifier(&data); err != nil {
return err
}

View file

@ -12,7 +12,7 @@ type testData struct {
Value int `json:"value"`
}
func TestFileRepository_Load(t *testing.T) {
func TestFileRepository_Load_Good(t *testing.T) {
t.Run("NonExistentFile", func(t *testing.T) {
tmpDir := t.TempDir()
path := filepath.Join(tmpDir, "nonexistent.json")
@ -78,7 +78,7 @@ func TestFileRepository_Load(t *testing.T) {
})
}
func TestFileRepository_Save(t *testing.T) {
func TestFileRepository_Save_Good(t *testing.T) {
t.Run("NewFile", func(t *testing.T) {
tmpDir := t.TempDir()
path := filepath.Join(tmpDir, "subdir", "new.json")
@ -130,7 +130,7 @@ func TestFileRepository_Save(t *testing.T) {
})
}
func TestFileRepository_Update(t *testing.T) {
func TestFileRepository_Update_Good(t *testing.T) {
t.Run("UpdateExisting", func(t *testing.T) {
tmpDir := t.TempDir()
path := filepath.Join(tmpDir, "update.json")
@ -215,7 +215,7 @@ func TestFileRepository_Update(t *testing.T) {
})
}
func TestFileRepository_Delete(t *testing.T) {
func TestFileRepository_Delete_Good(t *testing.T) {
tmpDir := t.TempDir()
path := filepath.Join(tmpDir, "delete.json")
repo := NewFileRepository[testData](path)
@ -244,7 +244,7 @@ func TestFileRepository_Delete(t *testing.T) {
}
}
func TestFileRepository_Path(t *testing.T) {
func TestFileRepository_Path_Good(t *testing.T) {
path := "/some/path/config.json"
repo := NewFileRepository[testData](path)
@ -253,7 +253,7 @@ func TestFileRepository_Path(t *testing.T) {
}
}
func TestFileRepository_UpdateWithLoadError(t *testing.T) {
func TestFileRepository_UpdateWithLoadError_Bad(t *testing.T) {
tmpDir := t.TempDir()
path := filepath.Join(tmpDir, "corrupt.json")
repo := NewFileRepository[testData](path)
@ -273,7 +273,7 @@ func TestFileRepository_UpdateWithLoadError(t *testing.T) {
}
}
func TestFileRepository_SaveToReadOnlyDirectory(t *testing.T) {
func TestFileRepository_SaveToReadOnlyDirectory_Bad(t *testing.T) {
if os.Getuid() == 0 {
t.Skip("Test skipped when running as root")
}
@ -295,7 +295,7 @@ func TestFileRepository_SaveToReadOnlyDirectory(t *testing.T) {
}
}
func TestFileRepository_DeleteNonExistent(t *testing.T) {
func TestFileRepository_DeleteNonExistent_Good(t *testing.T) {
tmpDir := t.TempDir()
path := filepath.Join(tmpDir, "nonexistent.json")
repo := NewFileRepository[testData](path)
@ -306,7 +306,7 @@ func TestFileRepository_DeleteNonExistent(t *testing.T) {
}
}
func TestFileRepository_ExistsOnInvalidPath(t *testing.T) {
func TestFileRepository_ExistsOnInvalidPath_Bad(t *testing.T) {
// Use a path that definitely doesn't exist
repo := NewFileRepository[testData]("/nonexistent/path/to/file.json")
@ -315,7 +315,7 @@ func TestFileRepository_ExistsOnInvalidPath(t *testing.T) {
}
}
func TestFileRepository_ConcurrentUpdates(t *testing.T) {
func TestFileRepository_ConcurrentUpdates_Ugly(t *testing.T) {
tmpDir := t.TempDir()
path := filepath.Join(tmpDir, "concurrent.json")
repo := NewFileRepository[testData](path, WithDefaults(func() testData {
@ -354,8 +354,9 @@ func TestFileRepository_ConcurrentUpdates(t *testing.T) {
}
}
// Test with slice data
func TestFileRepository_SliceData(t *testing.T) {
// repo := NewFileRepository[[]item](path, WithDefaults(func() []item { return []item{} }))
// repo.Save(items); repo.Update(func(data *[]item) error { *data = append(*data, item{...}); return nil })
func TestFileRepository_SliceData_Good(t *testing.T) {
type item struct {
ID string `json:"id"`
Name string `json:"name"`

View file

@ -73,7 +73,7 @@ func sanitizeErrorDetails(details string) string {
// respondWithError(c, http.StatusNotFound, ErrCodeMinerNotFound, "xmrig not found", err.Error())
func respondWithError(c *gin.Context, status int, code string, message string, details string) {
apiErr := APIError{
apiError := APIError{
Code: code,
Message: message,
Details: sanitizeErrorDetails(details),
@ -83,21 +83,21 @@ func respondWithError(c *gin.Context, status int, code string, message string, d
// Add suggestions based on error code
switch code {
case ErrCodeMinerNotFound:
apiErr.Suggestion = "Check the miner name or install the miner first"
apiError.Suggestion = "Check the miner name or install the miner first"
case ErrCodeProfileNotFound:
apiErr.Suggestion = "Create a new profile or check the profile ID"
apiError.Suggestion = "Create a new profile or check the profile ID"
case ErrCodeInstallFailed:
apiErr.Suggestion = "Check your internet connection and try again"
apiError.Suggestion = "Check your internet connection and try again"
case ErrCodeStartFailed:
apiErr.Suggestion = "Check the miner configuration and logs"
apiError.Suggestion = "Check the miner configuration and logs"
case ErrCodeInvalidInput:
apiErr.Suggestion = "Verify the request body matches the expected format"
apiError.Suggestion = "Verify the request body matches the expected format"
case ErrCodeServiceUnavailable:
apiErr.Suggestion = "The service is temporarily unavailable, try again later"
apiErr.Retryable = true
apiError.Suggestion = "The service is temporarily unavailable, try again later"
apiError.Retryable = true
}
c.JSON(status, apiErr)
c.JSON(status, apiError)
}
// respondWithMiningError(c, ErrMinerNotFound("xmrig"))
@ -114,7 +114,7 @@ func respondWithMiningError(c *gin.Context, err *MiningError) {
details += err.Details
}
apiErr := APIError{
apiError := APIError{
Code: err.Code,
Message: err.Message,
Details: sanitizeErrorDetails(details),
@ -122,7 +122,7 @@ func respondWithMiningError(c *gin.Context, err *MiningError) {
Retryable: err.Retryable,
}
c.JSON(err.StatusCode(), apiErr)
c.JSON(err.StatusCode(), apiError)
}
// isRetryableError(http.StatusServiceUnavailable) // => true
@ -220,8 +220,8 @@ func logWithRequestID(c *gin.Context, level string, message string, fields loggi
if fields == nil {
fields = logging.Fields{}
}
if reqID := getRequestID(c); reqID != "" {
fields["request_id"] = reqID
if requestID := getRequestID(c); requestID != "" {
fields["request_id"] = requestID
}
switch level {
case "error":

View file

@ -165,11 +165,11 @@ func (settingsManager *SettingsManager) Get() *AppSettings {
}
// settingsManager.Update(func(settings *AppSettings) { settings.Theme = "dark" })
func (settingsManager *SettingsManager) Update(fn func(*AppSettings)) error {
func (settingsManager *SettingsManager) Update(modifier func(*AppSettings)) error {
settingsManager.mutex.Lock()
defer settingsManager.mutex.Unlock()
fn(settingsManager.settings)
modifier(settingsManager.settings)
data, err := json.MarshalIndent(settingsManager.settings, "", " ")
if err != nil {

View file

@ -29,23 +29,23 @@ func FetchJSONStats[T any](ctx context.Context, config HTTPStatsConfig, target *
requestURL := "http://" + config.Host + ":" + strconv.Itoa(config.Port) + config.Endpoint
req, err := http.NewRequestWithContext(ctx, "GET", requestURL, nil)
httpRequest, err := http.NewRequestWithContext(ctx, "GET", requestURL, nil)
if err != nil {
return ErrInternal("failed to create request").WithCause(err)
}
resp, err := getHTTPClient().Do(req)
response, err := getHTTPClient().Do(httpRequest)
if err != nil {
return ErrInternal("HTTP request failed").WithCause(err)
}
defer resp.Body.Close()
defer response.Body.Close()
if resp.StatusCode != http.StatusOK {
io.Copy(io.Discard, resp.Body) // Drain body to allow connection reuse
return ErrInternal("unexpected status code: " + strconv.Itoa(resp.StatusCode))
if response.StatusCode != http.StatusOK {
io.Copy(io.Discard, response.Body) // Drain body to allow connection reuse
return ErrInternal("unexpected status code: " + strconv.Itoa(response.StatusCode))
}
body, err := io.ReadAll(resp.Body)
body, err := io.ReadAll(response.Body)
if err != nil {
return ErrInternal("failed to read response body").WithCause(err)
}

View file

@ -148,14 +148,14 @@ func (m *TTMiner) CheckInstallation() (*InstallationDetails, error) {
// Run version command before acquiring lock (I/O operation)
cmd := exec.Command(binaryPath, "--version")
var out bytes.Buffer
cmd.Stdout = &out
var commandOutput bytes.Buffer
cmd.Stdout = &commandOutput
var version string
if err := cmd.Run(); err != nil {
version = "Unknown (could not run executable)"
} else {
// Parse version from output
output := strings.TrimSpace(out.String())
output := strings.TrimSpace(commandOutput.String())
fields := strings.Fields(output)
if len(fields) >= 2 {
version = fields[1]

View file

@ -24,12 +24,12 @@ func (m *TTMiner) GetStats(ctx context.Context) (*PerformanceMetrics, error) {
m.mutex.RUnlock()
// Create request with context and timeout
reqCtx, cancel := context.WithTimeout(ctx, statsTimeout)
requestContext, cancel := context.WithTimeout(ctx, statsTimeout)
defer cancel()
// Use the common HTTP stats fetcher
var summary TTMinerSummary
if err := FetchJSONStats(reqCtx, config, &summary); err != nil {
if err := FetchJSONStats(requestContext, config, &summary); err != nil {
return nil, err
}

View file

@ -66,18 +66,18 @@ func FetchLatestGitHubVersion(owner, repo string) (string, error) {
func fetchGitHubVersionDirect(owner, repo string) (string, error) {
url := "https://api.github.com/repos/" + owner + "/" + repo + "/releases/latest"
resp, err := getHTTPClient().Get(url)
response, err := getHTTPClient().Get(url)
if err != nil {
return "", ErrInternal("failed to fetch version").WithCause(err)
}
defer resp.Body.Close()
defer response.Body.Close()
if resp.StatusCode != http.StatusOK {
io.Copy(io.Discard, resp.Body) // Drain body to allow connection reuse
return "", ErrInternal("failed to get latest release: unexpected status code " + strconv.Itoa(resp.StatusCode))
if response.StatusCode != http.StatusOK {
io.Copy(io.Discard, response.Body) // Drain body to allow connection reuse
return "", ErrInternal("failed to get latest release: unexpected status code " + strconv.Itoa(response.StatusCode))
}
body, err := io.ReadAll(resp.Body)
body, err := io.ReadAll(response.Body)
if err != nil {
return "", ErrInternal("failed to read release response").WithCause(err)
}

View file

@ -159,13 +159,13 @@ func (m *XMRigMiner) CheckInstallation() (*InstallationDetails, error) {
// Run version command before acquiring lock (I/O operation)
cmd := exec.Command(binaryPath, "--version")
var out bytes.Buffer
cmd.Stdout = &out
var output bytes.Buffer
cmd.Stdout = &output
var version string
if err := cmd.Run(); err != nil {
version = "Unknown (could not run executable)"
} else {
fields := strings.Fields(out.String())
fields := strings.Fields(output.String())
if len(fields) >= 2 {
version = fields[1]
} else {

View file

@ -5,7 +5,7 @@ import (
"time"
)
// reqCtx, cancel := context.WithTimeout(ctx, statsTimeout)
// requestContext, cancel := context.WithTimeout(ctx, statsTimeout)
const statsTimeout = 5 * time.Second
// metrics, err := miner.GetStats(ctx)
@ -29,12 +29,12 @@ func (m *XMRigMiner) GetStats(ctx context.Context) (*PerformanceMetrics, error)
m.mutex.RUnlock()
// Create request with context and timeout
reqCtx, cancel := context.WithTimeout(ctx, statsTimeout)
requestContext, cancel := context.WithTimeout(ctx, statsTimeout)
defer cancel()
// Use the common HTTP stats fetcher
var summary XMRigSummary
if err := FetchJSONStats(reqCtx, config, &summary); err != nil {
if err := FetchJSONStats(requestContext, config, &summary); err != nil {
return nil, err
}