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
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
## 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.
|
||||
Reference in New Issue
Block a user