mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-23 00:53:33 +00:00
fix: API key limit now only counts active keys and uses config value
- Modified API key limit to count only active (non-deleted) keys instead of all keys - Fixed frontend to use MAX_API_KEYS_PER_USER environment variable instead of hardcoded value - Added activeApiKeysCount computed property to filter deleted keys - Updated user profile endpoint to include maxApiKeysPerUser config - Enhanced user store to persist and retrieve config values 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -116,6 +116,9 @@ router.get('/profile', authenticateUser, async (req, res) => {
|
|||||||
lastLoginAt: user.lastLoginAt,
|
lastLoginAt: user.lastLoginAt,
|
||||||
apiKeyCount: user.apiKeyCount,
|
apiKeyCount: user.apiKeyCount,
|
||||||
totalUsage: user.totalUsage
|
totalUsage: user.totalUsage
|
||||||
|
},
|
||||||
|
config: {
|
||||||
|
maxApiKeysPerUser: config.userManagement.maxApiKeysPerUser
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
<div class="mt-4 sm:ml-16 sm:mt-0 sm:flex-none">
|
<div class="mt-4 sm:ml-16 sm:mt-0 sm:flex-none">
|
||||||
<button
|
<button
|
||||||
class="inline-flex items-center justify-center rounded-md border border-transparent bg-blue-600 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 sm:w-auto"
|
class="inline-flex items-center justify-center rounded-md border border-transparent bg-blue-600 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 sm:w-auto"
|
||||||
:disabled="apiKeys.length >= maxApiKeys"
|
:disabled="activeApiKeysCount >= maxApiKeys"
|
||||||
@click="showCreateModal = true"
|
@click="showCreateModal = true"
|
||||||
>
|
>
|
||||||
<svg class="-ml-1 mr-2 h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg class="-ml-1 mr-2 h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
|
|
||||||
<!-- API Keys 数量限制提示 -->
|
<!-- API Keys 数量限制提示 -->
|
||||||
<div
|
<div
|
||||||
v-if="apiKeys.length >= maxApiKeys"
|
v-if="activeApiKeysCount >= maxApiKeys"
|
||||||
class="rounded-md border border-yellow-200 bg-yellow-50 p-4"
|
class="rounded-md border border-yellow-200 bg-yellow-50 p-4"
|
||||||
>
|
>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
@@ -254,7 +254,7 @@ const userStore = useUserStore()
|
|||||||
|
|
||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
const apiKeys = ref([])
|
const apiKeys = ref([])
|
||||||
const maxApiKeys = ref(5) // 从配置获取
|
const maxApiKeys = computed(() => userStore.config?.maxApiKeysPerUser || 5)
|
||||||
|
|
||||||
const showCreateModal = ref(false)
|
const showCreateModal = ref(false)
|
||||||
const showViewModal = ref(false)
|
const showViewModal = ref(false)
|
||||||
@@ -270,6 +270,11 @@ const sortedApiKeys = computed(() => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Computed property to count only active (non-deleted) API keys
|
||||||
|
const activeApiKeysCount = computed(() => {
|
||||||
|
return apiKeys.value.filter((key) => !(key.isDeleted === 'true' || key.deletedAt)).length
|
||||||
|
})
|
||||||
|
|
||||||
const formatNumber = (num) => {
|
const formatNumber = (num) => {
|
||||||
if (num >= 1000000) {
|
if (num >= 1000000) {
|
||||||
return (num / 1000000).toFixed(1) + 'M'
|
return (num / 1000000).toFixed(1) + 'M'
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ export const useUserStore = defineStore('user', {
|
|||||||
user: null,
|
user: null,
|
||||||
isAuthenticated: false,
|
isAuthenticated: false,
|
||||||
sessionToken: null,
|
sessionToken: null,
|
||||||
loading: false
|
loading: false,
|
||||||
|
config: null
|
||||||
}),
|
}),
|
||||||
|
|
||||||
getters: {
|
getters: {
|
||||||
@@ -72,6 +73,7 @@ export const useUserStore = defineStore('user', {
|
|||||||
async checkAuth() {
|
async checkAuth() {
|
||||||
const token = localStorage.getItem('userToken')
|
const token = localStorage.getItem('userToken')
|
||||||
const userData = localStorage.getItem('userData')
|
const userData = localStorage.getItem('userData')
|
||||||
|
const userConfig = localStorage.getItem('userConfig')
|
||||||
|
|
||||||
if (!token || !userData) {
|
if (!token || !userData) {
|
||||||
this.clearAuth()
|
this.clearAuth()
|
||||||
@@ -81,6 +83,7 @@ export const useUserStore = defineStore('user', {
|
|||||||
try {
|
try {
|
||||||
this.sessionToken = token
|
this.sessionToken = token
|
||||||
this.user = JSON.parse(userData)
|
this.user = JSON.parse(userData)
|
||||||
|
this.config = userConfig ? JSON.parse(userConfig) : null
|
||||||
this.isAuthenticated = true
|
this.isAuthenticated = true
|
||||||
this.setAuthHeader()
|
this.setAuthHeader()
|
||||||
|
|
||||||
@@ -101,7 +104,9 @@ export const useUserStore = defineStore('user', {
|
|||||||
|
|
||||||
if (response.data.success) {
|
if (response.data.success) {
|
||||||
this.user = response.data.user
|
this.user = response.data.user
|
||||||
|
this.config = response.data.config
|
||||||
localStorage.setItem('userData', JSON.stringify(this.user))
|
localStorage.setItem('userData', JSON.stringify(this.user))
|
||||||
|
localStorage.setItem('userConfig', JSON.stringify(this.config))
|
||||||
return response.data.user
|
return response.data.user
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -170,9 +175,11 @@ export const useUserStore = defineStore('user', {
|
|||||||
this.user = null
|
this.user = null
|
||||||
this.sessionToken = null
|
this.sessionToken = null
|
||||||
this.isAuthenticated = false
|
this.isAuthenticated = false
|
||||||
|
this.config = null
|
||||||
|
|
||||||
localStorage.removeItem('userToken')
|
localStorage.removeItem('userToken')
|
||||||
localStorage.removeItem('userData')
|
localStorage.removeItem('userData')
|
||||||
|
localStorage.removeItem('userConfig')
|
||||||
|
|
||||||
// 清除 axios 默认头部
|
// 清除 axios 默认头部
|
||||||
delete axios.defaults.headers.common['x-user-token']
|
delete axios.defaults.headers.common['x-user-token']
|
||||||
|
|||||||
Reference in New Issue
Block a user