diff --git a/internal/middleware/auth.go b/internal/middleware/auth.go index ac0ae01..3be9609 100644 --- a/internal/middleware/auth.go +++ b/internal/middleware/auth.go @@ -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) diff --git a/internal/router/router.go b/internal/router/router.go index b29b9fa..7543bbe 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -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 { ` 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("

404 Not Found

Frontend assets not found. Please run 'npm run build' or check dev server.

Go Home")) + ctx.Abort() + return + } + fallback := `404 Not Found - -

Not Found. Redirecting to home...

+ +

Not Found. Redirecting...

` ctx.Header("Content-Type", "text/html; charset=utf-8") ctx.Data(status, "text/html", []byte(fallback)) diff --git a/web/src/api/index.ts b/web/src/api/index.ts index 5b1fa50..b370e72 100644 --- a/web/src/api/index.ts +++ b/web/src/api/index.ts @@ -344,7 +344,7 @@ export interface ExecutionResult { } export interface TaskListResponse { - data: Task[] + list: Task[] total: number page: number page_size: number @@ -365,7 +365,7 @@ export interface EnvVar { } export interface EnvListResponse { - data: EnvVar[] + list: EnvVar[] total: number page: number page_size: number @@ -396,7 +396,7 @@ export interface TaskLog { } export interface LogListResponse { - data: TaskLog[] + list: TaskLog[] total: number page: number page_size: number @@ -457,7 +457,7 @@ export interface LoginLog { } export interface LoginLogListResponse { - data: LoginLog[] + list: LoginLog[] total: number page: number page_size: number diff --git a/web/src/views/environments/Environments.vue b/web/src/views/environments/Environments.vue index 42a8032..9d6d16a 100644 --- a/web/src/views/environments/Environments.vue +++ b/web/src/views/environments/Environments.vue @@ -34,10 +34,10 @@ let searchTimer: ReturnType | null = null async function loadEnvVars() { try { const res = await api.env.list({ page: currentPage.value, page_size: pageSize.value, name: filterName.value || undefined }) - envVars.value = res.data + envVars.value = res.list total.value = res.total // 初始化显示状态,根据数据库的 hidden 状态同步显示 - res.data.forEach(env => { + res.list.forEach(env => { showValues.value[env.id] = !env.hidden }) } catch { toast.error('加载环境变量失败') } @@ -251,7 +251,8 @@ onMounted(loadEnvVars)
-

关联任务 ({{ associatedTasks.length }})

+

关联任务 ({{ + associatedTasks.length }})

{{ task.name }}
- {{ task.id }} + {{ task.id }}
diff --git a/web/src/views/history/History.vue b/web/src/views/history/History.vue index 62a0e76..d7b1ebe 100644 --- a/web/src/views/history/History.vue +++ b/web/src/views/history/History.vue @@ -76,7 +76,7 @@ async function loadLogs() { params.status = filterStatus.value } const response = await api.logs.list(params) - logs.value = response.data + logs.value = response.list total.value = response.total } catch { toast.error('加载日志失败') @@ -251,12 +251,12 @@ async function handleDeleteLog() { try { await api.logs.delete(deleteLogId.value) toast.success('该日志已删除') - + // 如果当前选中的是这条日志,关闭详情页 if (selectedLog.value?.id === deleteLogId.value) { closeDetail() } - + showDeleteDialog.value = false loadLogs() } catch (err: any) { @@ -340,7 +340,9 @@ watch(() => route.query, (newQuery) => { - @@ -382,7 +384,8 @@ watch(() => route.query, (newQuery) => { ]" @click="selectLog(log)">
- #{{ total - (currentPage - 1) * pageSize - index }} + #{{ total - (currentPage - 1) * pageSize - index + }} @@ -415,16 +418,19 @@ watch(() => route.query, (newQuery) => {
{{ formatDuration(log.duration) - }} + }} - {{ formatDuration(log.duration) - }} + }} - @@ -488,7 +496,8 @@ watch(() => route.query, (newQuery) => {
-