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
View File
@@ -125,6 +125,7 @@ export const api = {
getPublicSite: () => request<{ title: string; subtitle: string; icon: string; demo_mode: boolean }>('/settings/public'),
updateSite: (data: SiteSettings) =>
request('/settings/site', { method: 'PUT', body: JSON.stringify(data) }),
generateApiToken: () => request<{ token: string }>('/settings/site/api-token/generate', { method: 'POST' }),
getScheduler: () => request<SchedulerSettings>('/settings/scheduler'),
updateScheduler: (data: SchedulerSettings) =>
request('/settings/scheduler', { method: 'PUT', body: JSON.stringify(data) }),
@@ -401,6 +402,8 @@ export interface SiteSettings {
icon: string
page_size: string
cookie_days: string
api_token?: string
api_token_expire?: string
}
export interface SchedulerSettings {
+60 -1
View File
@@ -6,6 +6,8 @@ import { Button } from '@/components/ui/button'
import { api, type SiteSettings } from '@/api'
import { toast } from 'vue-sonner'
import { useSiteSettings } from '@/composables/useSiteSettings'
import { Badge } from '@/components/ui/badge'
import { RefreshCw, Copy } from 'lucide-vue-next'
const { refreshSettings } = useSiteSettings()
@@ -14,7 +16,9 @@ const form = ref<SiteSettings>({
subtitle: '',
icon: '',
page_size: '10',
cookie_days: '7'
cookie_days: '7',
api_token: '',
api_token_expire: ''
})
const loading = ref(false)
@@ -51,6 +55,32 @@ async function saveSettings() {
}
}
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 copyToken() {
if (!form.value.api_token) return
try {
await navigator.clipboard.writeText(form.value.api_token)
toast.success('Token 已复制到剪贴板')
} catch {
toast.error('复制失败,请手动复制')
}
}
onMounted(loadSettings)
</script>
@@ -84,6 +114,35 @@ onMounted(loadSettings)
</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: &lt;在此生成的Token&gt;</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="generateToken" 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">
<Button @click="saveSettings" :disabled="loading">
{{ loading ? '保存中...' : '保存设置' }}