fix: Address medium severity code quality issues
- Fix deprecated strings.Title usage with golang.org/x/text/cases - Replace log.Fatalf in service startup with channel-based error handling - Add graceful SIGTERM before SIGKILL in Stop() for proper cleanup - Add mutex protection for LogBuffer access in GetLogs() - Add instance name sanitization with regex to prevent injection - Add error logging in updateInstallationCache for failed operations 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
68c0033c55
commit
f2afdeeb82
6 changed files with 82 additions and 95 deletions
|
|
@ -12,6 +12,8 @@ import (
|
|||
|
||||
"github.com/Snider/Mining/pkg/mining"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/text/cases"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -121,7 +123,7 @@ var serveCmd = &cobra.Command{
|
|||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error getting miner stats: %v\n", err)
|
||||
} else {
|
||||
fmt.Printf("Miner Status for %s:\n", strings.Title(minerName))
|
||||
fmt.Printf("Miner Status for %s:\n", cases.Title(language.English).String(minerName))
|
||||
fmt.Printf(" Hash Rate: %d H/s\n", stats.Hashrate)
|
||||
fmt.Printf(" Shares: %d\n", stats.Shares)
|
||||
fmt.Printf(" Rejected: %d\n", stats.Rejected)
|
||||
|
|
|
|||
|
|
@ -2,9 +2,10 @@ package cmd
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/text/cases"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
// statusCmd represents the status command
|
||||
|
|
@ -27,7 +28,7 @@ var statusCmd = &cobra.Command{
|
|||
return fmt.Errorf("failed to get miner stats: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Miner Status for %s:\n", strings.Title(minerName))
|
||||
fmt.Printf("Miner Status for %s:\n", cases.Title(language.English).String(minerName))
|
||||
fmt.Printf(" Hash Rate: %d H/s\n", stats.Hashrate)
|
||||
fmt.Printf(" Shares: %d\n", stats.Shares)
|
||||
fmt.Printf(" Rejected: %d\n", stats.Rejected)
|
||||
|
|
|
|||
107
mkdocs.yml
107
mkdocs.yml
|
|
@ -139,94 +139,33 @@ nav:
|
|||
- Home: index.md
|
||||
|
||||
- Getting Started:
|
||||
- Introduction: getting-started/introduction.md
|
||||
- Installation: getting-started/installation.md
|
||||
- Quick Start: getting-started/quickstart.md
|
||||
- Configuration: getting-started/configuration.md
|
||||
- First Mining Session: getting-started/first-session.md
|
||||
- Installation: getting-started/index.md
|
||||
- Quick Start: getting-started/quick-start.md
|
||||
|
||||
- CLI Reference:
|
||||
- Overview: cli/overview.md
|
||||
- serve: cli/serve.md
|
||||
- start: cli/start.md
|
||||
- stop: cli/stop.md
|
||||
- status: cli/status.md
|
||||
- install: cli/install.md
|
||||
- uninstall: cli/uninstall.md
|
||||
- update: cli/update.md
|
||||
- list: cli/list.md
|
||||
- doctor: cli/doctor.md
|
||||
- profiles: cli/profiles.md
|
||||
- User Guide:
|
||||
- CLI: user-guide/cli.md
|
||||
- Web Dashboard: user-guide/web-dashboard.md
|
||||
- Desktop App: user-guide/desktop-app.md
|
||||
|
||||
- API Reference:
|
||||
- Overview: api/overview.md
|
||||
- Authentication: api/authentication.md
|
||||
- System Endpoints: api/system.md
|
||||
- Miner Endpoints: api/miners.md
|
||||
- Profile Endpoints: api/profiles.md
|
||||
- Stats Endpoints: api/stats.md
|
||||
- Swagger UI: api/swagger.md
|
||||
- Error Handling: api/errors.md
|
||||
- Overview: api/index.md
|
||||
- Endpoints: api/endpoints.md
|
||||
- Swagger Docs: swagger.json
|
||||
|
||||
- Web Dashboard:
|
||||
- Overview: dashboard/overview.md
|
||||
- Installation: dashboard/installation.md
|
||||
- Web Component: dashboard/web-component.md
|
||||
- Features: dashboard/features.md
|
||||
- Configuration: dashboard/configuration.md
|
||||
- Customization: dashboard/customization.md
|
||||
|
||||
- Desktop Application:
|
||||
- Overview: desktop/overview.md
|
||||
- Installation: desktop/installation.md
|
||||
- Building from Source: desktop/building.md
|
||||
- Platform Support: desktop/platforms.md
|
||||
- Configuration: desktop/configuration.md
|
||||
- Troubleshooting: desktop/troubleshooting.md
|
||||
|
||||
- Development Guide:
|
||||
- Setup: development/setup.md
|
||||
- Project Structure: development/structure.md
|
||||
- Building: development/building.md
|
||||
- Testing: development/testing.md
|
||||
- E2E Tests: development/e2e-tests.md
|
||||
- Code Style: development/code-style.md
|
||||
- Development:
|
||||
- Setup: development/index.md
|
||||
- Architecture: development/architecture.md
|
||||
- Contributing: development/contributing.md
|
||||
- Release Process: development/releases.md
|
||||
|
||||
- Architecture:
|
||||
- Overview: architecture/overview.md
|
||||
- Go Backend: architecture/backend.md
|
||||
- Miner Interface: architecture/miner-interface.md
|
||||
- Manager System: architecture/manager.md
|
||||
- REST Service: architecture/rest-service.md
|
||||
- XMRig Implementation: architecture/xmrig.md
|
||||
- Profile System: architecture/profiles.md
|
||||
- Stats Collection: architecture/stats.md
|
||||
- XDG Directories: architecture/xdg.md
|
||||
- Reference:
|
||||
- Mining Pools: reference/pools.md
|
||||
- Algorithms: reference/algorithms.md
|
||||
|
||||
- Pool Integration:
|
||||
- Research Overview: pools/research.md
|
||||
- Supported Algorithms: pools/algorithms.md
|
||||
- Stratum Protocol: pools/stratum.md
|
||||
- Pool Configuration: pools/configuration.md
|
||||
- ETChash Pools: pools/etchash.md
|
||||
- ProgPowZ Pools: pools/progpowz.md
|
||||
- Blake3DCR Pools: pools/blake3dcr.md
|
||||
- Custom Pools: pools/custom.md
|
||||
|
||||
- Miners:
|
||||
- Overview: miners/overview.md
|
||||
- XMRig: miners/xmrig.md
|
||||
- Adding New Miners: miners/adding-miners.md
|
||||
|
||||
- Troubleshooting:
|
||||
- Common Issues: troubleshooting/common-issues.md
|
||||
- Logs: troubleshooting/logs.md
|
||||
- Performance: troubleshooting/performance.md
|
||||
- GPU Issues: troubleshooting/gpu.md
|
||||
- Network Issues: troubleshooting/network.md
|
||||
|
||||
- FAQ: faq.md
|
||||
- Changelog: changelog.md
|
||||
- License: license.md
|
||||
- Legacy Docs:
|
||||
- Start Here: 00-START-HERE.md
|
||||
- Quick Reference: QUICK-REFERENCE.md
|
||||
- Pool Research: pool-research.md
|
||||
- Pool Integration: pool-integration-guide.md
|
||||
- Pool README: POOL-RESEARCH-README.md
|
||||
- Files Index: FILES-INDEX.md
|
||||
- Manifest: MANIFEST.md
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
|
@ -12,6 +13,9 @@ import (
|
|||
"github.com/Snider/Mining/pkg/database"
|
||||
)
|
||||
|
||||
// sanitizeInstanceName ensures the instance name only contains safe characters.
|
||||
var instanceNameRegex = regexp.MustCompile(`[^a-zA-Z0-9_/-]`)
|
||||
|
||||
// ManagerInterface defines the contract for a miner manager.
|
||||
type ManagerInterface interface {
|
||||
StartMiner(minerType string, config *Config) (Miner, error)
|
||||
|
|
@ -203,7 +207,9 @@ func (m *Manager) StartMiner(minerType string, config *Config) (Miner, error) {
|
|||
|
||||
instanceName := miner.GetName()
|
||||
if config.Algo != "" {
|
||||
instanceName = fmt.Sprintf("%s-%s", instanceName, config.Algo)
|
||||
// Sanitize algo to prevent directory traversal or invalid filenames
|
||||
sanitizedAlgo := instanceNameRegex.ReplaceAllString(config.Algo, "_")
|
||||
instanceName = fmt.Sprintf("%s-%s", instanceName, sanitizedAlgo)
|
||||
} else {
|
||||
instanceName = fmt.Sprintf("%s-%d", instanceName, time.Now().UnixNano()%1000)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/adrg/xdg"
|
||||
|
|
@ -127,7 +128,8 @@ func (b *BaseMiner) GetBinaryPath() string {
|
|||
return b.MinerBinary
|
||||
}
|
||||
|
||||
// Stop terminates the miner process.
|
||||
// Stop terminates the miner process gracefully.
|
||||
// It first tries SIGTERM to allow cleanup, then SIGKILL if needed.
|
||||
func (b *BaseMiner) Stop() error {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
|
|
@ -142,6 +144,25 @@ func (b *BaseMiner) Stop() error {
|
|||
b.stdinPipe = nil
|
||||
}
|
||||
|
||||
// Try graceful shutdown with SIGTERM first (Unix only)
|
||||
if runtime.GOOS != "windows" {
|
||||
if err := b.cmd.Process.Signal(syscall.SIGTERM); err == nil {
|
||||
// Wait up to 3 seconds for graceful shutdown
|
||||
done := make(chan error, 1)
|
||||
go func() {
|
||||
_, err := b.cmd.Process.Wait()
|
||||
done <- err
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-done:
|
||||
return nil
|
||||
case <-time.After(3 * time.Second):
|
||||
// Process didn't exit, force kill
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return b.cmd.Process.Kill()
|
||||
}
|
||||
|
||||
|
|
@ -371,10 +392,14 @@ func (b *BaseMiner) GetLowResHistoryLength() int {
|
|||
|
||||
// GetLogs returns the captured log output from the miner process.
|
||||
func (b *BaseMiner) GetLogs() []string {
|
||||
if b.LogBuffer == nil {
|
||||
b.mu.RLock()
|
||||
logBuffer := b.LogBuffer
|
||||
b.mu.RUnlock()
|
||||
|
||||
if logBuffer == nil {
|
||||
return []string{}
|
||||
}
|
||||
return b.LogBuffer.GetLines()
|
||||
return logBuffer.GetLines()
|
||||
}
|
||||
|
||||
// ReduceHashrateHistory aggregates and trims hashrate data.
|
||||
|
|
|
|||
|
|
@ -109,9 +109,13 @@ func (s *Service) ServiceStartup(ctx context.Context) error {
|
|||
s.InitRouter()
|
||||
s.Server.Handler = s.Router
|
||||
|
||||
// Channel to capture server startup errors
|
||||
errChan := make(chan error, 1)
|
||||
|
||||
go func() {
|
||||
if err := s.Server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
log.Fatalf("could not listen on %s: %v\n", s.Server.Addr, err)
|
||||
log.Printf("Server error on %s: %v", s.Server.Addr, err)
|
||||
errChan <- err
|
||||
}
|
||||
}()
|
||||
|
||||
|
|
@ -121,11 +125,18 @@ func (s *Service) ServiceStartup(ctx context.Context) error {
|
|||
ctxShutdown, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
if err := s.Server.Shutdown(ctxShutdown); err != nil {
|
||||
log.Fatalf("server shutdown failed: %+v", err)
|
||||
log.Printf("Server shutdown error: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
// Give the server a moment to start and check for immediate errors
|
||||
select {
|
||||
case err := <-errChan:
|
||||
return fmt.Errorf("failed to start server: %w", err)
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
// Server started successfully
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// SetupRoutes configures all API routes on the Gin router.
|
||||
|
|
@ -230,7 +241,10 @@ func (s *Service) updateInstallationCache() (*SystemInfo, error) {
|
|||
default:
|
||||
continue
|
||||
}
|
||||
details, _ := miner.CheckInstallation()
|
||||
details, err := miner.CheckInstallation()
|
||||
if err != nil {
|
||||
log.Printf("Warning: failed to check installation for %s: %v", availableMiner.Name, err)
|
||||
}
|
||||
systemInfo.InstalledMinersInfo = append(systemInfo.InstalledMinersInfo, details)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue