Files
tiny-forge/docs/docker-diagnostic-hints.md
alexei.dolgolyov 670948f113 fix: address code review findings for DNS management
- CRITICAL: Change DNS zones endpoint from GET to POST to avoid
  leaking API token in URL query parameters
- HIGH: Add sync.RWMutex to protect dnsProvider field in Server,
  Deployer, and proxy Manager against concurrent read/write races
- HIGH: Capture old DNS provider reference synchronously before
  launching background cleanup goroutine
- HIGH: Use getDNS()/getDNSProviderLocked() accessors instead of
  direct field reads in all DNS operations
2026-04-02 14:54:15 +03:00

102 lines
5.4 KiB
Markdown

## Feature: Docker Diagnostic Hints on Disconnection
**Problem:** When Docker is unreachable, the UI shows a generic "Docker disconnected" label with no actionable guidance. Users (especially on Windows/macOS where Docker Desktop must be running) have no idea what's wrong or how to fix it.
**Goal:** Enrich the health-check response with a structured diagnostic object so the frontend can display platform-aware, actionable hints.
---
### Backend Changes
**1. Enhance `GET /api/health` response** ([health.go](../internal/api/health.go))
Currently returns `{ "docker": true|false }`. Change to:
```json
{
"docker": {
"connected": false,
"error": "dial unix /var/run/docker.sock: connect: no such file or directory",
"category": "socket_not_found",
"hints": [
"Docker Desktop does not appear to be running.",
"Start Docker Desktop and wait for it to finish initializing.",
"If using a custom socket path, check DOCKER_HOST env variable."
],
"platform": "windows",
"checked_at": "2026-03-30T12:34:56Z"
}
}
```
**2. Create a Docker diagnostics module** (new file, e.g. `internal/docker/diagnostics.go`)
Classify the Ping error into a diagnostic category and generate platform-specific hints. Follow the pattern already established in [hints.go](../internal/proxy/hints.go) for proxy validation.
Error categories to handle:
| Category | Error signature | Windows hints | Linux hints | macOS hints |
|---|---|---|---|---|
| `socket_not_found` | `no such file or directory`, `The system cannot find the file specified` | Docker Desktop not running; start it from Start Menu or system tray | Docker daemon not running; `sudo systemctl start docker` | Docker Desktop not running; start from Applications or `open -a Docker` |
| `connection_refused` | `connection refused` | Docker Desktop is starting up — wait ~30s and retry | Docker daemon is starting; `sudo systemctl status docker` | Docker Desktop is starting; check the whale icon in the menu bar |
| `permission_denied` | `permission denied` | Run the application as Administrator, or add your user to the `docker-users` group | Add your user to the `docker` group: `sudo usermod -aG docker $USER` then re-login | Check Docker Desktop settings -> Resources -> File Sharing |
| `timeout` | `context deadline exceeded`, `i/o timeout` | Docker Desktop may be overloaded or hanging — restart it | Docker daemon may be overloaded; check `journalctl -u docker` | Docker Desktop may be unresponsive; restart from menu bar |
| `tls_error` | `tls:`, `certificate` | Check Docker TLS cert configuration and `DOCKER_TLS_VERIFY` | Verify certs in `~/.docker/` match daemon config | Check `~/.docker/` TLS configuration |
| `unknown` | (fallback) | Show raw error with link to Docker Desktop troubleshooting docs | Show raw error with `dockerd` docs link | Show raw error with Docker Desktop docs link |
Detect the platform via `runtime.GOOS` in the diagnostics module (the binary runs on the host, so this is accurate).
**3. Expose `runtime.GOOS` once** in diagnostics, don't scatter it through handlers.
**4. Preserve backward compat** — if any external consumer depends on the old `"docker": bool` shape, consider a migration path or version the health endpoint. Internal-only API can break freely.
---
### Frontend Changes
**5. Update the API type** ([api.ts](../web/src/lib/api.ts))
```typescript
interface DockerHealth {
connected: boolean;
error?: string;
category?: string;
hints?: string[];
platform?: string;
checked_at?: string;
}
export function getHealth(): Promise<{ docker: DockerHealth }> {
return get<{ docker: DockerHealth }>('/api/health');
}
```
**6. Enhance the health indicator** ([+layout.svelte](../web/src/routes/+layout.svelte))
When `dockerConnected === false`:
- Show a clickable/expandable area (tooltip, popover, or collapsible panel) below the red dot.
- Display the `hints` array as a bulleted list.
- Optionally show the raw `error` in a `<details>` collapse for advanced users.
- Show `checked_at` as relative time ("last checked 15s ago").
- Add a manual "Retry now" button that triggers an immediate health check instead of waiting for the 30s poll.
**7. Add i18n keys** ([en.json](../web/src/lib/i18n/en.json), [ru.json](../web/src/lib/i18n/ru.json))
Add keys for each hint category so hints can be translated. The backend should return `category` + `platform` identifiers; the frontend can use them to look up localized hint text instead of displaying raw English strings from the backend. This keeps i18n centralized in the frontend.
---
### Architecture Notes
- The proxy validator ([validator.go](../internal/proxy/validator.go), [hints.go](../internal/proxy/hints.go)) already implements a similar pattern: classifying errors by substring match and returning human-readable hints. Reuse that approach for consistency.
- Keep diagnostics pure — a function that takes an `error` and `runtime.GOOS` and returns `(category string, hints []string)`. No side effects, easy to unit-test.
- Consider caching the diagnostic result for a few seconds to avoid spamming Docker if the frontend retries rapidly.
---
### Testing
- Unit-test the diagnostics function with synthetic errors for each category x platform combination.
- Integration-test the health endpoint with a mock Docker client that returns each error type.
- Frontend: test that the hint UI renders correctly for each category and collapses/expands properly.