package handler import ( "encoding/json" "net/http" "git.yeij.top/AskaEth/Cyrene/platform-bridge/internal/bridge" "git.yeij.top/AskaEth/Cyrene/platform-bridge/internal/config" ) var validPlatformTypes = map[string]bool{ "qq": true, "telegram": true, "webhook": true, "wechat": true, "feishu": true, "discord": true, } // ConfigHandler exposes CRUD endpoints for platform configs. type ConfigHandler struct { store *config.Store router *bridge.PlatformRouter onChanged func(name, platform string, enabled bool, fields map[string]string) } func NewConfigHandler(store *config.Store, router *bridge.PlatformRouter) *ConfigHandler { return &ConfigHandler{store: store, router: router} } // SetOnConfigChanged sets a callback invoked after config is saved or deleted. func (h *ConfigHandler) SetOnConfigChanged(fn func(name, platform string, enabled bool, fields map[string]string)) { h.onChanged = fn } func (h *ConfigHandler) RegisterRoutes(mux *http.ServeMux) { mux.HandleFunc("/api/v1/configs", h.listConfigs) mux.HandleFunc("/api/v1/configs/", h.handleConfig) } func (h *ConfigHandler) listConfigs(w http.ResponseWriter, r *http.Request) { configs := h.store.List() type configSummary struct { Name string `json:"name"` Platform string `json:"platform"` Enabled bool `json:"enabled"` Label string `json:"label,omitempty"` Fields map[string]string `json:"fields"` UpdatedAt string `json:"updated_at"` Connected bool `json:"connected"` } var result []configSummary for _, c := range configs { connected := false if a, err := h.router.GetAdapter(c.Name); err == nil { connected = a.IsConnected() } platform := c.Platform if platform == "" { platform = c.Name } result = append(result, configSummary{ Name: c.Name, Platform: platform, Enabled: c.Enabled, Label: c.Label, Fields: c.Fields, UpdatedAt: c.UpdatedAt.Format("2006-01-02T15:04:05Z07:00"), Connected: connected, }) } // Also include platforms that exist as adapters but have no config yet. for _, name := range h.router.ListAdapters() { found := false for _, c := range configs { if c.Name == name { found = true break } } if !found { connected := false if a, err := h.router.GetAdapter(name); err == nil { connected = a.IsConnected() } result = append(result, configSummary{ Name: name, Platform: name, Enabled: false, Fields: map[string]string{}, Connected: connected, }) } } if result == nil { result = []configSummary{} } writeJSON(w, http.StatusOK, map[string]interface{}{ "configs": result, "total": len(result), }) } func (h *ConfigHandler) handleConfig(w http.ResponseWriter, r *http.Request) { name := r.URL.Path[len("/api/v1/configs/"):] if name == "" { writeJSON(w, http.StatusBadRequest, errResp("missing config name")) return } switch r.Method { case "GET": h.getConfig(w, r, name) case "POST", "PUT": h.saveConfig(w, r, name) case "DELETE": h.deleteConfig(w, r, name) default: writeJSON(w, http.StatusMethodNotAllowed, errResp("method not allowed")) } } func (h *ConfigHandler) getConfig(w http.ResponseWriter, r *http.Request, name string) { cfg, err := h.store.Get(name) if err != nil { writeJSON(w, http.StatusNotFound, errResp(err.Error())) return } connected := false if a, err := h.router.GetAdapter(name); err == nil { connected = a.IsConnected() } writeJSON(w, http.StatusOK, map[string]interface{}{ "name": cfg.Name, "platform": cfg.Platform, "enabled": cfg.Enabled, "label": cfg.Label, "fields": cfg.Fields, "updated_at": cfg.UpdatedAt.Format("2006-01-02T15:04:05Z07:00"), "connected": connected, }) } func (h *ConfigHandler) saveConfig(w http.ResponseWriter, r *http.Request, name string) { var body struct { Platform *string `json:"platform"` Enabled *bool `json:"enabled"` Label string `json:"label"` Fields map[string]string `json:"fields"` } if err := json.NewDecoder(r.Body).Decode(&body); err != nil { writeJSON(w, http.StatusBadRequest, errResp("invalid JSON: "+err.Error())) return } platform := name if body.Platform != nil && *body.Platform != "" { platform = *body.Platform } if !validPlatformTypes[platform] { writeJSON(w, http.StatusBadRequest, errResp("unknown or missing platform type: "+platform)) return } enabled := true if body.Enabled != nil { enabled = *body.Enabled } fields := body.Fields if fields == nil { fields = make(map[string]string) } cfg := config.PlatformConfig{ Name: name, Platform: platform, Enabled: enabled, Label: body.Label, Fields: fields, } if err := h.store.Set(cfg); err != nil { writeJSON(w, http.StatusInternalServerError, errResp(err.Error())) return } // Trigger hot-reload. if h.onChanged != nil { h.onChanged(name, platform, enabled, fields) } writeJSON(w, http.StatusOK, map[string]interface{}{ "name": name, "platform": platform, "enabled": enabled, "label": body.Label, "fields": fields, "status": "saved", }) } func (h *ConfigHandler) deleteConfig(w http.ResponseWriter, r *http.Request, name string) { // Get platform type before deleting (needed for onChanged callback). platform := name if cfg, err := h.store.Get(name); err == nil && cfg.Platform != "" { platform = cfg.Platform } if err := h.store.Delete(name); err != nil { writeJSON(w, http.StatusNotFound, errResp(err.Error())) return } // Trigger hot-reload: disable and clear fields. if h.onChanged != nil { h.onChanged(name, platform, false, nil) } writeJSON(w, http.StatusOK, map[string]string{"status": "deleted", "name": name}) }