优化结构
This commit is contained in:
@@ -7,6 +7,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
stdmime "mime"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
@@ -246,10 +247,18 @@ func (uc *MediaUsecase) CompleteUpload(ctx context.Context, userID, uploadID uin
|
||||
return fail(errors.New("整文件校验失败"))
|
||||
}
|
||||
media := &MediaFile{Name: session.FileName, URL: stored.URL, Tag: strings.TrimPrefix(filepath.Ext(session.FileName), "."), Key: key, Size: stored.Size, Mime: mime, MD5: hash, UserID: userID}
|
||||
if strings.TrimSpace(media.Mime) == "" {
|
||||
media.Mime = stdmime.TypeByExtension(ext)
|
||||
if media.Mime == "" {
|
||||
media.Mime = "application/octet-stream"
|
||||
}
|
||||
}
|
||||
if err = uc.CreateMedia(ctx, media); err != nil {
|
||||
return fail(err)
|
||||
}
|
||||
if err = uc.CompleteUploadSession(ctx, uploadID, key, media.ID); err != nil {
|
||||
_ = uc.DeleteMedia(ctx, media.ID)
|
||||
_ = uc.files.Delete(ctx, key)
|
||||
_ = uc.FailUploadSession(ctx, uploadID)
|
||||
return nil, fmt.Errorf("完成上传会话失败: %w", err)
|
||||
}
|
||||
|
||||
Vendored
+32
@@ -87,6 +87,38 @@ func (s *Store) Delete(ctx context.Context, key string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Store) GetAndDelete(ctx context.Context, key string) (string, bool, error) {
|
||||
if client := s.client(); client != nil {
|
||||
value, err := client.Eval(ctx, "local v=redis.call('GET',KEYS[1]); if v then redis.call('DEL',KEYS[1]); end; return v", []string{key}).Result()
|
||||
if err == redis.Nil {
|
||||
return "", false, nil
|
||||
}
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
if value == nil {
|
||||
return "", false, nil
|
||||
}
|
||||
switch typed := value.(type) {
|
||||
case string:
|
||||
return typed, true, nil
|
||||
case []byte:
|
||||
return string(typed), true, nil
|
||||
default:
|
||||
return "", false, nil
|
||||
}
|
||||
}
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
entry, ok := s.memory[key]
|
||||
if !ok || (!entry.expiresAt.IsZero() && time.Now().After(entry.expiresAt)) {
|
||||
delete(s.memory, key)
|
||||
return "", false, nil
|
||||
}
|
||||
delete(s.memory, key)
|
||||
return entry.value, true, nil
|
||||
}
|
||||
|
||||
func (s *Store) Increment(ctx context.Context, key string, expiration time.Duration) (int64, error) {
|
||||
if client := s.client(); client != nil {
|
||||
if expiration > 0 {
|
||||
|
||||
@@ -438,7 +438,7 @@ func alipayCreateMethod(extra, config map[string]any) (string, error) {
|
||||
}
|
||||
normalized := paymentkit.NormalizePaymentMethod(value)
|
||||
switch normalized {
|
||||
case "", "create", "trade_create", "alipay_trade_create", "jsapi", "miniapp", "mini_program":
|
||||
case "", "create", "trade_create", "alipay_trade_create", "jsapi", "js_api", "miniapp", "mini_app", "mini_program":
|
||||
return "alipay.trade.create", nil
|
||||
case "pay", "trade_pay", "alipay_trade_pay", "barcode", "barcode_pay", "micropay", "face_to_face":
|
||||
return "alipay.trade.pay", nil
|
||||
|
||||
@@ -183,7 +183,7 @@ func wechatV2CreateMethod(extra, config map[string]any) (string, error) {
|
||||
}
|
||||
normalized := paymentkit.NormalizePaymentMethod(value)
|
||||
switch normalized {
|
||||
case "", "jsapi", "js_api", "mini", "miniapp", "mini_program", "miniprogram", "applet":
|
||||
case "", "jsapi", "js_api", "mini", "miniapp", "mini_app", "mini_program", "miniprogram", "applet":
|
||||
return gopayWechat.TradeType_JsApi, nil
|
||||
case "app", "app_pay":
|
||||
return gopayWechat.TradeType_App, nil
|
||||
|
||||
@@ -49,7 +49,7 @@ func (a *wechatV3Adapter) Create(ctx context.Context, req *bizpayment.PaymentReq
|
||||
tradeType = normalizeWechatV3TradeType(firstAny(c, "trade_type", "pay_type", "method"))
|
||||
}
|
||||
switch tradeType {
|
||||
case "", "jsapi", "mini", "miniprogram", "mini_program", "applet":
|
||||
case "", "jsapi", "js_api", "mini", "miniapp", "mini_app", "miniprogram", "mini_program", "applet":
|
||||
if openID := firstAny(req.Extra, "openid", "open_id"); openID != "" {
|
||||
bm.SetBodyMap("payer", func(payer gopay.BodyMap) { payer.Set("openid", openID) })
|
||||
}
|
||||
|
||||
@@ -165,7 +165,7 @@ var routes = map[string]routeValue{
|
||||
"POST /info/createInfo": {group: "公告", description: "新建公告", audit: true},
|
||||
"POST /init/checkdb": {group: "初始化", description: "检查数据库", public: true},
|
||||
"POST /init/initdb": {group: "初始化", description: "初始化数据库", public: true},
|
||||
"POST /integration/configs/:kind/:provider/test": {group: "集成配置", description: "测试通信集成连接", audit: true, bodyPolicy: BodyPolicyPaymentConfig},
|
||||
"POST /integration/configs/:kind/:provider/test": {group: "集成配置", description: "测试通信集成连接", audit: true, bodyPolicy: BodyPolicyIntegrationConfig},
|
||||
"POST /jwt/jsonInBlacklist": {group: "jwt", description: "jwt加入黑名单(退出,必选)"},
|
||||
"POST /mediaUpload/chunk": {group: "媒体上传", description: "上传分片", bodyPolicy: BodyPolicyUpload},
|
||||
"POST /mediaUpload/complete": {group: "媒体上传", description: "完成大文件上传"},
|
||||
|
||||
@@ -61,7 +61,9 @@ func NewGinEngineWithRuntime(runtime *config.Store, access *systemservice.Access
|
||||
if ws != nil && ws.Enabled() {
|
||||
engine.GET(ws.Path(), handleWebSocket)
|
||||
}
|
||||
registerSwagger(engine, prefix, version, logger)
|
||||
if snapshot == nil || snapshot.Admin == nil || snapshot.Admin.App == nil || strings.ToLower(strings.TrimSpace(snapshot.Admin.App.Env)) != "production" {
|
||||
registerSwagger(engine, prefix, version, logger)
|
||||
}
|
||||
staticfiles.Register(engine, runtime)
|
||||
if logger != nil {
|
||||
for _, route := range engine.Routes() {
|
||||
|
||||
@@ -31,7 +31,7 @@ func AccessLog(runtime *config.Store, logger *slog.Logger, version string) gin.H
|
||||
started := time.Now()
|
||||
var requestBody []byte
|
||||
multipart := strings.Contains(c.GetHeader("Content-Type"), "multipart/form-data")
|
||||
mediaUpload := multipart && routecatalog.BodyPolicyFor(c.Request.Method, c.Request.URL.Path) == routecatalog.BodyPolicyUpload
|
||||
mediaUpload := routecatalog.BodyPolicyFor(c.Request.Method, c.Request.URL.Path) == routecatalog.BodyPolicyUpload
|
||||
var admin *config.Admin
|
||||
if runtime != nil {
|
||||
if snapshot := runtime.Snapshot(); snapshot != nil {
|
||||
@@ -46,7 +46,14 @@ func AccessLog(runtime *config.Store, logger *slog.Logger, version string) gin.H
|
||||
}
|
||||
}
|
||||
bytesIn := c.Request.ContentLength
|
||||
logLimit := 1024
|
||||
if admin != nil && admin.Zap != nil && admin.Zap.AccessLogMaxBytes > 0 {
|
||||
logLimit = admin.Zap.AccessLogMaxBytes
|
||||
}
|
||||
maxBytes := 1 << 20
|
||||
if logLimit > maxBytes {
|
||||
maxBytes = logLimit
|
||||
}
|
||||
writer := &captureWriter{ResponseWriter: c.Writer, maxBytes: maxBytes}
|
||||
c.Writer = writer
|
||||
c.Header("X-Kra-Version", version)
|
||||
@@ -75,10 +82,6 @@ func AccessLog(runtime *config.Store, logger *slog.Logger, version string) gin.H
|
||||
if bytesIn < 0 {
|
||||
bytesIn = 0
|
||||
}
|
||||
logLimit := 1024
|
||||
if admin != nil && admin.Zap != nil && admin.Zap.AccessLogMaxBytes > 0 {
|
||||
logLimit = admin.Zap.AccessLogMaxBytes
|
||||
}
|
||||
bodyPolicy := routecatalog.BodyPolicyFor(c.Request.Method, c.Request.URL.Path)
|
||||
paymentCallback := bodyPolicy == routecatalog.BodyPolicyPaymentCallback
|
||||
paymentConfigWrite := bodyPolicy == routecatalog.BodyPolicyPaymentConfig
|
||||
|
||||
@@ -65,10 +65,6 @@ func OperationAudit(runtime *config.Store, recorder *systemservice.AuditRecorder
|
||||
c.Request.Body = io.NopCloser(bytes.NewReader(requestBody))
|
||||
}
|
||||
}
|
||||
// The reference middleware captures up to the global 1 MiB response
|
||||
// safety limit and only applies the configured operation-log limit when a
|
||||
// download response is recorded. Using maxBytes here would silently
|
||||
// truncate ordinary JSON responses before that decision is possible.
|
||||
started := time.Now()
|
||||
c.Next()
|
||||
userID := uint(0)
|
||||
|
||||
@@ -34,7 +34,7 @@ func CORS(runtime *config.Store) gin.HandlerFunc {
|
||||
corsHandled := false
|
||||
if mode == "allow-all" {
|
||||
if origin != "" {
|
||||
setCORSHeaders(c, origin, defaultCORSHeaders, defaultCORSMethods, defaultCORSExpose, true)
|
||||
setCORSHeaders(c, origin, defaultCORSHeaders, defaultCORSMethods, defaultCORSExpose, false)
|
||||
corsHandled = true
|
||||
}
|
||||
} else if rule := matchingCORSRule(cors.Whitelist, origin); rule != nil {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
integrationbiz "kra/internal/biz/integration"
|
||||
"strings"
|
||||
|
||||
"kra/internal/service/dto"
|
||||
)
|
||||
@@ -38,13 +39,13 @@ func (s *IntegrationConfigService) List(ctx context.Context, kind string) ([]*dt
|
||||
enabled = config.Enabled
|
||||
delete(byProvider, definition.Provider)
|
||||
}
|
||||
result = append(result, &dto.IntegrationConfigResponse{Kind: definition.Kind, Provider: definition.Provider, Name: definition.Name, Description: definition.Description, Enabled: enabled, Configured: configured, Config: values, Fields: integrationFieldsDTO(definition.Fields)})
|
||||
result = append(result, &dto.IntegrationConfigResponse{Kind: definition.Kind, Provider: definition.Provider, Name: definition.Name, Description: definition.Description, Enabled: enabled, Configured: configured, Config: redactIntegrationJSON(values), Fields: integrationFieldsDTO(definition.Fields)})
|
||||
}
|
||||
for _, config := range configs {
|
||||
if _, exists := byProvider[config.Provider]; !exists {
|
||||
continue
|
||||
}
|
||||
result = append(result, &dto.IntegrationConfigResponse{Kind: config.Kind, Provider: config.Provider, Name: config.Provider, Enabled: config.Enabled, Configured: true, Config: config.Values})
|
||||
result = append(result, &dto.IntegrationConfigResponse{Kind: config.Kind, Provider: config.Provider, Name: config.Provider, Enabled: config.Enabled, Configured: true, Config: redactIntegrationJSON(config.Values)})
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
@@ -55,16 +56,44 @@ func (s *IntegrationConfigService) Find(ctx context.Context, kind, provider stri
|
||||
return nil, err
|
||||
}
|
||||
definition, found := integrationbiz.IntegrationDefinition(config.Kind, config.Provider)
|
||||
response := &dto.IntegrationConfigResponse{Kind: config.Kind, Provider: config.Provider, Name: config.Provider, Enabled: config.Enabled, Configured: true, Config: config.Values}
|
||||
response := &dto.IntegrationConfigResponse{Kind: config.Kind, Provider: config.Provider, Name: config.Provider, Enabled: config.Enabled, Configured: true, Config: redactIntegrationJSON(config.Values)}
|
||||
if found {
|
||||
response.Name = definition.Name
|
||||
response.Description = definition.Description
|
||||
response.Fields = integrationFieldsDTO(definition.Fields)
|
||||
response.Config = mergeConfigJSON(definition.Defaults, config.Values)
|
||||
response.Config = redactIntegrationJSON(mergeConfigJSON(definition.Defaults, config.Values))
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func redactIntegrationJSON(raw json.RawMessage) json.RawMessage {
|
||||
var value any
|
||||
if json.Unmarshal(raw, &value) != nil {
|
||||
return raw
|
||||
}
|
||||
var walk func(any)
|
||||
walk = func(v any) {
|
||||
switch x := v.(type) {
|
||||
case map[string]any:
|
||||
for k, item := range x {
|
||||
n := strings.ToLower(strings.ReplaceAll(k, "-", "_"))
|
||||
if strings.Contains(n, "secret") || strings.Contains(n, "password") || strings.Contains(n, "private") || strings.Contains(n, "credential") || strings.Contains(n, "token") || strings.HasSuffix(n, "_key") || strings.Contains(n, "cert") || strings.Contains(n, "p12") {
|
||||
x[k] = "******"
|
||||
} else {
|
||||
walk(item)
|
||||
}
|
||||
}
|
||||
case []any:
|
||||
for _, item := range x {
|
||||
walk(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
walk(value)
|
||||
out, _ := json.Marshal(value)
|
||||
return out
|
||||
}
|
||||
|
||||
func (s *IntegrationConfigService) Save(ctx context.Context, kind, provider string, req *dto.IntegrationConfigRequest) error {
|
||||
if req == nil {
|
||||
return s.uc.Save(ctx, nil)
|
||||
|
||||
@@ -53,7 +53,13 @@ func (s *ExportService) IssueToken(ctx context.Context, templateID string, param
|
||||
}
|
||||
|
||||
func (s *ExportService) ConsumeToken(ctx context.Context, token string, expectBlank bool) (*ExportToken, error) {
|
||||
raw, ok, err := s.cache.Get(ctx, "export:"+token)
|
||||
key := "export:" + token
|
||||
raw, ok, err := s.cache.Get(ctx, key)
|
||||
if atomic, supported := s.cache.(interface {
|
||||
GetAndDelete(context.Context, string) (string, bool, error)
|
||||
}); supported {
|
||||
raw, ok, err = atomic.GetAndDelete(ctx, key)
|
||||
}
|
||||
if err != nil || !ok {
|
||||
return nil, ErrExportTokenInvalid
|
||||
}
|
||||
@@ -64,7 +70,11 @@ func (s *ExportService) ConsumeToken(ctx context.Context, token string, expectBl
|
||||
if value.Blank != expectBlank {
|
||||
return nil, ErrExportTokenType
|
||||
}
|
||||
_ = s.cache.Delete(ctx, "export:"+token)
|
||||
if _, supported := s.cache.(interface {
|
||||
GetAndDelete(context.Context, string) (string, bool, error)
|
||||
}); !supported {
|
||||
_ = s.cache.Delete(ctx, key)
|
||||
}
|
||||
return &ExportToken{TemplateID: value.TemplateID, Params: value.Params, Blank: value.Blank}, nil
|
||||
}
|
||||
func exportTemplateDomain(value *dto.ExportTemplateRequest) *system.ExportTemplate {
|
||||
|
||||
@@ -40,5 +40,5 @@ func convertUser(user *system.User) *dto.UserResponse {
|
||||
if user.Department != nil {
|
||||
department = departmentResponse(user.Department)
|
||||
}
|
||||
return &dto.UserResponse{ID: user.ID, CreatedAt: user.CreatedAt, UpdatedAt: user.UpdatedAt, DeletedAt: nil, UUID: user.UUID, Username: user.Username, NickName: user.NickName, HeaderImg: user.HeaderImg, AuthorityID: user.AuthorityID, Authority: convertAuthority(user.Authority), Authorities: authorities, DeptID: user.DeptID, Department: department, Departments: departments, Positions: positions, Phone: user.Phone, Email: user.Email, Enable: user.Enable, OriginSetting: user.OriginSetting, PasswordUpdatedAt: user.PasswordUpdatedAt}
|
||||
return &dto.UserResponse{ID: user.ID, CreatedAt: user.CreatedAt, UpdatedAt: user.UpdatedAt, UUID: user.UUID, Username: user.Username, NickName: user.NickName, HeaderImg: user.HeaderImg, AuthorityID: user.AuthorityID, Authority: convertAuthority(user.Authority), Authorities: authorities, DeptID: user.DeptID, Department: department, Departments: departments, Positions: positions, Phone: user.Phone, Email: user.Email, Enable: user.Enable, OriginSetting: user.OriginSetting, PasswordUpdatedAt: user.PasswordUpdatedAt}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user