82 lines
2.8 KiB
Go
82 lines
2.8 KiB
Go
package httpapi
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"io"
|
|
"knowledge-governance/internal/qaopsorg"
|
|
"net/http"
|
|
"net/url"
|
|
)
|
|
|
|
type QAOrgStore interface {
|
|
GetView(context.Context) (qaopsorg.State, error)
|
|
PreviewRules(context.Context, qaopsorg.SaveRequest) (qaopsorg.State, error)
|
|
SaveRules(context.Context, qaopsorg.SaveRequest, string) (qaopsorg.State, error)
|
|
}
|
|
|
|
func qaOrganization(w http.ResponseWriter, r *http.Request, config Config) {
|
|
if !authorizeQAOps(w, r) {
|
|
return
|
|
}
|
|
if r.URL.RawQuery != "" {
|
|
writeOrgError(w, qaopsorg.ErrInvalid)
|
|
return
|
|
}
|
|
if config.QAOrg == nil {
|
|
writeOrgError(w, qaopsorg.ErrUnavailable)
|
|
return
|
|
}
|
|
var result qaopsorg.State
|
|
var err error
|
|
if r.Method == http.MethodGet {
|
|
result, err = config.QAOrg.GetView(r.Context())
|
|
} else {
|
|
if r.Header.Get("X-Governance-Action") != "qa-organization" || r.Header.Get("Sec-Fetch-Site") == "cross-site" {
|
|
writeJSON(w, 403, map[string]string{"error": "区域配置需要同源操作意图"})
|
|
return
|
|
}
|
|
// A same-origin browser request can pass a reverse proxy that rewrites
|
|
// Host. Fetch Metadata is browser-controlled; keep the explicit action
|
|
// header and reject cross-site requests without trusting forwarded hosts.
|
|
if raw := r.Header.Get("Origin"); raw != "" && r.Header.Get("Sec-Fetch-Site") != "same-origin" {
|
|
origin, parseErr := url.Parse(raw)
|
|
if parseErr != nil || (origin.Scheme != "http" && origin.Scheme != "https") || origin.Host != r.Host {
|
|
writeJSON(w, 403, map[string]string{"error": "拒绝跨站配置请求"})
|
|
return
|
|
}
|
|
}
|
|
var request qaopsorg.SaveRequest
|
|
decoder := json.NewDecoder(http.MaxBytesReader(w, r.Body, 1<<20))
|
|
decoder.DisallowUnknownFields()
|
|
if decoder.Decode(&request) != nil || decoder.Decode(&struct{}{}) != io.EOF || request.DirectoryID == "" || request.Rules == nil {
|
|
writeOrgError(w, qaopsorg.ErrInvalid)
|
|
return
|
|
}
|
|
if r.Method == http.MethodPost {
|
|
result, err = config.QAOrg.PreviewRules(r.Context(), request)
|
|
} else {
|
|
identity, _ := sessionFrom(r.Context())
|
|
result, err = config.QAOrg.SaveRules(r.Context(), request, identity.Username)
|
|
}
|
|
}
|
|
if err != nil {
|
|
writeOrgError(w, err)
|
|
return
|
|
}
|
|
writeJSON(w, 200, map[string]any{"data": result})
|
|
}
|
|
func writeOrgError(w http.ResponseWriter, err error) {
|
|
status, message := 503, "通讯录或区域配置存储不可用,未展示为空数据"
|
|
switch {
|
|
case errors.Is(err, qaopsorg.ErrInvalid):
|
|
status, message = 400, "部门树或区域配置无效,请核对重复、缺失路径及区域"
|
|
case errors.Is(err, qaopsorg.ErrConflict):
|
|
status, message = 409, "通讯录或区域配置已变化,请重新读取后预览"
|
|
case errors.Is(err, qaopsorg.ErrNoDirectory):
|
|
status, message = 409, "尚无完整通讯录快照,不能保存区域配置"
|
|
}
|
|
writeJSON(w, status, map[string]string{"error": message})
|
|
}
|