191 lines
6.1 KiB
Go
191 lines
6.1 KiB
Go
package server
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"chat-agent-system/internal/authz"
|
|
"chat-agent-system/internal/errorevents"
|
|
"chat-agent-system/internal/sysadmin"
|
|
)
|
|
|
|
// 系统管理台:细粒度 system:* 权限(phase-2:管理员 all-or-nothing)。
|
|
|
|
func (s *Server) handleAdminSystemOverview(w http.ResponseWriter, r *http.Request) {
|
|
if _, ok := s.requireSystemPermission(w, r, authz.PermSystemOverview); !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.requireSystemPermission(w, r, authz.PermSystemKBRead); !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.requireSystemPermission(w, r, authz.PermSystemFeaturesRead); !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.requireSystemPermission(w, r, authz.PermSystemSettingsRead); !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 {
|
|
writeDomainError(w, r, http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
writeJSONResponse(w, http.StatusOK, map[string]any{"settings": settings})
|
|
}
|
|
|
|
func (s *Server) handleAdminSystemSettingByKey(w http.ResponseWriter, r *http.Request) {
|
|
user, ok := s.requireSystemPermission(w, r, authz.PermSystemSettingsWrite)
|
|
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"`
|
|
ExpectedRevision *int64 `json:"expected_revision"`
|
|
}
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
writeSafeError(w, r, http.StatusBadRequest, "无效的请求体")
|
|
return
|
|
}
|
|
setting, err := s.sysadmin().UpdateSettingEx(r.Context(), sysadmin.SettingUpdate{
|
|
Key: key,
|
|
Value: req.Value,
|
|
UpdatedBy: user.ID,
|
|
ExpectedRevision: req.ExpectedRevision,
|
|
RequestID: r.Header.Get("X-Request-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.ErrExpectedRevisionRequired):
|
|
writeSafeError(w, r, http.StatusBadRequest, "该设置需要 expected_revision")
|
|
case errors.Is(err, sysadmin.ErrSettingRevisionConflict):
|
|
writeSafeError(w, r, http.StatusConflict, `{"error":"SETTING_REVISION_CONFLICT","message":"设置版本冲突,请刷新后重试"}`)
|
|
case errors.Is(err, sysadmin.ErrStoreUnavailable):
|
|
writeSafeError(w, r, http.StatusServiceUnavailable, "设置存储不可用")
|
|
case err != nil:
|
|
writeDomainError(w, r, http.StatusInternalServerError, err)
|
|
default:
|
|
writeJSONResponse(w, http.StatusOK, setting)
|
|
}
|
|
}
|
|
|
|
func (s *Server) handleAdminSystemSettingAudits(w http.ResponseWriter, r *http.Request) {
|
|
if _, ok := s.requireSystemPermission(w, r, authz.PermSystemAuditRead); !ok {
|
|
return
|
|
}
|
|
if s.sysadmin() == nil {
|
|
writeSafeError(w, r, http.StatusServiceUnavailable, "sysadmin service unavailable")
|
|
return
|
|
}
|
|
key := r.PathValue("key")
|
|
limit := 50
|
|
if raw := r.URL.Query().Get("limit"); raw != "" {
|
|
n, err := strconv.Atoi(raw)
|
|
if err != nil || n < 0 {
|
|
writeSafeError(w, r, http.StatusBadRequest, "无效的 limit")
|
|
return
|
|
}
|
|
limit = n
|
|
}
|
|
audits, err := s.sysadmin().ListPolicyAudits(r.Context(), key, limit)
|
|
switch {
|
|
case errors.Is(err, sysadmin.ErrUnknownSetting):
|
|
writeSafeError(w, r, http.StatusBadRequest, "未知的设置键")
|
|
case errors.Is(err, sysadmin.ErrStoreUnavailable):
|
|
writeSafeError(w, r, http.StatusServiceUnavailable, "设置存储不可用")
|
|
case err != nil:
|
|
writeDomainError(w, r, http.StatusInternalServerError, err)
|
|
default:
|
|
writeJSONResponse(w, http.StatusOK, map[string]any{"audits": audits})
|
|
}
|
|
}
|
|
|
|
func (s *Server) handleAdminErrorEvents(w http.ResponseWriter, r *http.Request) {
|
|
if _, ok := s.requireSystemPermission(w, r, authz.PermSystemAuditRead); !ok {
|
|
return
|
|
}
|
|
store := errorevents.Active()
|
|
if store == nil {
|
|
writeSafeError(w, r, http.StatusServiceUnavailable, "错误审计存储不可用")
|
|
return
|
|
}
|
|
q := r.URL.Query()
|
|
limit := 50
|
|
if raw := q.Get("limit"); raw != "" {
|
|
n, err := strconv.Atoi(raw)
|
|
if err != nil || n < 0 {
|
|
writeSafeError(w, r, http.StatusBadRequest, "无效的 limit")
|
|
return
|
|
}
|
|
limit = n
|
|
}
|
|
filter := errorevents.Filter{
|
|
Code: q.Get("code"),
|
|
RequestID: q.Get("request_id"),
|
|
Class: q.Get("class"),
|
|
Limit: limit,
|
|
}
|
|
if raw := strings.TrimSpace(q.Get("since")); raw != "" {
|
|
ts, err := time.Parse(time.RFC3339, raw)
|
|
if err != nil {
|
|
writeSafeError(w, r, http.StatusBadRequest, "无效的 since")
|
|
return
|
|
}
|
|
filter.Since = ts
|
|
}
|
|
if raw := strings.TrimSpace(q.Get("until")); raw != "" {
|
|
ts, err := time.Parse(time.RFC3339, raw)
|
|
if err != nil {
|
|
writeSafeError(w, r, http.StatusBadRequest, "无效的 until")
|
|
return
|
|
}
|
|
filter.Until = ts
|
|
}
|
|
events, err := store.List(r.Context(), filter)
|
|
if err != nil {
|
|
writeDomainError(w, r, http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
writeJSONResponse(w, http.StatusOK, map[string]any{"events": events})
|
|
}
|