mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-23 18:35:09 +00:00
feat: 实现完整用户管理系统和LDAP认证集成
- 新增LDAP认证服务支持用户登录验证 - 实现用户服务包含会话管理和权限控制 - 添加用户专用路由和API端点 - 扩展认证中间件支持用户和管理员双重身份 - 新增用户仪表板、API密钥管理和使用统计界面 - 完善前端用户管理组件和路由配置 - 支持用户自助API密钥创建和管理 - 添加管理员用户管理功能包含角色权限控制 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
185
web/admin-spa/src/stores/user.js
Normal file
185
web/admin-spa/src/stores/user.js
Normal file
@@ -0,0 +1,185 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import axios from 'axios'
|
||||
|
||||
const API_BASE = '/users'
|
||||
|
||||
export const useUserStore = defineStore('user', {
|
||||
state: () => ({
|
||||
user: null,
|
||||
isAuthenticated: false,
|
||||
sessionToken: null,
|
||||
loading: false
|
||||
}),
|
||||
|
||||
getters: {
|
||||
isLoggedIn: (state) => state.isAuthenticated && state.user,
|
||||
userName: (state) => state.user?.displayName || state.user?.username,
|
||||
userRole: (state) => state.user?.role
|
||||
},
|
||||
|
||||
actions: {
|
||||
// 🔐 用户登录
|
||||
async login(credentials) {
|
||||
this.loading = true
|
||||
try {
|
||||
const response = await axios.post(`${API_BASE}/login`, credentials)
|
||||
|
||||
if (response.data.success) {
|
||||
this.user = response.data.user
|
||||
this.sessionToken = response.data.sessionToken
|
||||
this.isAuthenticated = true
|
||||
|
||||
// 保存到 localStorage
|
||||
localStorage.setItem('userToken', this.sessionToken)
|
||||
localStorage.setItem('userData', JSON.stringify(this.user))
|
||||
|
||||
// 设置 axios 默认头部
|
||||
this.setAuthHeader()
|
||||
|
||||
return response.data
|
||||
} else {
|
||||
throw new Error(response.data.message || 'Login failed')
|
||||
}
|
||||
} catch (error) {
|
||||
this.clearAuth()
|
||||
throw error
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
// 🚪 用户登出
|
||||
async logout() {
|
||||
try {
|
||||
if (this.sessionToken) {
|
||||
await axios.post(`${API_BASE}/logout`, {}, {
|
||||
headers: { 'x-user-token': this.sessionToken }
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Logout request failed:', error)
|
||||
} finally {
|
||||
this.clearAuth()
|
||||
}
|
||||
},
|
||||
|
||||
// 🔄 检查认证状态
|
||||
async checkAuth() {
|
||||
const token = localStorage.getItem('userToken')
|
||||
const userData = localStorage.getItem('userData')
|
||||
|
||||
if (!token || !userData) {
|
||||
this.clearAuth()
|
||||
return false
|
||||
}
|
||||
|
||||
try {
|
||||
this.sessionToken = token
|
||||
this.user = JSON.parse(userData)
|
||||
this.isAuthenticated = true
|
||||
this.setAuthHeader()
|
||||
|
||||
// 验证 token 是否仍然有效
|
||||
await this.getUserProfile()
|
||||
return true
|
||||
} catch (error) {
|
||||
console.error('Auth check failed:', error)
|
||||
this.clearAuth()
|
||||
return false
|
||||
}
|
||||
},
|
||||
|
||||
// 👤 获取用户资料
|
||||
async getUserProfile() {
|
||||
try {
|
||||
const response = await axios.get(`${API_BASE}/profile`)
|
||||
|
||||
if (response.data.success) {
|
||||
this.user = response.data.user
|
||||
localStorage.setItem('userData', JSON.stringify(this.user))
|
||||
return response.data.user
|
||||
}
|
||||
} catch (error) {
|
||||
if (error.response?.status === 401) {
|
||||
this.clearAuth()
|
||||
}
|
||||
throw error
|
||||
}
|
||||
},
|
||||
|
||||
// 🔑 获取用户API Keys
|
||||
async getUserApiKeys() {
|
||||
try {
|
||||
const response = await axios.get(`${API_BASE}/api-keys`)
|
||||
return response.data.success ? response.data.apiKeys : []
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch API keys:', error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
|
||||
// 🔑 创建API Key
|
||||
async createApiKey(keyData) {
|
||||
try {
|
||||
const response = await axios.post(`${API_BASE}/api-keys`, keyData)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
console.error('Failed to create API key:', error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
|
||||
// 🔄 重新生成API Key
|
||||
async regenerateApiKey(keyId) {
|
||||
try {
|
||||
const response = await axios.post(`${API_BASE}/api-keys/${keyId}/regenerate`)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
console.error('Failed to regenerate API key:', error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
|
||||
// 🗑️ 删除API Key
|
||||
async deleteApiKey(keyId) {
|
||||
try {
|
||||
const response = await axios.delete(`${API_BASE}/api-keys/${keyId}`)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
console.error('Failed to delete API key:', error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
|
||||
// 📊 获取使用统计
|
||||
async getUserUsageStats(params = {}) {
|
||||
try {
|
||||
const response = await axios.get(`${API_BASE}/usage-stats`, { params })
|
||||
return response.data.success ? response.data.stats : null
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch usage stats:', error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
|
||||
// 🧹 清除认证信息
|
||||
clearAuth() {
|
||||
this.user = null
|
||||
this.sessionToken = null
|
||||
this.isAuthenticated = false
|
||||
|
||||
localStorage.removeItem('userToken')
|
||||
localStorage.removeItem('userData')
|
||||
|
||||
// 清除 axios 默认头部
|
||||
delete axios.defaults.headers.common['x-user-token']
|
||||
},
|
||||
|
||||
// 🔧 设置认证头部
|
||||
setAuthHeader() {
|
||||
if (this.sessionToken) {
|
||||
axios.defaults.headers.common['x-user-token'] = this.sessionToken
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user