diff --git a/docs/api/swagger.md b/docs/api/swagger.md new file mode 100644 index 0000000..ce15bf5 --- /dev/null +++ b/docs/api/swagger.md @@ -0,0 +1,62 @@ +# Swagger Documentation + +Interactive API documentation is available when running the Mining service. + +## Accessing Swagger UI + +When the service is running, you can access the Swagger UI at: + +``` +http://localhost:9090/api/v1/mining/swagger/index.html +``` + +## Starting the Service + +=== "CLI" + + ```bash + miner-ctrl serve + ``` + +=== "Development" + + ```bash + make dev + ``` + +## OpenAPI Specification + +The OpenAPI 3.0 specification is available at: + +- **JSON**: `http://localhost:9090/api/v1/mining/swagger/doc.json` +- **YAML**: `http://localhost:9090/api/v1/mining/swagger/doc.yaml` + +## Regenerating Docs + +After modifying API endpoints, regenerate the Swagger documentation: + +```bash +make docs +``` + +This runs `swag init` to parse the Go annotations and update the specification files. + +## API Annotations + +API documentation is generated from Go comments using [swaggo/swag](https://github.com/swaggo/swag). Example: + +```go +// StartMiner godoc +// @Summary Start a miner +// @Description Start mining with a specific profile +// @Tags miners +// @Accept json +// @Produce json +// @Param profile_id path string true "Profile ID" +// @Success 200 {object} MinerResponse +// @Failure 400 {object} ErrorResponse +// @Router /miners/{profile_id}/start [post] +func (s *Service) StartMiner(c *gin.Context) { + // ... +} +``` diff --git a/mkdocs.yml b/mkdocs.yml index a8b7dee..01bcebf 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -150,7 +150,7 @@ nav: - API Reference: - Overview: api/index.md - Endpoints: api/endpoints.md - - Swagger Docs: swagger.json + - Swagger Docs: api/swagger.md - Development: - Setup: development/index.md diff --git a/pkg/mining/miner.go b/pkg/mining/miner.go index a0d7033..08ffa5b 100644 --- a/pkg/mining/miner.go +++ b/pkg/mining/miner.go @@ -144,26 +144,42 @@ func (b *BaseMiner) Stop() error { b.stdinPipe = nil } + // Helper to clean up state + cleanup := func() { + b.Running = false + b.cmd = 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) + done := make(chan struct{}) go func() { - _, err := b.cmd.Process.Wait() - done <- err + b.cmd.Process.Wait() + close(done) }() select { case <-done: + cleanup() return nil case <-time.After(3 * time.Second): - // Process didn't exit, force kill + // Process didn't exit gracefully, force kill below } } } - return b.cmd.Process.Kill() + // Force kill and wait for process to exit + if err := b.cmd.Process.Kill(); err != nil { + cleanup() + return err + } + + // Wait for process to fully terminate to avoid zombies + b.cmd.Process.Wait() + cleanup() + return nil } // WriteStdin sends input to the miner's stdin (for console commands).