mirror of
https://github.com/fawney19/Aether.git
synced 2026-01-04 00:32:26 +08:00
Initial commit
This commit is contained in:
107
frontend/src/stores/auth.ts
Normal file
107
frontend/src/stores/auth.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref, computed } from 'vue'
|
||||
import { authApi, type User } from '@/api/auth'
|
||||
import apiClient from '@/api/client'
|
||||
import { log } from '@/utils/logger'
|
||||
|
||||
export const useAuthStore = defineStore('auth', () => {
|
||||
// 初始化时从 localStorage 恢复 token
|
||||
const storedToken = apiClient.getToken()
|
||||
|
||||
const user = ref<User | null>(null)
|
||||
const token = ref<string | null>(storedToken)
|
||||
const loading = ref(false)
|
||||
const error = ref<string | null>(null)
|
||||
|
||||
const isAuthenticated = computed(() => {
|
||||
// 使用 store 中的 token 状态判断认证状态
|
||||
// 如果需要同步 localStorage,应该在 checkAuth 或专门的 syncToken 方法中处理
|
||||
return !!token.value
|
||||
})
|
||||
|
||||
/**
|
||||
* 同步 localStorage 中的 token 到 store
|
||||
* 用于处理多标签页或外部 token 变更的情况
|
||||
*/
|
||||
function syncToken() {
|
||||
const currentToken = apiClient.getToken()
|
||||
if (token.value !== currentToken) {
|
||||
token.value = currentToken
|
||||
}
|
||||
}
|
||||
const isAdmin = computed(() => user.value?.role === 'admin')
|
||||
|
||||
async function login(email: string, password: string) {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
const response = await authApi.login({ email, password })
|
||||
token.value = response.access_token
|
||||
|
||||
// 获取用户信息
|
||||
const userInfo = await authApi.getCurrentUser()
|
||||
user.value = userInfo
|
||||
|
||||
return true
|
||||
} catch (err: any) {
|
||||
// 不要暴露后端的详细错误信息
|
||||
if (err.response?.status === 401) {
|
||||
error.value = '邮箱或密码错误'
|
||||
} else if (err.response?.status === 422) {
|
||||
error.value = '请输入有效的邮箱地址'
|
||||
} else if (err.response?.status === 500) {
|
||||
error.value = '服务器错误,请稍后重试'
|
||||
} else {
|
||||
error.value = '登录失败,请检查网络连接'
|
||||
}
|
||||
return false
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function logout() {
|
||||
user.value = null
|
||||
token.value = null
|
||||
authApi.logout()
|
||||
}
|
||||
|
||||
async function fetchCurrentUser() {
|
||||
try {
|
||||
const userInfo = await authApi.getCurrentUser()
|
||||
user.value = userInfo
|
||||
return userInfo
|
||||
} catch (err: any) {
|
||||
log.error('Failed to fetch user info', err)
|
||||
// 根据用户要求,不管什么错误都不清除状态
|
||||
// 保持登录状态,除非用户手动退出
|
||||
log.info('Keeping session despite error, as per user requirement')
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
async function checkAuth() {
|
||||
const storedToken = apiClient.getToken()
|
||||
if (storedToken) {
|
||||
token.value = storedToken
|
||||
// 即使获取用户信息失败,也保留 token
|
||||
// 只有 401 错误才表示 token 真正失效
|
||||
await fetchCurrentUser()
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
user,
|
||||
token,
|
||||
loading,
|
||||
error,
|
||||
isAuthenticated,
|
||||
isAdmin,
|
||||
login,
|
||||
logout,
|
||||
fetchCurrentUser,
|
||||
checkAuth,
|
||||
syncToken
|
||||
}
|
||||
})
|
||||
133
frontend/src/stores/users.ts
Normal file
133
frontend/src/stores/users.ts
Normal file
@@ -0,0 +1,133 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
import { usersApi, type User, type CreateUserRequest, type UpdateUserRequest, type ApiKey } from '@/api/users'
|
||||
|
||||
export const useUsersStore = defineStore('users', () => {
|
||||
const users = ref<User[]>([])
|
||||
const loading = ref(false)
|
||||
const error = ref<string | null>(null)
|
||||
|
||||
async function fetchUsers() {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
users.value = await usersApi.getAllUsers()
|
||||
} catch (err: any) {
|
||||
error.value = err.response?.data?.detail || '获取用户列表失败'
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function createUser(userData: CreateUserRequest) {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
const newUser = await usersApi.createUser(userData)
|
||||
users.value.push(newUser)
|
||||
return newUser
|
||||
} catch (err: any) {
|
||||
error.value = err.response?.data?.detail || '创建用户失败'
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function updateUser(userId: string, updates: UpdateUserRequest) {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
const updatedUser = await usersApi.updateUser(userId, updates)
|
||||
const index = users.value.findIndex(u => u.id === userId)
|
||||
if (index !== -1) {
|
||||
// 保留原有的创建时间等字段,只更新返回的字段
|
||||
users.value[index] = {
|
||||
...users.value[index],
|
||||
...updatedUser
|
||||
}
|
||||
}
|
||||
return updatedUser
|
||||
} catch (err: any) {
|
||||
error.value = err.response?.data?.detail || '更新用户失败'
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteUser(userId: string) {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
await usersApi.deleteUser(userId)
|
||||
users.value = users.value.filter(u => u.id !== userId)
|
||||
} catch (err: any) {
|
||||
error.value = err.response?.data?.detail || '删除用户失败'
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function getUserApiKeys(userId: string): Promise<ApiKey[]> {
|
||||
try {
|
||||
return await usersApi.getUserApiKeys(userId)
|
||||
} catch (err: any) {
|
||||
error.value = err.response?.data?.detail || '获取 API Keys 失败'
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
async function createApiKey(userId: string, name?: string): Promise<ApiKey> {
|
||||
try {
|
||||
return await usersApi.createApiKey(userId, name)
|
||||
} catch (err: any) {
|
||||
error.value = err.response?.data?.detail || '创建 API Key 失败'
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteApiKey(userId: string, keyId: string) {
|
||||
try {
|
||||
await usersApi.deleteApiKey(userId, keyId)
|
||||
} catch (err: any) {
|
||||
error.value = err.response?.data?.detail || '删除 API Key 失败'
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
async function resetUserQuota(userId: string) {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
await usersApi.resetUserQuota(userId)
|
||||
// 刷新用户列表以获取最新数据
|
||||
await fetchUsers()
|
||||
} catch (err: any) {
|
||||
error.value = err.response?.data?.detail || '重置配额失败'
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
users,
|
||||
loading,
|
||||
error,
|
||||
fetchUsers,
|
||||
createUser,
|
||||
updateUser,
|
||||
deleteUser,
|
||||
getUserApiKeys,
|
||||
createApiKey,
|
||||
deleteApiKey,
|
||||
resetUserQuota
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user