chore: remove api token function
This commit is contained in:
@@ -42,7 +42,6 @@ const (
|
|||||||
KeyIcon = "icon"
|
KeyIcon = "icon"
|
||||||
KeyPageSize = "page_size"
|
KeyPageSize = "page_size"
|
||||||
KeyCookieDays = "cookie_days"
|
KeyCookieDays = "cookie_days"
|
||||||
KeyApiToken = "api_token"
|
|
||||||
KeyOpenapiToken = "openapi_token"
|
KeyOpenapiToken = "openapi_token"
|
||||||
|
|
||||||
// Security Settings Key 常量
|
// Security Settings Key 常量
|
||||||
|
|||||||
@@ -89,15 +89,6 @@ func (sc *SettingsController) ChangePassword(c *gin.Context) {
|
|||||||
func (sc *SettingsController) GetSiteSettings(c *gin.Context) {
|
func (sc *SettingsController) GetSiteSettings(c *gin.Context) {
|
||||||
settings := sc.settingsService.GetSection(constant.SectionSite)
|
settings := sc.settingsService.GetSection(constant.SectionSite)
|
||||||
|
|
||||||
// 解析 JSON 格式的 API Token
|
|
||||||
if tokenJson, ok := settings[constant.KeyApiToken]; ok && tokenJson != "" {
|
|
||||||
var tokenConfig vo.TokenConfig
|
|
||||||
if err := json.Unmarshal([]byte(tokenJson), &tokenConfig); err == nil {
|
|
||||||
settings["api_token"] = tokenConfig.Token
|
|
||||||
settings["api_token_expire"] = tokenConfig.ExpireAt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 解析 JSON 格式的 OpenAPI Token
|
// 解析 JSON 格式的 OpenAPI Token
|
||||||
if tokenJson, ok := settings[constant.KeyOpenapiToken]; ok && tokenJson != "" {
|
if tokenJson, ok := settings[constant.KeyOpenapiToken]; ok && tokenJson != "" {
|
||||||
var tokenConfig vo.TokenConfig
|
var tokenConfig vo.TokenConfig
|
||||||
@@ -135,8 +126,6 @@ func (sc *SettingsController) UpdateSiteSettings(c *gin.Context) {
|
|||||||
Icon string `json:"icon"`
|
Icon string `json:"icon"`
|
||||||
PageSize string `json:"page_size"`
|
PageSize string `json:"page_size"`
|
||||||
CookieDays string `json:"cookie_days"`
|
CookieDays string `json:"cookie_days"`
|
||||||
ApiToken string `json:"api_token"`
|
|
||||||
ApiTokenExpire string `json:"api_token_expire"`
|
|
||||||
OpenapiEnabled bool `json:"openapi_enabled"`
|
OpenapiEnabled bool `json:"openapi_enabled"`
|
||||||
OpenapiToken string `json:"openapi_token"`
|
OpenapiToken string `json:"openapi_token"`
|
||||||
OpenapiTokenExpire string `json:"openapi_token_expire"`
|
OpenapiTokenExpire string `json:"openapi_token_expire"`
|
||||||
@@ -147,17 +136,6 @@ func (sc *SettingsController) UpdateSiteSettings(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
apiTokenJson := ""
|
|
||||||
if req.ApiToken != "" || req.ApiTokenExpire != "" {
|
|
||||||
tokenConfig := vo.TokenConfig{
|
|
||||||
Token: req.ApiToken,
|
|
||||||
ExpireAt: req.ApiTokenExpire,
|
|
||||||
}
|
|
||||||
if b, err := json.Marshal(tokenConfig); err == nil {
|
|
||||||
apiTokenJson = string(b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
openapiTokenJson := ""
|
openapiTokenJson := ""
|
||||||
if req.OpenapiToken != "" || req.OpenapiTokenExpire != "" || req.OpenapiEnabled {
|
if req.OpenapiToken != "" || req.OpenapiTokenExpire != "" || req.OpenapiEnabled {
|
||||||
tokenConfig := vo.TokenConfig{
|
tokenConfig := vo.TokenConfig{
|
||||||
@@ -176,7 +154,6 @@ func (sc *SettingsController) UpdateSiteSettings(c *gin.Context) {
|
|||||||
constant.KeyIcon: req.Icon,
|
constant.KeyIcon: req.Icon,
|
||||||
constant.KeyPageSize: req.PageSize,
|
constant.KeyPageSize: req.PageSize,
|
||||||
constant.KeyCookieDays: req.CookieDays,
|
constant.KeyCookieDays: req.CookieDays,
|
||||||
constant.KeyApiToken: apiTokenJson,
|
|
||||||
constant.KeyOpenapiToken: openapiTokenJson,
|
constant.KeyOpenapiToken: openapiTokenJson,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,13 +165,6 @@ func (sc *SettingsController) UpdateSiteSettings(c *gin.Context) {
|
|||||||
utils.SuccessMsg(c, "保存成功")
|
utils.SuccessMsg(c, "保存成功")
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateApiToken 随机生成API Token
|
|
||||||
func (sc *SettingsController) GenerateApiToken(c *gin.Context) {
|
|
||||||
utils.Success(c, gin.H{
|
|
||||||
"token": strings.ToLower(utils.RandomString(32)),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// GenerateOpenapiToken 随机生成OpenAPI Token
|
// GenerateOpenapiToken 随机生成OpenAPI Token
|
||||||
func (sc *SettingsController) GenerateOpenapiToken(c *gin.Context) {
|
func (sc *SettingsController) GenerateOpenapiToken(c *gin.Context) {
|
||||||
utils.Success(c, gin.H{
|
utils.Success(c, gin.H{
|
||||||
|
|||||||
@@ -17,13 +17,7 @@ import (
|
|||||||
|
|
||||||
// AuthRequired 认证中间件
|
// AuthRequired 认证中间件
|
||||||
func AuthRequired() gin.HandlerFunc {
|
func AuthRequired() gin.HandlerFunc {
|
||||||
settingsSvc := services.NewSettingsService()
|
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
// 校验 API Token (实验特性)
|
|
||||||
if checkApiToken(c, settingsSvc) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
token, err := c.Cookie(constant.CookieName)
|
token, err := c.Cookie(constant.CookieName)
|
||||||
if err != nil || token == "" {
|
if err != nil || token == "" {
|
||||||
utils.Unauthorized(c, "请先登录")
|
utils.Unauthorized(c, "请先登录")
|
||||||
@@ -56,56 +50,6 @@ 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 tokenConfig vo.TokenConfig
|
|
||||||
if err := json.Unmarshal([]byte(tokenJson), &tokenConfig); err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if tokenConfig.Token == "" || apiToken != tokenConfig.Token {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查过期时间
|
|
||||||
if tokenConfig.ExpireAt != "" {
|
|
||||||
// 前端传来的时间格式是 YYYY-MM-DD,使用 2006-01-02 解析
|
|
||||||
expireDate, err := time.Parse("2006-01-02", tokenConfig.ExpireAt)
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 模拟 Admin 角色,必须通过实际存在的 admin 用户 ID 来关联
|
|
||||||
var adminUser models.User
|
|
||||||
if err := database.DB.Where("role = ?", "admin").First(&adminUser).Error; err != nil {
|
|
||||||
utils.Unauthorized(c, "未找到管理员账户,API Token 校验失败")
|
|
||||||
c.Abort()
|
|
||||||
return true // 返回 true 表示中间件已处理并截断了请求
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Set("userID", adminUser.ID)
|
|
||||||
c.Set("username", adminUser.Username)
|
|
||||||
c.Next()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// OpenapiRequired OpenAPI 认证中间件
|
// OpenapiRequired OpenAPI 认证中间件
|
||||||
func OpenapiRequired() gin.HandlerFunc {
|
func OpenapiRequired() gin.HandlerFunc {
|
||||||
settingsSvc := services.NewSettingsService()
|
settingsSvc := services.NewSettingsService()
|
||||||
|
|||||||
@@ -343,7 +343,6 @@ func registerSettingsRoutes(g *gin.RouterGroup, c *Controllers) {
|
|||||||
settings.POST("/password", c.Settings.ChangePassword)
|
settings.POST("/password", c.Settings.ChangePassword)
|
||||||
settings.GET("/site", c.Settings.GetSiteSettings)
|
settings.GET("/site", c.Settings.GetSiteSettings)
|
||||||
settings.PUT("/site", c.Settings.UpdateSiteSettings)
|
settings.PUT("/site", c.Settings.UpdateSiteSettings)
|
||||||
settings.POST("/site/api-token/generate", c.Settings.GenerateApiToken)
|
|
||||||
settings.POST("/site/openapi-token/generate", c.Settings.GenerateOpenapiToken)
|
settings.POST("/site/openapi-token/generate", c.Settings.GenerateOpenapiToken)
|
||||||
settings.GET("/paths", c.Settings.GetPaths)
|
settings.GET("/paths", c.Settings.GetPaths)
|
||||||
settings.GET("/scheduler", c.Settings.GetSchedulerSettings)
|
settings.GET("/scheduler", c.Settings.GetSchedulerSettings)
|
||||||
|
|||||||
@@ -132,7 +132,6 @@ export const api = {
|
|||||||
getPublicSite: () => request<{ title: string; subtitle: string; icon: string; demo_mode: boolean }>('/settings/public'),
|
getPublicSite: () => request<{ title: string; subtitle: string; icon: string; demo_mode: boolean }>('/settings/public'),
|
||||||
updateSite: (data: SiteSettings) =>
|
updateSite: (data: SiteSettings) =>
|
||||||
request('/settings/site', { method: 'PUT', body: JSON.stringify(data) }),
|
request('/settings/site', { method: 'PUT', body: JSON.stringify(data) }),
|
||||||
generateApiToken: () => request<{ token: string }>('/settings/site/api-token/generate', { method: 'POST' }),
|
|
||||||
generateOpenapiToken: () => request<{ token: string }>('/settings/site/openapi-token/generate', { method: 'POST' }),
|
generateOpenapiToken: () => request<{ token: string }>('/settings/site/openapi-token/generate', { method: 'POST' }),
|
||||||
getScheduler: () => request<SchedulerSettings>('/settings/scheduler'),
|
getScheduler: () => request<SchedulerSettings>('/settings/scheduler'),
|
||||||
updateScheduler: (data: SchedulerSettings) =>
|
updateScheduler: (data: SchedulerSettings) =>
|
||||||
@@ -437,8 +436,6 @@ export interface SiteSettings {
|
|||||||
openapi_enabled?: boolean
|
openapi_enabled?: boolean
|
||||||
openapi_token?: string
|
openapi_token?: string
|
||||||
openapi_token_expire?: string
|
openapi_token_expire?: string
|
||||||
api_token?: string
|
|
||||||
api_token_expire?: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SchedulerSettings {
|
export interface SchedulerSettings {
|
||||||
|
|||||||
@@ -32,13 +32,10 @@ const form = ref<SiteSettings>({
|
|||||||
cookie_days: '7',
|
cookie_days: '7',
|
||||||
openapi_enabled: false,
|
openapi_enabled: false,
|
||||||
openapi_token: '',
|
openapi_token: '',
|
||||||
openapi_token_expire: '',
|
openapi_token_expire: ''
|
||||||
api_token: '',
|
|
||||||
api_token_expire: ''
|
|
||||||
})
|
})
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const showOpenapiConfirmDialog = ref(false)
|
const showOpenapiConfirmDialog = ref(false)
|
||||||
const showApiConfirmDialog = ref(false)
|
|
||||||
|
|
||||||
const iconPreview = computed(() => {
|
const iconPreview = computed(() => {
|
||||||
if (!form.value.icon) return ''
|
if (!form.value.icon) return ''
|
||||||
@@ -92,21 +89,6 @@ async function generateOpenapiToken() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function generateToken() {
|
|
||||||
try {
|
|
||||||
const res = await api.settings.generateApiToken()
|
|
||||||
form.value.api_token = res.token
|
|
||||||
|
|
||||||
// 如果没有设置过期时间,默认给一年后
|
|
||||||
if (!form.value.api_token_expire) {
|
|
||||||
const d = new Date()
|
|
||||||
d.setFullYear(d.getFullYear() + 1)
|
|
||||||
form.value.api_token_expire = d.toISOString().split('T')[0]
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
toast.error('生成 Token 失败')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function copyOpenapiToken() {
|
async function copyOpenapiToken() {
|
||||||
if (!form.value.openapi_token) return
|
if (!form.value.openapi_token) return
|
||||||
@@ -118,15 +100,6 @@ async function copyOpenapiToken() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function copyToken() {
|
|
||||||
if (!form.value.api_token) return
|
|
||||||
try {
|
|
||||||
await navigator.clipboard.writeText(form.value.api_token)
|
|
||||||
toast.success('Token 已复制到剪贴板')
|
|
||||||
} catch {
|
|
||||||
toast.error('复制失败,请手动复制')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(loadSettings)
|
onMounted(loadSettings)
|
||||||
</script>
|
</script>
|
||||||
@@ -210,38 +183,6 @@ onMounted(loadSettings)
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pt-6 border-t mt-6">
|
|
||||||
<div class="flex items-center gap-2 mb-4">
|
|
||||||
<h3 class="text-lg font-medium text-foreground">API Token</h3>
|
|
||||||
<Badge variant="secondary"
|
|
||||||
class="font-normal text-xs bg-amber-500/10 text-amber-600 dark:text-amber-400 border-amber-500/20">
|
|
||||||
实验特性,可能变更,将会在后期下线</Badge>
|
|
||||||
</div>
|
|
||||||
<p class="text-sm text-muted-foreground mb-4">开启全局 API 直接访问能力,配置后可通过请求头 <code
|
|
||||||
class="bg-muted px-1.5 py-0.5 rounded text-xs select-all font-sans">X-API-Token: <在此生成的Token></code>
|
|
||||||
无需登录直接调用系统的所有接口,请妥善保管并设置合理的有效期。</p>
|
|
||||||
|
|
||||||
<div class="grid grid-cols-1 sm:grid-cols-4 items-center gap-2 sm:gap-4 mb-4">
|
|
||||||
<Label class="sm:text-right text-muted-foreground">Token 密钥</Label>
|
|
||||||
<div class="sm:col-span-3 flex w-full max-w-sm items-center space-x-2">
|
|
||||||
<Input v-model="form.api_token" placeholder="点击右侧按钮生成 32 位随机 Token" class="text-sm" />
|
|
||||||
<Button type="button" variant="outline" size="icon" @click="showApiConfirmDialog = true" title="随机生成">
|
|
||||||
<RefreshCw class="w-4 h-4" />
|
|
||||||
</Button>
|
|
||||||
<Button type="button" variant="outline" size="icon" @click="copyToken" title="复制" :disabled="!form.api_token">
|
|
||||||
<Copy class="w-4 h-4" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="grid grid-cols-1 sm:grid-cols-4 items-center gap-2 sm:gap-4">
|
|
||||||
<Label class="sm:text-right text-muted-foreground">截止有效期</Label>
|
|
||||||
<div class="sm:col-span-3">
|
|
||||||
<Input v-model="form.api_token_expire" type="date" class="w-full max-w-xs dark:[color-scheme:dark]" />
|
|
||||||
<div class="text-xs text-muted-foreground mt-1.5 ml-1">超过此日期后该 Token 将失效,置空代表该特性完全关闭。</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex justify-end pt-2">
|
<div class="flex justify-end pt-2">
|
||||||
<Button @click="saveSettings" :disabled="loading">
|
<Button @click="saveSettings" :disabled="loading">
|
||||||
{{ loading ? '保存中...' : '保存设置' }}
|
{{ loading ? '保存中...' : '保存设置' }}
|
||||||
@@ -267,23 +208,5 @@ onMounted(loadSettings)
|
|||||||
</AlertDialogContent>
|
</AlertDialogContent>
|
||||||
</AlertDialog>
|
</AlertDialog>
|
||||||
|
|
||||||
<!-- API Token 重新生成确认弹窗 -->
|
|
||||||
<AlertDialog :open="showApiConfirmDialog" @update:open="showApiConfirmDialog = $event">
|
|
||||||
<AlertDialogContent>
|
|
||||||
<AlertDialogHeader>
|
|
||||||
<AlertDialogTitle class="flex items-center gap-2">
|
|
||||||
<AlertTriangle class="w-5 h-5 text-amber-500" />
|
|
||||||
确认重新生成 Token?
|
|
||||||
</AlertDialogTitle>
|
|
||||||
<AlertDialogDescription>
|
|
||||||
此操作将立刻覆盖当前配置框内的旧版 API Token,原有的 Token 在点击【保存设置】后将会永久失效。建议逐步迁移到 OpenAPI Token 后直接将此特性置空关闭。确认要继续吗?
|
|
||||||
</AlertDialogDescription>
|
|
||||||
</AlertDialogHeader>
|
|
||||||
<AlertDialogFooter>
|
|
||||||
<AlertDialogCancel>取消</AlertDialogCancel>
|
|
||||||
<AlertDialogAction @click="generateToken">重新生成</AlertDialogAction>
|
|
||||||
</AlertDialogFooter>
|
|
||||||
</AlertDialogContent>
|
|
||||||
</AlertDialog>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Reference in New Issue
Block a user