- new internal/sysadmin package (overview/kb/features/settings) with small
port interfaces; business packages stay unaware of sysadmin
- new routes /api/v1/admin/system/{overview,kb,features,settings} guarded
by requireSystemAdmin
- goose 00049: system_settings table (key/jsonb value, updated_by/at)
- SettingSpec whitelist registry with DB > env > default precedence
- register sysadmin in module-boundaries.yaml (archtest rules)
95 lines
2.9 KiB
Go
95 lines
2.9 KiB
Go
package server
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"net/http"
|
|
|
|
"chat-agent-system/internal/sysadmin"
|
|
)
|
|
|
|
// 系统管理台(一期):全部端点要求系统管理员。
|
|
|
|
func (s *Server) handleAdminSystemOverview(w http.ResponseWriter, r *http.Request) {
|
|
if _, ok := s.requireSystemAdmin(w, r); !ok {
|
|
return
|
|
}
|
|
if s.sysadmin == nil {
|
|
writeSafeError(w, r, http.StatusServiceUnavailable, "sysadmin service unavailable")
|
|
return
|
|
}
|
|
writeJSONResponse(w, http.StatusOK, s.sysadmin.Overview(r.Context()))
|
|
}
|
|
|
|
func (s *Server) handleAdminSystemKB(w http.ResponseWriter, r *http.Request) {
|
|
if _, ok := s.requireSystemAdmin(w, r); !ok {
|
|
return
|
|
}
|
|
if s.sysadmin == nil {
|
|
writeSafeError(w, r, http.StatusServiceUnavailable, "sysadmin service unavailable")
|
|
return
|
|
}
|
|
writeJSONResponse(w, http.StatusOK, s.sysadmin.KBInfo())
|
|
}
|
|
|
|
func (s *Server) handleAdminSystemFeatures(w http.ResponseWriter, r *http.Request) {
|
|
if _, ok := s.requireSystemAdmin(w, r); !ok {
|
|
return
|
|
}
|
|
if s.sysadmin == nil {
|
|
writeSafeError(w, r, http.StatusServiceUnavailable, "sysadmin service unavailable")
|
|
return
|
|
}
|
|
writeJSONResponse(w, http.StatusOK, map[string]any{"features": s.sysadmin.Features()})
|
|
}
|
|
|
|
func (s *Server) handleAdminSystemSettings(w http.ResponseWriter, r *http.Request) {
|
|
if _, ok := s.requireSystemAdmin(w, r); !ok {
|
|
return
|
|
}
|
|
if s.sysadmin == nil {
|
|
writeSafeError(w, r, http.StatusServiceUnavailable, "sysadmin service unavailable")
|
|
return
|
|
}
|
|
settings, err := s.sysadmin.ListSettings(r.Context())
|
|
if err != nil {
|
|
writeSafeError(w, r, http.StatusInternalServerError, err.Error())
|
|
return
|
|
}
|
|
writeJSONResponse(w, http.StatusOK, map[string]any{"settings": settings})
|
|
}
|
|
|
|
func (s *Server) handleAdminSystemSettingByKey(w http.ResponseWriter, r *http.Request) {
|
|
user, ok := s.requireSystemAdmin(w, r)
|
|
if !ok {
|
|
return
|
|
}
|
|
if s.sysadmin == nil {
|
|
writeSafeError(w, r, http.StatusServiceUnavailable, "sysadmin service unavailable")
|
|
return
|
|
}
|
|
key := r.PathValue("key")
|
|
var req struct {
|
|
Value json.RawMessage `json:"value"`
|
|
}
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
writeSafeError(w, r, http.StatusBadRequest, "无效的请求体")
|
|
return
|
|
}
|
|
setting, err := s.sysadmin.UpdateSetting(r.Context(), key, req.Value, user.ID)
|
|
switch {
|
|
case errors.Is(err, sysadmin.ErrUnknownSetting):
|
|
writeSafeError(w, r, http.StatusBadRequest, "未知的设置键")
|
|
case errors.Is(err, sysadmin.ErrSettingReadOnly):
|
|
writeSafeError(w, r, http.StatusForbidden, "该设置为只读")
|
|
case errors.Is(err, sysadmin.ErrInvalidSettingValue):
|
|
writeSafeError(w, r, http.StatusBadRequest, "设置值类型不匹配")
|
|
case errors.Is(err, sysadmin.ErrStoreUnavailable):
|
|
writeSafeError(w, r, http.StatusServiceUnavailable, "设置存储不可用")
|
|
case err != nil:
|
|
writeSafeError(w, r, http.StatusInternalServerError, err.Error())
|
|
default:
|
|
writeJSONResponse(w, http.StatusOK, setting)
|
|
}
|
|
}
|