feat: openapi supoort -- fix list page

This commit is contained in:
engigu
2026-03-05 22:56:15 +08:00
parent 863a298609
commit 40c3c58312
7 changed files with 99 additions and 61 deletions
+19 -4
View File
@@ -178,11 +178,12 @@ func ClearAuthCookie(c *gin.Context) {
// SwaggerAuth Swagger 认证中间件 (Basic Auth)
func SwaggerAuth() gin.HandlerFunc {
settingsSvc := services.NewSettingsService()
return func(c *gin.Context) {
settingsSvc := services.NewSettingsService()
siteConfig := settingsSvc.GetSection(constant.SectionSite)
tokenJson, ok := siteConfig[constant.KeyOpenapiToken]
if !ok || tokenJson == "" {
tokenJson := siteConfig[constant.KeyOpenapiToken]
if tokenJson == "" {
c.Status(http.StatusNotFound)
c.Abort()
return
@@ -202,6 +203,20 @@ func SwaggerAuth() gin.HandlerFunc {
return
}
// 检查过期时间
if tokenConfig.ExpireAt != "" {
expire, err := time.ParseInLocation("2006/01/02", tokenConfig.ExpireAt, time.Local)
if err == nil {
// 包含当天,所以设置到当天 23:59:59
expire = expire.Add(24*time.Hour - time.Second)
if time.Now().After(expire) {
c.Status(http.StatusNotFound)
c.Abort()
return
}
}
}
_, password, hasAuth := c.Request.BasicAuth()
// 允许使用任意用户名,但密码必须匹配 OpenAPI Token
if hasAuth && password == tokenConfig.Token && tokenConfig.Token != "" {
@@ -209,7 +224,7 @@ func SwaggerAuth() gin.HandlerFunc {
return
}
// 认证失败,提示输入密码 (如果未提供认证)
// 未提供认证,触发浏览器登录弹窗
if !hasAuth {
c.Header("WWW-Authenticate", `Basic realm="OpenAPI Access Token (Any username)"`)
c.Status(http.StatusUnauthorized)
+25 -10
View File
@@ -89,7 +89,7 @@ func Setup(c *Controllers) *gin.Engine {
}
// OpenAPI documentation using Scalar UI (带 Basic Auth 认证)
router.GET("/openapi/*any", func(c *gin.Context) {
root.GET("/openapi/*any", func(c *gin.Context) {
settingsSvc := services.NewSettingsService()
siteConfig := settingsSvc.GetSection(constant.SectionSite)
tokenJson := siteConfig[constant.KeyOpenapiToken]
@@ -118,12 +118,13 @@ func Setup(c *Controllers) *gin.Engine {
return
}
// 获取内部路径(移除开头的斜杠)
path := strings.TrimPrefix(c.Param("any"), "/")
// 获取内部路径并标准化(移除前后的所有斜杠)
// c.Param("any") 对于 *any 匹配通常包含领先斜杠,如 "/index.html"
path := strings.Trim(c.Param("any"), "/")
// 1. 根路径或空路径 -> 重定向到 index.html
if path == "" || path == "/" {
c.Redirect(http.StatusMovedPermanently, urlPrefix+"/openapi/index.html")
if path == "" {
c.Redirect(http.StatusMovedPermanently, c.Request.URL.Path+"index.html")
return
}
@@ -145,7 +146,8 @@ func Setup(c *Controllers) *gin.Engine {
</body>
</html>`
c.Header("Content-Type", "text/html; charset=utf-8")
c.String(http.StatusOK, scalarHTML)
c.Status(http.StatusOK)
c.Writer.Write([]byte(scalarHTML))
c.Abort()
return
}
@@ -158,7 +160,7 @@ func Setup(c *Controllers) *gin.Engine {
return
}
// 4. 其余路径一律返回 SPA 的 404
// 其他未匹配路径 -> 返回 404 SPA 页面
serveSPA(c, urlPrefix, 404)
})
@@ -399,10 +401,23 @@ func Setup(c *Controllers) *gin.Engine {
func serveSPA(ctx *gin.Context, urlPrefix string, status int) {
data, err := static.ReadFile("index.html")
if err != nil {
// 如果读不到 index.html (如 dev 模式未 build),返回基础 HTML 触发前端路由
// 如果读不到 index.html (如 dev 模式未 build),返回基础 HTML
// 如果已经是 /404 路径,则不再重定向以免死循环
path := ctx.Request.URL.Path
if strings.HasSuffix(path, "/404") {
ctx.Data(status, "text/html; charset=utf-8", []byte("<!DOCTYPE html><html><body><h1>404 Not Found</h1><p>Frontend assets not found. Please run 'npm run build' or check dev server.</p><a href='/'>Go Home</a></body></html>"))
ctx.Abort()
return
}
fallback := `<!DOCTYPE html><html><head><meta charset="utf-8"/><title>404 Not Found</title></head><body>
<script>window.location.href = (window.__BASE_URL__ || "/") + "404";</script>
<p>Not Found. Redirecting to home...</p>
<script>
const baseUrl = window.__BASE_URL__ || "/";
if (!window.location.pathname.endsWith("/404")) {
window.location.href = baseUrl + (baseUrl.endsWith("/") ? "" : "/") + "404";
}
</script>
<p>Not Found. Redirecting...</p>
</body></html>`
ctx.Header("Content-Type", "text/html; charset=utf-8")
ctx.Data(status, "text/html", []byte(fallback))