优化结构
This commit is contained in:
@@ -8,5 +8,10 @@ grouped by role:
|
||||
- `router/`: resource route registration and the system route registrar
|
||||
- `httpx/`: system adapter for shared response and cookie helpers
|
||||
|
||||
Cross-cutting route policy lives in `internal/routecatalog`: public/private
|
||||
Swagger security, operation-audit flags, sensitive request-body handling and
|
||||
API group/description metadata must be declared there. Router contract tests
|
||||
keep the catalog aligned with the Gin registrations.
|
||||
|
||||
Keep new files in the matching role directory instead of adding transport files
|
||||
to the root package.
|
||||
|
||||
+19
-193
@@ -12,6 +12,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"kra/internal/conf"
|
||||
"kra/internal/routecatalog"
|
||||
"kra/internal/server/handler"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -64,6 +65,24 @@ func TestGinRouteContract(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGinRoutesMatchSharedCatalog(t *testing.T) {
|
||||
engine := NewGinEngine(conf.NewRuntime(nil, &conf.AdminBackend{}), nil, emptyHandlers(), nil, nil, nil, nil, "test")
|
||||
registered := make(map[string]struct{}, len(engine.Routes()))
|
||||
for _, route := range engine.Routes() {
|
||||
key := route.Method + " " + route.Path
|
||||
registered[key] = struct{}{}
|
||||
if _, ok := routecatalog.Lookup(route.Method, route.Path); !ok {
|
||||
t.Errorf("registered route has no descriptor: %s", key)
|
||||
}
|
||||
}
|
||||
for _, descriptor := range routecatalog.Descriptors() {
|
||||
key := descriptor.Method + " " + descriptor.Path
|
||||
if _, ok := registered[key]; !ok {
|
||||
t.Errorf("route descriptor is not registered: %s", key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGinStartupLogsEveryRegisteredRoute(t *testing.T) {
|
||||
var output bytes.Buffer
|
||||
logger := slog.New(slog.NewJSONHandler(&output, nil))
|
||||
@@ -229,196 +248,3 @@ func TestLocalStorageRouteIsNotRegisteredForRemoteStorage(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const expectedGinRouteContract = `DELETE /api/deleteApisByIds
|
||||
DELETE /dataAccessLog/deleteDataAccessLogByIds
|
||||
DELETE /department/deleteDepartment
|
||||
DELETE /info/deleteInfo
|
||||
DELETE /info/deleteInfoByIds
|
||||
DELETE /mediaUpload/:uploadId
|
||||
DELETE /position/deletePosition
|
||||
DELETE /sysDictionary/deleteSysDictionary
|
||||
DELETE /sysDictionaryDetail/deleteSysDictionaryDetail
|
||||
DELETE /sysError/deleteSysError
|
||||
DELETE /sysError/deleteSysErrorByIds
|
||||
DELETE /sysExportTemplate/deleteSysExportTemplate
|
||||
DELETE /sysExportTemplate/deleteSysExportTemplateByIds
|
||||
DELETE /sysLoginLog/deleteLoginLog
|
||||
DELETE /sysLoginLog/deleteLoginLogByIds
|
||||
DELETE /sysOperationRecord/deleteSysOperationRecord
|
||||
DELETE /sysOperationRecord/deleteSysOperationRecordByIds
|
||||
DELETE /sysParams/deleteSysParams
|
||||
DELETE /sysParams/deleteSysParamsByIds
|
||||
DELETE /sysVersion/deleteSysVersion
|
||||
DELETE /sysVersion/deleteSysVersionByIds
|
||||
DELETE /timedTask/deleteTimedTask
|
||||
DELETE /user/deleteUser
|
||||
GET /api/freshCasbin
|
||||
GET /api/getApiGroups
|
||||
GET /api/getApiRoles
|
||||
GET /api/syncApi
|
||||
GET /attachmentCategory/getCategoryList
|
||||
GET /authority/getDataScopeDepts
|
||||
GET /authority/getUsersByAuthority
|
||||
GET /department/findDepartment
|
||||
GET /department/getDepartmentUsers
|
||||
GET /fileUploadAndDownload/findFile
|
||||
GET /health
|
||||
GET /info/findInfo
|
||||
GET /info/getInfoDataSource
|
||||
GET /info/getInfoList
|
||||
GET /info/getInfoPublic
|
||||
GET /logViewer/content
|
||||
GET /logViewer/dates
|
||||
GET /logViewer/files
|
||||
GET /menu/getMenuRoles
|
||||
GET /integration/configs/:kind
|
||||
GET /integration/configs/:kind/:provider
|
||||
GET /payment/orders
|
||||
GET /payment/orders/:provider/:tradeNo
|
||||
GET /position/findPosition
|
||||
GET /position/getPositionUsers
|
||||
GET /securityConfig/getSecurityConfig
|
||||
GET /swagger/*any
|
||||
GET /sysDictionary/exportSysDictionary
|
||||
GET /sysDictionary/findSysDictionary
|
||||
GET /sysDictionary/getSysDictionaryList
|
||||
GET /sysDictionary/getSysDictionaryListWithDetails
|
||||
GET /sysDictionaryDetail/findSysDictionaryDetail
|
||||
GET /sysDictionaryDetail/getDictionaryDetailsByParent
|
||||
GET /sysDictionaryDetail/getDictionaryPath
|
||||
GET /sysDictionaryDetail/getDictionaryTreeList
|
||||
GET /sysDictionaryDetail/getDictionaryTreeListByType
|
||||
GET /sysDictionaryDetail/getSysDictionaryDetailList
|
||||
GET /sysError/findSysError
|
||||
GET /sysError/getSysErrorList
|
||||
GET /sysExportTemplate/exportExcel
|
||||
GET /sysExportTemplate/exportExcelByToken
|
||||
GET /sysExportTemplate/exportTemplate
|
||||
GET /sysExportTemplate/exportTemplateByToken
|
||||
GET /sysExportTemplate/findSysExportTemplate
|
||||
GET /sysExportTemplate/getSysExportTemplateList
|
||||
GET /sysExportTemplate/previewSQL
|
||||
GET /sysLoginLog/findLoginLog
|
||||
GET /sysLoginLog/getLoginLogList
|
||||
GET /sysOperationRecord/findSysOperationRecord
|
||||
GET /sysOperationRecord/getSysOperationRecordList
|
||||
GET /sysParams/findSysParams
|
||||
GET /sysParams/getSysParam
|
||||
GET /sysParams/getSysParamsList
|
||||
GET /sysVersion/downloadVersionJson
|
||||
GET /sysVersion/findSysVersion
|
||||
GET /sysVersion/getSysVersionList
|
||||
GET /timedTask/alertStream
|
||||
GET /timedTask/getRegisteredMethods
|
||||
GET /timedTask/getTimedTaskList
|
||||
GET /timedTask/getTimedTaskLogList
|
||||
GET /user/getUserInfo
|
||||
POST /api/createApi
|
||||
POST /api/deleteApi
|
||||
POST /api/enterSyncApi
|
||||
POST /api/getAllApis
|
||||
POST /api/getApiById
|
||||
POST /api/getApiList
|
||||
POST /api/ignoreApi
|
||||
POST /api/setApiRoles
|
||||
POST /api/updateApi
|
||||
POST /attachmentCategory/addCategory
|
||||
POST /attachmentCategory/deleteCategory
|
||||
POST /authority/copyAuthority
|
||||
POST /authority/createAuthority
|
||||
POST /authority/deleteAuthority
|
||||
POST /authority/getAuthorityList
|
||||
POST /authority/setDataScope
|
||||
POST /authority/setRoleUsers
|
||||
POST /authorityBtn/canRemoveAuthorityBtn
|
||||
POST /authorityBtn/getAuthorityBtn
|
||||
POST /authorityBtn/setAuthorityBtn
|
||||
POST /base/captcha
|
||||
POST /base/login
|
||||
POST /casbin/getPolicyPathByAuthorityId
|
||||
POST /casbin/updateCasbin
|
||||
POST /dataAccessLog/getDataAccessLogList
|
||||
POST /department/createDepartment
|
||||
POST /department/getDepartmentList
|
||||
POST /department/setDepartmentUsers
|
||||
POST /email/emailTest
|
||||
POST /email/sendEmail
|
||||
POST /fileUploadAndDownload/deleteFile
|
||||
POST /fileUploadAndDownload/deleteFiles
|
||||
POST /fileUploadAndDownload/editFileName
|
||||
POST /fileUploadAndDownload/getFileList
|
||||
POST /fileUploadAndDownload/importURL
|
||||
POST /fileUploadAndDownload/listOssFiles
|
||||
POST /fileUploadAndDownload/upload
|
||||
POST /info/createInfo
|
||||
POST /init/checkdb
|
||||
POST /init/initdb
|
||||
POST /integration/configs/:kind/:provider/test
|
||||
POST /jwt/jsonInBlacklist
|
||||
POST /mediaUpload/chunk
|
||||
POST /mediaUpload/complete
|
||||
POST /mediaUpload/init
|
||||
POST /menu/addBaseMenu
|
||||
POST /menu/addMenuAuthority
|
||||
POST /menu/deleteBaseMenu
|
||||
POST /menu/getBaseMenuById
|
||||
POST /menu/getBaseMenuTree
|
||||
POST /menu/getMenu
|
||||
POST /menu/getMenuAuthority
|
||||
POST /menu/getMenuList
|
||||
POST /menu/setMenuRoles
|
||||
POST /menu/updateBaseMenu
|
||||
POST /payment/callback/:provider
|
||||
POST /payment/create
|
||||
POST /payment/fulfill
|
||||
POST /payment/order
|
||||
POST /payment/orders/:provider/:tradeNo/fulfill
|
||||
POST /payment/orders/:provider/:tradeNo/refund
|
||||
POST /payment/query
|
||||
POST /payment/refund
|
||||
POST /position/createPosition
|
||||
POST /position/getPositionList
|
||||
POST /position/setPositionUsers
|
||||
POST /securityConfig/setSecurityConfig
|
||||
POST /sysApiToken/createApiToken
|
||||
POST /sysApiToken/deleteApiToken
|
||||
POST /sysApiToken/getApiTokenList
|
||||
POST /sysDictionary/createSysDictionary
|
||||
POST /sysDictionary/importSysDictionary
|
||||
POST /sysDictionaryDetail/createSysDictionaryDetail
|
||||
POST /sysError/createSysError
|
||||
POST /sysExportTemplate/createSysExportTemplate
|
||||
POST /sysExportTemplate/importExcel
|
||||
POST /sysParams/createSysParams
|
||||
POST /sysVersion/exportVersion
|
||||
POST /sysVersion/importVersion
|
||||
POST /system/getServerInfo
|
||||
POST /system/getSystemConfig
|
||||
POST /system/reloadSystem
|
||||
POST /system/setSystemConfig
|
||||
POST /timedTask/createTimedTask
|
||||
POST /timedTask/toggleTimedTask
|
||||
POST /timedTask/triggerTimedTask
|
||||
POST /user/admin_register
|
||||
POST /user/changePassword
|
||||
POST /user/getUserList
|
||||
POST /user/resetPassword
|
||||
POST /user/setUserAuthorities
|
||||
POST /user/setUserAuthority
|
||||
POST /user/setUserDepartments
|
||||
POST /user/setUserPositions
|
||||
PUT /authority/updateAuthority
|
||||
PUT /department/updateDepartment
|
||||
PUT /info/updateInfo
|
||||
PUT /integration/configs/:kind/:provider
|
||||
PUT /position/updatePosition
|
||||
PUT /sysDictionary/updateSysDictionary
|
||||
PUT /sysDictionaryDetail/updateSysDictionaryDetail
|
||||
PUT /sysError/updateSysError
|
||||
PUT /sysExportTemplate/updateSysExportTemplate
|
||||
PUT /sysParams/updateSysParams
|
||||
PUT /timedTask/updateTimedTask
|
||||
PUT /user/setSelfInfo
|
||||
PUT /user/setSelfSetting
|
||||
PUT /user/setUserInfo`
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"time"
|
||||
|
||||
"kra/internal/conf"
|
||||
"kra/internal/routecatalog"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@@ -75,8 +76,9 @@ func AccessLog(runtime *conf.Runtime, logger *slog.Logger, version string) gin.H
|
||||
if config != nil && config.Zap != nil && config.Zap.AccessLogMaxBytes > 0 {
|
||||
logLimit = int(config.Zap.AccessLogMaxBytes)
|
||||
}
|
||||
paymentCallback := isPaymentCallbackPath(c.Request.URL.Path)
|
||||
paymentConfigWrite := isPaymentIntegrationConfigWrite(c.Request.Method, c.Request.URL.Path)
|
||||
bodyPolicy := routecatalog.BodyPolicyFor(c.Request.Method, c.Request.URL.Path)
|
||||
paymentCallback := bodyPolicy == routecatalog.BodyPolicyPaymentCallback
|
||||
paymentConfigWrite := bodyPolicy == routecatalog.BodyPolicyPaymentConfig
|
||||
requestText := ""
|
||||
if paymentCallback {
|
||||
requestText = paymentCallbackSummary(requestBody, c.GetHeader("Content-Type"))
|
||||
@@ -159,16 +161,6 @@ func isMediaUploadRoute(route string) bool {
|
||||
strings.HasSuffix(route, "/mediaUpload/chunk")
|
||||
}
|
||||
|
||||
func isPaymentCallbackPath(path string) bool {
|
||||
parts := strings.Split(strings.Trim(path, "/"), "/")
|
||||
for index := 0; index+1 < len(parts); index++ {
|
||||
if parts[index] == "payment" && parts[index+1] == "callback" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func paymentCallbackProvider(path string) string {
|
||||
parts := strings.Split(strings.Trim(path, "/"), "/")
|
||||
for index := 0; index+2 < len(parts); index++ {
|
||||
@@ -179,6 +171,12 @@ func paymentCallbackProvider(path string) string {
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
// isPaymentIntegrationConfigWrite is kept as a compatibility seam for
|
||||
// middleware tests; policy ownership lives in routecatalog.
|
||||
func isPaymentIntegrationConfigWrite(method, path string) bool {
|
||||
return routecatalog.BodyPolicyFor(method, path) == routecatalog.BodyPolicyPaymentConfig
|
||||
}
|
||||
|
||||
func paymentCallbackSummary(body []byte, contentType string) string {
|
||||
mediaType, _, err := mime.ParseMediaType(contentType)
|
||||
if err != nil || mediaType == "" {
|
||||
@@ -191,19 +189,6 @@ func paymentCallbackSummary(body []byte, contentType string) string {
|
||||
return "[支付回调正文已省略 body_bytes=" + strconv.Itoa(len(body)) + " body_sha256=" + hex.EncodeToString(digest[:]) + " content_type=" + mediaType + "]"
|
||||
}
|
||||
|
||||
func isPaymentIntegrationConfigWrite(method, path string) bool {
|
||||
if method != http.MethodPut {
|
||||
return false
|
||||
}
|
||||
parts := strings.Split(strings.Trim(path, "/"), "/")
|
||||
for index := 0; index+3 < len(parts); index++ {
|
||||
if parts[index] == "integration" && parts[index+1] == "configs" && parts[index+2] == "payment" && parts[index+3] != "" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func paymentConfigSummary(body []byte) string {
|
||||
digest := sha256.Sum256(body)
|
||||
return "[支付配置正文已省略 body_bytes=" + strconv.Itoa(len(body)) + " body_sha256=" + hex.EncodeToString(digest[:]) + "]"
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"time"
|
||||
|
||||
"kra/internal/conf"
|
||||
"kra/internal/routecatalog"
|
||||
"kra/internal/service"
|
||||
"kra/internal/service/dto"
|
||||
|
||||
@@ -27,8 +28,12 @@ func OperationAudit(runtime *conf.Runtime, recorder *service.AuditRecorder) gin.
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
routePath := c.FullPath()
|
||||
if routePath == "" {
|
||||
routePath = c.Request.URL.Path
|
||||
}
|
||||
path := c.Request.URL.Path
|
||||
if !recordsOperation(c.Request.Method, path) {
|
||||
if !routecatalog.ShouldAudit(c.Request.Method, routePath) {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
@@ -86,7 +91,7 @@ func OperationAudit(runtime *conf.Runtime, recorder *service.AuditRecorder) gin.
|
||||
}
|
||||
errorMessage := c.Errors.ByType(gin.ErrorTypePrivate).String()
|
||||
operationBody := operationRequestBody(requestBody, c.GetHeader("Content-Type"), maxBytes)
|
||||
if isPaymentIntegrationConfigWrite(c.Request.Method, path) {
|
||||
if routecatalog.BodyPolicyFor(c.Request.Method, path) == routecatalog.BodyPolicyPaymentConfig {
|
||||
operationBody = paymentConfigSummary(requestBody)
|
||||
}
|
||||
if err := recorder.RecordOperationRequest(c.Request.Context(), &dto.OperationRecordRequest{IP: c.ClientIP(), Method: c.Request.Method, Path: path, Status: status, LatencyMS: time.Since(started).Milliseconds(), Agent: c.Request.UserAgent(), ErrorMessage: errorMessage, Body: operationBody, Response: responseBody, UserID: userID, RequestID: stringValue(requestID), TraceID: stringValueFromContext(c, "trace_id"), DeviceID: c.GetHeader("X-Device-Id")}); err != nil {
|
||||
@@ -161,62 +166,8 @@ func isDownloadResponse(c *gin.Context) bool {
|
||||
strings.Contains(header.Get("Content-Transfer-Encoding"), "binary")
|
||||
}
|
||||
|
||||
// recordsOperation mirrors the routes on which operation records are enabled.
|
||||
// Matching by suffix keeps the behavior stable when router-prefix is configured.
|
||||
// recordsOperation remains as a small compatibility helper for tests and
|
||||
// custom middleware chains that do not have a Gin context.
|
||||
func recordsOperation(method, path string) bool {
|
||||
for route := range operationRoutes {
|
||||
parts := strings.SplitN(route, " ", 2)
|
||||
if len(parts) != 2 || parts[0] != method || !operationPathMatches(parts[1], path) {
|
||||
continue
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return routecatalog.ShouldAudit(method, path)
|
||||
}
|
||||
|
||||
func operationPathMatches(pattern, path string) bool {
|
||||
patternParts := strings.Split(strings.Trim(pattern, "/"), "/")
|
||||
pathParts := strings.Split(strings.Trim(path, "/"), "/")
|
||||
if len(pathParts) < len(patternParts) {
|
||||
return false
|
||||
}
|
||||
pathParts = pathParts[len(pathParts)-len(patternParts):]
|
||||
for index, patternPart := range patternParts {
|
||||
if strings.HasPrefix(patternPart, "*") {
|
||||
return index <= len(pathParts)
|
||||
}
|
||||
if index >= len(pathParts) || (strings.HasPrefix(patternPart, ":") == false && patternPart != pathParts[index]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return len(patternParts) == len(pathParts)
|
||||
}
|
||||
|
||||
var operationRoutes = func() map[string]struct{} {
|
||||
values := []string{
|
||||
"POST /user/admin_register", "POST /user/changePassword", "POST /user/setUserAuthority", "DELETE /user/deleteUser", "PUT /user/setUserInfo", "PUT /user/setSelfInfo", "POST /user/setUserAuthorities", "POST /user/setUserDepartments", "POST /user/setUserPositions", "POST /user/resetPassword", "PUT /user/setSelfSetting",
|
||||
"GET /api/getApiGroups", "GET /api/syncApi", "POST /api/ignoreApi", "POST /api/enterSyncApi", "POST /api/createApi", "POST /api/deleteApi", "POST /api/getApiById", "POST /api/updateApi", "DELETE /api/deleteApisByIds", "POST /api/setApiRoles", "POST /casbin/updateCasbin",
|
||||
"POST /authority/createAuthority", "POST /authority/deleteAuthority", "PUT /authority/updateAuthority", "POST /authority/copyAuthority", "POST /authority/setDataScope", "POST /authority/setRoleUsers",
|
||||
"POST /menu/addBaseMenu", "POST /menu/addMenuAuthority", "POST /menu/deleteBaseMenu", "POST /menu/updateBaseMenu", "POST /menu/setMenuRoles",
|
||||
"POST /department/createDepartment", "PUT /department/updateDepartment", "DELETE /department/deleteDepartment", "POST /department/setDepartmentUsers",
|
||||
"POST /position/createPosition", "PUT /position/updatePosition", "DELETE /position/deletePosition", "POST /position/setPositionUsers",
|
||||
"POST /sysDictionary/createSysDictionary", "DELETE /sysDictionary/deleteSysDictionary", "PUT /sysDictionary/updateSysDictionary", "POST /sysDictionary/importSysDictionary", "GET /sysDictionary/exportSysDictionary",
|
||||
"POST /sysDictionaryDetail/createSysDictionaryDetail", "DELETE /sysDictionaryDetail/deleteSysDictionaryDetail", "PUT /sysDictionaryDetail/updateSysDictionaryDetail",
|
||||
"POST /sysParams/createSysParams", "DELETE /sysParams/deleteSysParams", "DELETE /sysParams/deleteSysParamsByIds", "PUT /sysParams/updateSysParams",
|
||||
"POST /securityConfig/setSecurityConfig", "POST /system/setSystemConfig", "POST /system/reloadSystem",
|
||||
"POST /sysApiToken/createApiToken", "POST /sysApiToken/getApiTokenList", "POST /sysApiToken/deleteApiToken",
|
||||
"DELETE /sysVersion/deleteSysVersion", "DELETE /sysVersion/deleteSysVersionByIds", "POST /sysVersion/exportVersion", "POST /sysVersion/importVersion",
|
||||
"POST /sysExportTemplate/createSysExportTemplate", "DELETE /sysExportTemplate/deleteSysExportTemplate", "DELETE /sysExportTemplate/deleteSysExportTemplateByIds", "PUT /sysExportTemplate/updateSysExportTemplate", "POST /sysExportTemplate/importExcel",
|
||||
"DELETE /sysError/deleteSysError", "DELETE /sysError/deleteSysErrorByIds", "PUT /sysError/updateSysError",
|
||||
"DELETE /sysLoginLog/deleteLoginLog", "DELETE /sysLoginLog/deleteLoginLogByIds", "DELETE /dataAccessLog/deleteDataAccessLogByIds",
|
||||
"POST /timedTask/createTimedTask", "PUT /timedTask/updateTimedTask", "DELETE /timedTask/deleteTimedTask", "POST /timedTask/toggleTimedTask", "POST /timedTask/triggerTimedTask",
|
||||
"POST /info/createInfo", "DELETE /info/deleteInfo", "DELETE /info/deleteInfoByIds", "PUT /info/updateInfo", "POST /email/emailTest", "POST /email/sendEmail",
|
||||
"PUT /integration/configs/:kind/:provider", "POST /integration/configs/:kind/:provider/test", "DELETE /integration/configs/:kind/:provider",
|
||||
"POST /payment/create", "POST /payment/query", "POST /payment/refund", "POST /payment/orders/:provider/:tradeNo/refund", "POST /payment/fulfill", "POST /payment/orders/:provider/:tradeNo/fulfill", "POST /payment/providers/:provider/test",
|
||||
}
|
||||
out := make(map[string]struct{}, len(values))
|
||||
for _, value := range values {
|
||||
out[value] = struct{}{}
|
||||
}
|
||||
return out
|
||||
}()
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"kra/internal/service"
|
||||
"kra/internal/routecatalog"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
swaggerFiles "github.com/swaggo/files"
|
||||
@@ -87,7 +87,8 @@ func buildSwaggerDocument(routes []gin.RouteInfo, prefix, version string) string
|
||||
apiPath = "/"
|
||||
}
|
||||
documentPath := swaggerPathParameter.ReplaceAllString(apiPath, `{$1}`)
|
||||
group, description := service.RouteMetadata(route.Method, apiPath)
|
||||
descriptor := routecatalog.Describe(route.Method, apiPath)
|
||||
group, description := descriptor.Group, descriptor.Description
|
||||
if description == "" {
|
||||
description = route.Method + " " + apiPath
|
||||
}
|
||||
@@ -103,7 +104,7 @@ func buildSwaggerDocument(routes []gin.RouteInfo, prefix, version string) string
|
||||
if parameters := swaggerPathParameters(apiPath); len(parameters) > 0 {
|
||||
operation["parameters"] = parameters
|
||||
}
|
||||
if !swaggerPublicPath(apiPath) {
|
||||
if !descriptor.Public {
|
||||
operation["security"] = []map[string][]string{{"ApiKeyAuth": {}}}
|
||||
}
|
||||
if paths[documentPath] == nil {
|
||||
@@ -156,16 +157,3 @@ func swaggerPathParameters(path string) []map[string]any {
|
||||
}
|
||||
return parameters
|
||||
}
|
||||
|
||||
func swaggerPublicPath(path string) bool {
|
||||
for _, marker := range []string{
|
||||
"/health", "/base/login", "/base/captcha", "/init/checkdb", "/init/initdb",
|
||||
"/sysExportTemplate/exportExcelByToken", "/sysExportTemplate/exportTemplateByToken",
|
||||
"/sysError/createSysError", "/info/getInfoPublic",
|
||||
} {
|
||||
if path == marker {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ func TestSwaggerMarksOnlyPublicRoutesWithoutAuth(t *testing.T) {
|
||||
{Method: "GET", Path: "/api/freshCasbin"},
|
||||
{Method: "GET", Path: "/info/getInfoDataSource"},
|
||||
{Method: "GET", Path: "/info/getInfoPublic"},
|
||||
{Method: "POST", Path: "/payment/callback/:provider"},
|
||||
}, "", "test")
|
||||
var payload struct {
|
||||
Paths map[string]map[string]map[string]any `json:"paths"`
|
||||
@@ -24,7 +25,9 @@ func TestSwaggerMarksOnlyPublicRoutesWithoutAuth(t *testing.T) {
|
||||
t.Fatalf("private route %s was marked public", path)
|
||||
}
|
||||
}
|
||||
if _, ok := payload.Paths["/info/getInfoPublic"]["get"]["security"]; ok {
|
||||
t.Fatal("public route /info/getInfoPublic requires authentication")
|
||||
for path, method := range map[string]string{"/info/getInfoPublic": "get", "/payment/callback/{provider}": "post"} {
|
||||
if _, ok := payload.Paths[path][method]["security"]; ok {
|
||||
t.Fatalf("public route %s requires authentication", path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user