v0.21.1: Add GET /api/config endpoint for live config content
New endpoint returns raw controller.yaml content (text/yaml) for Hub live diff and pull operations. Same auth as other config endpoints. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,8 @@
|
|||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
|
### v0.21.1 — Config Content Endpoint (2026-02-20)
|
||||||
|
- **`GET /api/config`**: New endpoint returning raw controller.yaml content (text/yaml). Used by Hub for live config diff and pull operations. Same auth as other config endpoints (Bearer token or session cookie).
|
||||||
|
|
||||||
### What was just completed (2026-02-20 session 64)
|
### What was just completed (2026-02-20 session 64)
|
||||||
- **v0.21.0 — Hub Monitoring Takeover (Controller-side, Phases 5+6):**
|
- **v0.21.0 — Hub Monitoring Takeover (Controller-side, Phases 5+6):**
|
||||||
|
|
||||||
|
|||||||
@@ -1066,6 +1066,7 @@ Self-update endpoints accept session auth OR `Authorization: Bearer <hub_api_key
|
|||||||
|--------|----------|-------------|
|
|--------|----------|-------------|
|
||||||
| POST | `/api/config/apply` | Apply new controller.yaml from Hub (atomic write) |
|
| POST | `/api/config/apply` | Apply new controller.yaml from Hub (atomic write) |
|
||||||
| GET | `/api/config/hash` | Get SHA256 hash of current controller.yaml |
|
| GET | `/api/config/hash` | Get SHA256 hash of current controller.yaml |
|
||||||
|
| GET | `/api/config` | Get raw controller.yaml content (text/yaml) for live diff and pull |
|
||||||
|
|
||||||
Config endpoints accept session auth OR `Authorization: Bearer <hub_api_key>` (same as self-update). The `/api/config/apply` endpoint:
|
Config endpoints accept session auth OR `Authorization: Bearer <hub_api_key>` (same as self-update). The `/api/config/apply` endpoint:
|
||||||
- Accepts raw YAML body (the generated config from Hub)
|
- Accepts raw YAML body (the generated config from Hub)
|
||||||
@@ -1162,6 +1163,7 @@ See `docker-compose.yml` for the full volume configuration.
|
|||||||
- [x] Disaster recovery (v0.15.5) — Hub-based infra backup, auto-mount by UUID, restore UI with full-page takeover
|
- [x] Disaster recovery (v0.15.5) — Hub-based infra backup, auto-mount by UUID, restore UI with full-page takeover
|
||||||
- [x] Controller self-update (v0.16.0) — Watchtower-style pull + restart, Settings page UI, API key auth, auto-update scheduling
|
- [x] Controller self-update (v0.16.0) — Watchtower-style pull + restart, Settings page UI, API key auth, auto-update scheduling
|
||||||
- [x] Hub-managed config (v0.20.0) — Config apply endpoint (`POST /api/config/apply`), config hash in reports for sync comparison
|
- [x] Hub-managed config (v0.20.0) — Config apply endpoint (`POST /api/config/apply`), config hash in reports for sync comparison
|
||||||
|
- [x] Config content endpoint (v0.21.1) — `GET /api/config` returns raw YAML for Hub live diff and pull operations
|
||||||
|
|
||||||
### In Progress / Planned
|
### In Progress / Planned
|
||||||
|
|
||||||
|
|||||||
@@ -89,6 +89,10 @@ func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||||||
case path == "/config/hash" && req.Method == http.MethodGet:
|
case path == "/config/hash" && req.Method == http.MethodGet:
|
||||||
r.configHash(w, req)
|
r.configHash(w, req)
|
||||||
|
|
||||||
|
// GET /api/config — return raw controller.yaml content
|
||||||
|
case path == "/config" && req.Method == http.MethodGet:
|
||||||
|
r.configContent(w, req)
|
||||||
|
|
||||||
// GET /api/stacks/{name}/deploy-fields
|
// GET /api/stacks/{name}/deploy-fields
|
||||||
case hasSuffix(path, "/deploy-fields") && req.Method == http.MethodGet:
|
case hasSuffix(path, "/deploy-fields") && req.Method == http.MethodGet:
|
||||||
r.getDeployFields(w, req, extractName(path, "/deploy-fields"))
|
r.getDeployFields(w, req, extractName(path, "/deploy-fields"))
|
||||||
@@ -978,6 +982,16 @@ func (r *Router) configHash(w http.ResponseWriter, _ *http.Request) {
|
|||||||
writeJSON(w, http.StatusOK, apiResponse{OK: true, Data: map[string]string{"hash": hash, "path": filepath.Base(r.configPath)}})
|
writeJSON(w, http.StatusOK, apiResponse{OK: true, Data: map[string]string{"hash": hash, "path": filepath.Base(r.configPath)}})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Router) configContent(w http.ResponseWriter, _ *http.Request) {
|
||||||
|
data, err := os.ReadFile(r.configPath)
|
||||||
|
if err != nil {
|
||||||
|
writeJSON(w, http.StatusInternalServerError, apiResponse{OK: false, Error: "failed to read config"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "text/yaml; charset=utf-8")
|
||||||
|
w.Write(data)
|
||||||
|
}
|
||||||
|
|
||||||
func writeJSON(w http.ResponseWriter, status int, v interface{}) {
|
func writeJSON(w http.ResponseWriter, status int, v interface{}) {
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
w.WriteHeader(status)
|
w.WriteHeader(status)
|
||||||
|
|||||||
Reference in New Issue
Block a user