feat: add api token call

This commit is contained in:
engigu
2026-02-28 10:52:53 +08:00
parent dd8a887399
commit 06cdc58a72
6 changed files with 158 additions and 8 deletions
+3 -2
View File
@@ -38,8 +38,9 @@ const (
KeyTitle = "title"
KeySubtitle = "subtitle"
KeyIcon = "icon"
KeyPageSize = "page_size"
KeyCookieDays = "cookie_days"
KeyPageSize = "page_size"
KeyCookieDays = "cookie_days"
KeyApiToken = "api_token"
// System Settings Key 常量
KeyInitialized = "initialized"
+38 -5
View File
@@ -5,8 +5,10 @@ import (
"runtime"
"strconv"
"encoding/json"
"fmt"
"os"
"strings"
"time"
"github.com/engigu/baihu-panel/internal/constant"
@@ -82,6 +84,16 @@ func (sc *SettingsController) ChangePassword(c *gin.Context) {
// GetSiteSettings 获取站点设置
func (sc *SettingsController) GetSiteSettings(c *gin.Context) {
settings := sc.settingsService.GetSection(constant.SectionSite)
// 解析 JSON 格式的 API Token
if tokenJson, ok := settings[constant.KeyApiToken]; ok && tokenJson != "" {
var tokenData map[string]string
if err := json.Unmarshal([]byte(tokenJson), &tokenData); err == nil {
settings["api_token"] = tokenData["token"]
settings["api_token_expire"] = tokenData["expire_at"]
}
}
utils.Success(c, settings)
}
@@ -100,11 +112,13 @@ func (sc *SettingsController) GetPublicSiteSettings(c *gin.Context) {
// UpdateSiteSettings 更新站点设置
func (sc *SettingsController) UpdateSiteSettings(c *gin.Context) {
var req struct {
Title string `json:"title"`
Subtitle string `json:"subtitle"`
Icon string `json:"icon"`
PageSize string `json:"page_size"`
CookieDays string `json:"cookie_days"`
Title string `json:"title"`
Subtitle string `json:"subtitle"`
Icon string `json:"icon"`
PageSize string `json:"page_size"`
CookieDays string `json:"cookie_days"`
ApiToken string `json:"api_token"`
ApiTokenExpire string `json:"api_token_expire"`
}
if err := c.ShouldBindJSON(&req); err != nil {
@@ -112,12 +126,24 @@ func (sc *SettingsController) UpdateSiteSettings(c *gin.Context) {
return
}
apiTokenJson := ""
if req.ApiToken != "" || req.ApiTokenExpire != "" {
tokenData := map[string]string{
"token": req.ApiToken,
"expire_at": req.ApiTokenExpire,
}
if b, err := json.Marshal(tokenData); err == nil {
apiTokenJson = string(b)
}
}
values := map[string]string{
constant.KeyTitle: req.Title,
constant.KeySubtitle: req.Subtitle,
constant.KeyIcon: req.Icon,
constant.KeyPageSize: req.PageSize,
constant.KeyCookieDays: req.CookieDays,
constant.KeyApiToken: apiTokenJson,
}
if err := sc.settingsService.SetSection(constant.SectionSite, values); err != nil {
@@ -128,6 +154,13 @@ func (sc *SettingsController) UpdateSiteSettings(c *gin.Context) {
utils.SuccessMsg(c, "保存成功")
}
// GenerateApiToken 随机生成API Token
func (sc *SettingsController) GenerateApiToken(c *gin.Context) {
utils.Success(c, gin.H{
"token": strings.ToLower(utils.RandomString(32)),
})
}
// GetSchedulerSettings 获取调度设置
func (sc *SettingsController) GetSchedulerSettings(c *gin.Context) {
settings := sc.settingsService.GetSection(constant.SectionScheduler)
+53
View File
@@ -1,7 +1,11 @@
package middleware
import (
"encoding/json"
"time"
"github.com/engigu/baihu-panel/internal/constant"
"github.com/engigu/baihu-panel/internal/services"
"github.com/engigu/baihu-panel/internal/utils"
"github.com/gin-gonic/gin"
@@ -9,7 +13,13 @@ import (
// AuthRequired 认证中间件
func AuthRequired() gin.HandlerFunc {
settingsSvc := services.NewSettingsService()
return func(c *gin.Context) {
// 校验 API Token (实验特性)
if checkApiToken(c, settingsSvc) {
return
}
token, err := c.Cookie(constant.CookieName)
if err != nil || token == "" {
utils.Unauthorized(c, "请先登录")
@@ -32,6 +42,49 @@ func AuthRequired() gin.HandlerFunc {
}
}
// checkApiToken 校验 API Token (实验特性,后续可能移除或重构)
// 返回 true 表示校验通过并已放行请求
func checkApiToken(c *gin.Context, settingsSvc *services.SettingsService) bool {
apiToken := c.GetHeader("X-API-Token")
if apiToken == "" {
return false
}
siteConfig := settingsSvc.GetSection(constant.SectionSite)
tokenJson, ok := siteConfig[constant.KeyApiToken]
if !ok || tokenJson == "" {
return false
}
var tokenData map[string]string
if err := json.Unmarshal([]byte(tokenJson), &tokenData); err != nil {
return false
}
expectedToken, ok := tokenData["token"]
if !ok || expectedToken == "" || apiToken != expectedToken {
return false
}
// 检查过期时间
if expireStr, ok := tokenData["expire_at"]; ok && expireStr != "" {
// 前端传来的时间格式是 YYYY-MM-DD,使用 2006-01-02 解析
expireDate, err := time.Parse("2006-01-02", expireStr)
if err == nil {
// 将过期时间设为当天的 23:59:59
expireDate = expireDate.Add(23*time.Hour + 59*time.Minute + 59*time.Second)
if time.Now().After(expireDate) {
return false
}
}
}
c.Set("userID", uint(1)) // 模拟 Admin 角色
c.Set("username", "api_token_user")
c.Next()
return true
}
// SetAuthCookie 设置认证 CookieexpireDays 为过期天数
func SetAuthCookie(c *gin.Context, token string, expireDays int) {
maxAge := 86400 * expireDays
+1
View File
@@ -189,6 +189,7 @@ func Setup(c *Controllers) *gin.Engine {
settings.POST("/password", c.Settings.ChangePassword)
settings.GET("/site", c.Settings.GetSiteSettings)
settings.PUT("/site", c.Settings.UpdateSiteSettings)
settings.POST("/site/api-token/generate", c.Settings.GenerateApiToken)
settings.GET("/paths", c.Settings.GetPaths)
settings.GET("/scheduler", c.Settings.GetSchedulerSettings)
settings.PUT("/scheduler", c.Settings.UpdateSchedulerSettings)