优化结构

This commit is contained in:
Yvan
2026-08-28 15:07:16 +08:00
parent ae5345de14
commit a4ed3a02df
15 changed files with 92 additions and 60 deletions
+5 -1
View File
@@ -61,7 +61,11 @@ func NewGinEngineWithRuntime(runtime *config.Store, access *systemservice.Access
if ws != nil && ws.Enabled() {
engine.GET(ws.Path(), handleWebSocket)
}
if snapshot == nil || snapshot.Admin == nil || snapshot.Admin.App == nil || strings.ToLower(strings.TrimSpace(snapshot.Admin.App.Env)) != "production" {
env := ""
if snapshot != nil && snapshot.Admin != nil && snapshot.Admin.App != nil {
env = strings.ToLower(strings.TrimSpace(snapshot.Admin.App.Env))
}
if env != "production" && env != "prod" && env != "live" && env != "staging" {
registerSwagger(engine, prefix, version, logger)
}
staticfiles.Register(engine, runtime)
+17
View File
@@ -30,6 +30,9 @@ type PageResult struct {
}
func Write(c *gin.Context, code int, data any, message string) {
if code == CodeError {
message = sanitizeFailureMessage(message)
}
c.JSON(http.StatusOK, Response{Code: code, Data: data, Msg: message})
}
@@ -39,6 +42,20 @@ func OKWithData(c *gin.Context, data any) { Write(c, CodeSuccess, data, "成功"
func Fail(c *gin.Context, message string) { Write(c, CodeError, gin.H{}, message) }
func sanitizeFailureMessage(message string) string {
message = strings.TrimSpace(message)
lower := strings.ToLower(message)
for _, marker := range []string{"gorm", "sql:", "redis", "mysql", "mongo", "dial ", "connection", "provider", "sdk", "json:", "serialize", "timeout", "http 4", "http 5", "tls:"} {
if strings.Contains(lower, marker) {
return "操作失败"
}
}
if strings.Contains(message, "失败:") {
return strings.TrimSpace(strings.SplitN(message, "失败:", 2)[0]) + "失败"
}
return message
}
func NoAuth(c *gin.Context, message string) {
c.AbortWithStatusJSON(http.StatusUnauthorized, Response{Code: CodeError, Data: nil, Msg: message})
}
+8 -3
View File
@@ -40,10 +40,11 @@ func AccessLog(runtime *config.Store, logger *slog.Logger, version string) gin.H
}
bodyLimit := defaultRequestBodyLimit
if mediaUpload {
bodyLimit = system.DefaultMaxMediaFileSize + (1 << 20)
if admin != nil && admin.Media != nil && admin.Media.MaxFileSize > 0 {
bodyLimit = admin.Media.MaxFileSize + (1 << 20)
mediaSettings := system.MediaSettings{}
if admin != nil && admin.Media != nil {
mediaSettings.MaxFileSize = admin.Media.MaxFileSize
}
bodyLimit = mediaSettings.EffectiveMaxFileSize() + (1 << 20)
}
bytesIn := c.Request.ContentLength
logLimit := 1024
@@ -96,6 +97,7 @@ func AccessLog(runtime *config.Store, logger *slog.Logger, version string) gin.H
requestText = redactJSON(requestBody, c.GetHeader("Content-Type"), logLimit)
}
c.Set(ctxReqBodyKey, requestText)
c.Set(ctxRespTruncatedKey, writer.Truncated())
if paymentCallback {
// Callback acknowledgements and provider payloads must not flow into
// the generic response/error audit pipeline.
@@ -110,6 +112,9 @@ func AccessLog(runtime *config.Store, logger *slog.Logger, version string) gin.H
return
}
responseText := redactJSON(writer.body.Bytes(), c.Writer.Header().Get("Content-Type"), logLimit)
if writer.Truncated() {
responseText = "[超出记录长度]"
}
if paymentCallback {
responseText = "[支付回调响应已省略]"
}
+3 -28
View File
@@ -43,8 +43,7 @@ func OperationAudit(runtime *config.Store, recorder *systemservice.AuditRecorder
}
if c.Request.Method == http.MethodGet {
requestBody = operationQueryBody(c.Request.URL.RawQuery)
} else if value, ok := c.Get(ctxReqBodyKey); ok {
requestBody = []byte(stringValue(value))
} else if _, ok := c.Get(ctxReqBodyKey); ok {
} else if c.Request.Body != nil {
// Fallback for tests or custom middleware chains that omit AccessLog.
if strings.Contains(c.GetHeader("Content-Type"), "multipart/form-data") {
@@ -79,13 +78,13 @@ func OperationAudit(runtime *config.Store, recorder *systemservice.AuditRecorder
responseBody = redactJSON(body.Bytes(), c.Writer.Header().Get("Content-Type"), maxBytes)
}
}
if isDownloadResponse(c) && len(responseBody) > maxBytes {
if truncated, _ := c.Get(ctxRespTruncatedKey); truncated == true {
responseBody = "[超出记录长度]"
}
errorMessage := c.Errors.ByType(gin.ErrorTypePrivate).String()
operationBody := ""
capturedBody := false
if c.Request.Method != http.MethodGet {
if c.Request.Method != "GET" {
if value, ok := c.Get(ctxReqBodyKey); ok {
// AccessLog has already captured, redacted, summarized, and bounded the
// request body. Treat that value as final so audit does not process it a
@@ -162,27 +161,3 @@ func maskOperationBody(value any) {
}
}
}
// operationDownloadHeaders lists the response headers whose presence marks a
// download: the value is the substring that identifies it.
var operationDownloadHeaders = [...]struct{ header, marker string }{
{"Pragma", "public"},
{"Expires", "0"},
{"Cache-Control", "must-revalidate, post-check=0, pre-check=0"},
{"Content-Type", "application/force-download"},
{"Content-Type", "application/octet-stream"},
{"Content-Type", "application/vnd.ms-excel"},
{"Content-Type", "application/download"},
{"Content-Disposition", "attachment"},
{"Content-Transfer-Encoding", "binary"},
}
func isDownloadResponse(c *gin.Context) bool {
header := c.Writer.Header()
for _, candidate := range operationDownloadHeaders {
if strings.Contains(header.Get(candidate.header), candidate.marker) {
return true
}
}
return false
}
+9 -4
View File
@@ -12,14 +12,16 @@ import (
// OperationAudit consumes these values after the handler returns, avoiding a
// second body read/writer wrapper.
const (
ctxReqBodyKey = "kra_req_body"
ctxRespBufferKey = "kra_resp_buffer"
ctxReqBodyKey = "kra_req_body"
ctxRespBufferKey = "kra_resp_buffer"
ctxRespTruncatedKey = "kra_resp_truncated"
)
type captureWriter struct {
gin.ResponseWriter
body bytes.Buffer
maxBytes int
body bytes.Buffer
maxBytes int
truncated bool
}
func (w *captureWriter) Write(data []byte) (int, error) {
@@ -31,6 +33,7 @@ func (w *captureWriter) Write(data []byte) (int, error) {
remaining := limit - w.body.Len()
if len(data) > remaining {
_, _ = w.body.Write(data[:remaining])
w.truncated = true
} else {
_, _ = w.body.Write(data)
}
@@ -38,6 +41,8 @@ func (w *captureWriter) Write(data []byte) (int, error) {
return w.ResponseWriter.Write(data)
}
func (w *captureWriter) Truncated() bool { return w != nil && w.truncated }
func redactJSON(raw []byte, contentType string, limit int) string {
if len(raw) == 0 {
return ""