feat: connect admin session and public config endpoints

This commit is contained in:
Lan
2026-06-03 02:52:38 +08:00
parent 32cbc10cd8
commit c6f33096be
7 changed files with 83 additions and 7 deletions
+1
View File
@@ -1,5 +1,6 @@
export { useAdminFiles } from './useAdminFiles' export { useAdminFiles } from './useAdminFiles'
export { useAdminLogin } from './useAdminLogin' export { useAdminLogin } from './useAdminLogin'
export { useAdminSession } from './useAdminSession'
export { useAppShell } from './useAppShell' export { useAppShell } from './useAppShell'
export { useDashboardStats } from './useDashboardStats' export { useDashboardStats } from './useDashboardStats'
export { useInjectedDarkMode } from './useInjectedDarkMode' export { useInjectedDarkMode } from './useInjectedDarkMode'
+7 -1
View File
@@ -35,7 +35,13 @@ export function useAdminLogin() {
return false return false
} }
adminStore.setToken(response.detail.token) adminStore.login({
id: response.detail.id || 'admin',
username: response.detail.username || 'admin',
token: response.detail.token,
token_type: response.detail.token_type,
expires_at: response.detail.expires_at
})
return true return true
} catch (error: unknown) { } catch (error: unknown) {
alertStore.showAlert(getErrorMessage(error, '登录失败'), 'error') alertStore.showAlert(getErrorMessage(error, '登录失败'), 'error')
+35
View File
@@ -0,0 +1,35 @@
import { AuthService } from '@/services'
import { useAdminStore } from '@/stores/adminStore'
export function useAdminSession() {
const adminStore = useAdminStore()
const verifySession = async () => {
if (!adminStore.hasToken) return false
try {
const response = await AuthService.verifyToken()
if (response.code === 200 && response.detail?.token) {
adminStore.login(response.detail)
return true
}
} catch {
adminStore.logout()
}
return false
}
const logout = async () => {
try {
await AuthService.logout()
} finally {
adminStore.logout()
}
}
return {
verifySession,
logout
}
}
+10 -5
View File
@@ -120,7 +120,7 @@ import {
import { RouterLink, useRoute, useRouter } from 'vue-router' import { RouterLink, useRoute, useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { ROUTE_NAMES, ROUTES } from '@/constants' import { ROUTE_NAMES, ROUTES } from '@/constants'
import { useAdminStore } from '@/stores/adminStore' import { useAdminSession } from '@/composables'
interface MenuItem { interface MenuItem {
id: string id: string
@@ -133,7 +133,7 @@ const router = useRouter()
const route = useRoute() const route = useRoute()
const { t } = useI18n() const { t } = useI18n()
const isDarkMode = inject('isDarkMode') const isDarkMode = inject('isDarkMode')
const adminStore = useAdminStore() const { verifySession, logout } = useAdminSession()
const menuItems: MenuItem[] = [ const menuItems: MenuItem[] = [
{ {
id: ROUTE_NAMES.DASHBOARD, id: ROUTE_NAMES.DASHBOARD,
@@ -172,6 +172,11 @@ const handleResize = () => {
onMounted(() => { onMounted(() => {
handleResize() handleResize()
window.addEventListener('resize', handleResize) window.addEventListener('resize', handleResize)
void verifySession().then((isValid) => {
if (!isValid) {
void router.push(ROUTES.LOGIN)
}
})
}) })
onUnmounted(() => { onUnmounted(() => {
@@ -179,9 +184,9 @@ onUnmounted(() => {
}) })
// 登出处理 // 登出处理
const handleLogout = () => { const handleLogout = async () => {
adminStore.logout() await logout()
router.push(ROUTES.LOGIN) await router.push(ROUTES.LOGIN)
} }
</script> </script>
+26
View File
@@ -1,14 +1,40 @@
import api from './client' import api from './client'
import type { ApiResponse, ConfigState } from '@/types' import type { ApiResponse, ConfigState } from '@/types'
type PublicConfigEnvelope = {
config?: Partial<ConfigState>
meta?: unknown
}
const isPublicConfigEnvelope = (
detail: ConfigState | PublicConfigEnvelope | null | undefined
): detail is PublicConfigEnvelope => {
return !!detail && typeof detail === 'object' && 'config' in detail
}
export class ConfigService { export class ConfigService {
static async getConfig(): Promise<ApiResponse<ConfigState>> { static async getConfig(): Promise<ApiResponse<ConfigState>> {
return api.get('/admin/config/get') return api.get('/admin/config/get')
} }
static async getUserConfig(): Promise<ApiResponse<ConfigState>> { static async getUserConfig(): Promise<ApiResponse<ConfigState>> {
try {
const response = (await api.get('/api/v1/config')) as ApiResponse<
ConfigState | PublicConfigEnvelope
>
if (isPublicConfigEnvelope(response.detail) && response.detail.config) {
return {
...response,
detail: response.detail.config as ConfigState
}
}
return response as ApiResponse<ConfigState>
} catch {
return api.post('/') return api.post('/')
} }
}
static async updateConfig(config: Partial<ConfigState>): Promise<ApiResponse> { static async updateConfig(config: Partial<ConfigState>): Promise<ApiResponse> {
return api.patch('/admin/config/update', config) return api.patch('/admin/config/update', config)
+1
View File
@@ -1,6 +1,7 @@
export interface ApiResponse<T = unknown> { export interface ApiResponse<T = unknown> {
code: number code: number
message?: string message?: string
msg?: string
detail?: T detail?: T
} }
+2
View File
@@ -2,4 +2,6 @@ export interface AdminUser {
id: string id: string
username: string username: string
token: string token: string
token_type?: string
expires_at?: number
} }