feat(bridge): separate bridge config from .env, web panel + hot-reload
- BridgeConfigStore: file-backed JSON config (configs/bridge.json) - Auto-creates config file only when user saves via admin panel - Hot-reload: watches config file every 5s, reloads adapters live - Admin API: GET/PUT/DELETE /api/v1/admin/bridge/config/:name - Frontend: full CRUD page with platform type selector + toggle - Removed bridge env vars from .env.example
This commit is contained in:
1 parent
217319eabc
commit
9eaed2883f
5 files changed
+478
-73
No files matched your search
@@ -77,6 +77,13 @@ type PerfMonitor interface {
|
||||
History() []map[string]any
|
||||
}
|
||||
|
||||
// BridgeConfigManager manages platform adapter configurations.
|
||||
type BridgeConfigManager interface {
|
||||
GetAdapters() []map[string]any
|
||||
SaveAdapter(name, adapterType string, enabled bool, settings map[string]string) error
|
||||
DeleteAdapter(name string) error
|
||||
}
|
||||
|
||||
// Dependencies holds all injectable services.
|
||||
type Dependencies struct {
|
||||
AuthMgr AuthManager
|
||||
@@ -87,6 +94,7 @@ type Dependencies struct {
|
||||
LLMLogger LLMCallLogger
|
||||
TraceLog TraceLogger
|
||||
PerfMon PerfMonitor
|
||||
BridgeCfg BridgeConfigManager
|
||||
ModelConfig *ModelConfigStore
|
||||
PluginList []map[string]string
|
||||
}
|
||||
@@ -133,6 +141,8 @@ func (r *Router) setupRoutes() {
|
||||
r.mux.HandleFunc("/api/v1/admin/models/config/", r.authMW(r.adminMW(r.modelConfigItemHandler)))
|
||||
r.mux.HandleFunc("/api/v1/admin/trace", r.authMW(r.adminMW(r.traceHandler)))
|
||||
r.mux.HandleFunc("/api/v1/admin/perf", r.authMW(r.adminMW(r.perfHandler)))
|
||||
r.mux.HandleFunc("/api/v1/admin/bridge/config", r.authMW(r.adminMW(r.bridgeConfigHandler)))
|
||||
r.mux.HandleFunc("/api/v1/admin/bridge/config/", r.authMW(r.adminMW(r.bridgeConfigItemHandler)))
|
||||
}
|
||||
|
||||
// ServeHTTP implements http.Handler.
|
||||
@@ -522,6 +532,58 @@ func (r *Router) perfHandler(w http.ResponseWriter, req *http.Request) {
|
||||
})
|
||||
}
|
||||
|
||||
// --- Bridge Config ---
|
||||
|
||||
func (r *Router) bridgeConfigHandler(w http.ResponseWriter, req *http.Request) {
|
||||
if r.deps.BridgeCfg == nil {
|
||||
writeJSON(w, 503, M{"error": "bridge config unavailable"})
|
||||
return
|
||||
}
|
||||
switch req.Method {
|
||||
case "GET":
|
||||
writeJSON(w, 200, M{"adapters": r.deps.BridgeCfg.GetAdapters()})
|
||||
default:
|
||||
writeJSON(w, 405, M{"error": "method not allowed"})
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Router) bridgeConfigItemHandler(w http.ResponseWriter, req *http.Request) {
|
||||
if r.deps.BridgeCfg == nil {
|
||||
writeJSON(w, 503, M{"error": "bridge config unavailable"})
|
||||
return
|
||||
}
|
||||
name := strings.TrimPrefix(req.URL.Path, "/api/v1/admin/bridge/config/")
|
||||
if name == "" {
|
||||
writeJSON(w, 400, M{"error": "missing adapter name"})
|
||||
return
|
||||
}
|
||||
|
||||
switch req.Method {
|
||||
case "PUT", "POST":
|
||||
var body struct {
|
||||
Type string `json:"type"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Settings map[string]string `json:"settings"`
|
||||
}
|
||||
json.NewDecoder(req.Body).Decode(&body)
|
||||
if err := r.deps.BridgeCfg.SaveAdapter(name, body.Type, body.Enabled, body.Settings); err != nil {
|
||||
writeJSON(w, 500, M{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
writeJSON(w, 200, M{"ok": true})
|
||||
|
||||
case "DELETE":
|
||||
if err := r.deps.BridgeCfg.DeleteAdapter(name); err != nil {
|
||||
writeJSON(w, 500, M{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
writeJSON(w, 204, nil)
|
||||
|
||||
default:
|
||||
writeJSON(w, 405, M{"error": "method not allowed"})
|
||||
}
|
||||
}
|
||||
|
||||
// --- Helpers ---
|
||||
|
||||
type M = map[string]any
|
||||
|
||||
Reference in new issue
Block a user