94 lines
2.7 KiB
Go
94 lines
2.7 KiB
Go
package controllers
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"strconv"
|
|
"time"
|
|
|
|
"git.pcux.cn/xuans/Smail/config"
|
|
"git.pcux.cn/xuans/Smail/dto/response"
|
|
"git.pcux.cn/xuans/Smail/services/access"
|
|
"git.pcux.cn/xuans/Smail/services/logstore"
|
|
"git.pcux.cn/xuans/Smail/services/runtimelog"
|
|
appcontext "git.pcux.cn/xuans/Smail/utils/context"
|
|
)
|
|
|
|
func logQueryError(w http.ResponseWriter, err error) {
|
|
response.NewErrorResponse(response.ServerError, "日志查询失败,请检查日志存储状态", "LOG_QUERY_FAILED").FPrint(w)
|
|
}
|
|
|
|
// GetLogs 只读日志入口,授权在服务端检查,不依赖导航隐藏。
|
|
func GetLogs(ctx *appcontext.Context, w http.ResponseWriter, req *http.Request) {
|
|
if err := authorizeCapability(ctx, access.CapabilityAuditRead, "", false); err != nil {
|
|
writeConfigAccessError(w, err)
|
|
return
|
|
}
|
|
values := req.URL.Query()
|
|
limit, offset := 50, 0
|
|
var err error
|
|
if values.Has("limit") {
|
|
limit, err = strconv.Atoi(values.Get("limit"))
|
|
}
|
|
if err == nil && values.Has("offset") {
|
|
offset, err = strconv.Atoi(values.Get("offset"))
|
|
}
|
|
if err != nil || limit < 1 || limit > 500 || offset < 0 || offset > 100000 {
|
|
writeConfigError(w, config.ErrInvalidAuditQuery)
|
|
return
|
|
}
|
|
bounded, cancel := context.WithTimeout(req.Context(), 10*time.Second)
|
|
defer cancel()
|
|
if values.Get("kind") == "runtime" {
|
|
writer := runtimelog.Current()
|
|
if writer == nil {
|
|
logQueryError(w, runtimelog.ErrInvalid)
|
|
return
|
|
}
|
|
if values.Get("file") == "" {
|
|
files, err := writer.Files()
|
|
if err != nil {
|
|
logQueryError(w, err)
|
|
return
|
|
}
|
|
response.NewSuccessResponse(map[string]any{"files": files}).FPrint(w)
|
|
return
|
|
}
|
|
page, err := writer.Read(bounded, values.Get("file"), offset, limit)
|
|
if err != nil {
|
|
logQueryError(w, err)
|
|
return
|
|
}
|
|
response.NewSuccessResponse(page).FPrint(w)
|
|
return
|
|
}
|
|
query := logstore.Query{Kind: values.Get("kind"), ActorID: values.Get("actorId"), Limit: limit + 1, Offset: offset}
|
|
for _, item := range []struct {
|
|
name string
|
|
target *time.Time
|
|
}{{"from", &query.From}, {"to", &query.To}} {
|
|
if values.Get(item.name) != "" {
|
|
*item.target, err = time.Parse(time.RFC3339Nano, values.Get(item.name))
|
|
if err != nil {
|
|
writeConfigError(w, config.ErrInvalidAuditQuery)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
if query.Kind != "config" && query.Kind != "operation" && query.Kind != "mail" {
|
|
writeConfigError(w, config.ErrInvalidAuditQuery)
|
|
return
|
|
}
|
|
store := logstore.Current()
|
|
items, err := store.Query(bounded, query)
|
|
if err != nil {
|
|
logQueryError(w, err)
|
|
return
|
|
}
|
|
hasMore := len(items) > limit
|
|
if hasMore {
|
|
items = items[:limit]
|
|
}
|
|
response.NewSuccessResponse(map[string]any{"items": items, "hasMore": hasMore, "retentionDays": 30}).FPrint(w)
|
|
}
|