Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
陈曦
sub2api
Commits
e7bc6250
Unverified
Commit
e7bc6250
authored
Jan 19, 2026
by
Wesley Liddick
Committed by
GitHub
Jan 19, 2026
Browse files
Merge pull request #333 from whoismonay/main
fix: 普通用户接口移除管理员敏感字段透传
parents
eb5e6214
c8fb9ef3
Changes
26
Show whitespace changes
Inline
Side-by-side
frontend/src/components/common/GroupSelector.vue
View file @
e7bc6250
...
@@ -42,13 +42,13 @@
...
@@ -42,13 +42,13 @@
import
{
computed
}
from
'
vue
'
import
{
computed
}
from
'
vue
'
import
{
useI18n
}
from
'
vue-i18n
'
import
{
useI18n
}
from
'
vue-i18n
'
import
GroupBadge
from
'
./GroupBadge.vue
'
import
GroupBadge
from
'
./GroupBadge.vue
'
import
type
{
Group
,
GroupPlatform
}
from
'
@/types
'
import
type
{
Admin
Group
,
GroupPlatform
}
from
'
@/types
'
const
{
t
}
=
useI18n
()
const
{
t
}
=
useI18n
()
interface
Props
{
interface
Props
{
modelValue
:
number
[]
modelValue
:
number
[]
groups
:
Group
[]
groups
:
Admin
Group
[]
platform
?:
GroupPlatform
// Optional platform filter
platform
?:
GroupPlatform
// Optional platform filter
mixedScheduling
?:
boolean
// For antigravity accounts: allow anthropic/gemini groups
mixedScheduling
?:
boolean
// For antigravity accounts: allow anthropic/gemini groups
}
}
...
...
frontend/src/types/index.ts
View file @
e7bc6250
...
@@ -27,7 +27,6 @@ export interface FetchOptions {
...
@@ -27,7 +27,6 @@ export interface FetchOptions {
export
interface
User
{
export
interface
User
{
id
:
number
id
:
number
username
:
string
username
:
string
notes
:
string
email
:
string
email
:
string
role
:
'
admin
'
|
'
user
'
// User role for authorization
role
:
'
admin
'
|
'
user
'
// User role for authorization
balance
:
number
// User balance for API usage
balance
:
number
// User balance for API usage
...
@@ -39,6 +38,11 @@ export interface User {
...
@@ -39,6 +38,11 @@ export interface User {
updated_at
:
string
updated_at
:
string
}
}
export
interface
AdminUser
extends
User
{
// 管理员备注(普通用户接口不返回)
notes
:
string
}
export
interface
LoginRequest
{
export
interface
LoginRequest
{
email
:
string
email
:
string
password
:
string
password
:
string
...
@@ -270,12 +274,17 @@ export interface Group {
...
@@ -270,12 +274,17 @@ export interface Group {
// Claude Code 客户端限制
// Claude Code 客户端限制
claude_code_only
:
boolean
claude_code_only
:
boolean
fallback_group_id
:
number
|
null
fallback_group_id
:
number
|
null
// 模型路由配置(仅 anthropic 平台使用)
created_at
:
string
updated_at
:
string
}
export
interface
AdminGroup
extends
Group
{
// 模型路由配置(仅管理员可见,内部信息)
model_routing
:
Record
<
string
,
number
[]
>
|
null
model_routing
:
Record
<
string
,
number
[]
>
|
null
model_routing_enabled
:
boolean
model_routing_enabled
:
boolean
// 分组下账号数量(仅管理员可见)
account_count
?:
number
account_count
?:
number
created_at
:
string
updated_at
:
string
}
}
export
interface
ApiKey
{
export
interface
ApiKey
{
...
@@ -637,7 +646,6 @@ export interface UsageLog {
...
@@ -637,7 +646,6 @@ export interface UsageLog {
total_cost
:
number
total_cost
:
number
actual_cost
:
number
actual_cost
:
number
rate_multiplier
:
number
rate_multiplier
:
number
account_rate_multiplier
?:
number
|
null
billing_type
:
number
billing_type
:
number
stream
:
boolean
stream
:
boolean
...
@@ -651,18 +659,30 @@ export interface UsageLog {
...
@@ -651,18 +659,30 @@ export interface UsageLog {
// User-Agent
// User-Agent
user_agent
:
string
|
null
user_agent
:
string
|
null
// IP 地址(仅管理员可见)
ip_address
:
string
|
null
created_at
:
string
created_at
:
string
user
?:
User
user
?:
User
api_key
?:
ApiKey
api_key
?:
ApiKey
account
?:
Account
group
?:
Group
group
?:
Group
subscription
?:
UserSubscription
subscription
?:
UserSubscription
}
}
export
interface
UsageLogAccountSummary
{
id
:
number
name
:
string
}
export
interface
AdminUsageLog
extends
UsageLog
{
// 账号计费倍率(仅管理员可见)
account_rate_multiplier
?:
number
|
null
// 用户请求 IP(仅管理员可见)
ip_address
?:
string
|
null
// 最小账号信息(仅管理员接口返回)
account
?:
UsageLogAccountSummary
}
export
interface
UsageCleanupFilters
{
export
interface
UsageCleanupFilters
{
start_time
:
string
start_time
:
string
end_time
:
string
end_time
:
string
...
...
frontend/src/views/admin/AccountsView.vue
View file @
e7bc6250
...
@@ -187,14 +187,14 @@ import AccountCapacityCell from '@/components/account/AccountCapacityCell.vue'
...
@@ -187,14 +187,14 @@ import AccountCapacityCell from '@/components/account/AccountCapacityCell.vue'
import
PlatformTypeBadge
from
'
@/components/common/PlatformTypeBadge.vue
'
import
PlatformTypeBadge
from
'
@/components/common/PlatformTypeBadge.vue
'
import
Icon
from
'
@/components/icons/Icon.vue
'
import
Icon
from
'
@/components/icons/Icon.vue
'
import
{
formatDateTime
,
formatRelativeTime
}
from
'
@/utils/format
'
import
{
formatDateTime
,
formatRelativeTime
}
from
'
@/utils/format
'
import
type
{
Account
,
Proxy
,
Group
}
from
'
@/types
'
import
type
{
Account
,
Proxy
,
Admin
Group
}
from
'
@/types
'
const
{
t
}
=
useI18n
()
const
{
t
}
=
useI18n
()
const
appStore
=
useAppStore
()
const
appStore
=
useAppStore
()
const
authStore
=
useAuthStore
()
const
authStore
=
useAuthStore
()
const
proxies
=
ref
<
Proxy
[]
>
([])
const
proxies
=
ref
<
Proxy
[]
>
([])
const
groups
=
ref
<
Group
[]
>
([])
const
groups
=
ref
<
Admin
Group
[]
>
([])
const
selIds
=
ref
<
number
[]
>
([])
const
selIds
=
ref
<
number
[]
>
([])
const
showCreate
=
ref
(
false
)
const
showCreate
=
ref
(
false
)
const
showEdit
=
ref
(
false
)
const
showEdit
=
ref
(
false
)
...
...
frontend/src/views/admin/GroupsView.vue
View file @
e7bc6250
...
@@ -1107,7 +1107,7 @@ import { useI18n } from 'vue-i18n'
...
@@ -1107,7 +1107,7 @@ import { useI18n } from 'vue-i18n'
import
{
useAppStore
}
from
'
@/stores/app
'
import
{
useAppStore
}
from
'
@/stores/app
'
import
{
useOnboardingStore
}
from
'
@/stores/onboarding
'
import
{
useOnboardingStore
}
from
'
@/stores/onboarding
'
import
{
adminAPI
}
from
'
@/api/admin
'
import
{
adminAPI
}
from
'
@/api/admin
'
import
type
{
Group
,
GroupPlatform
,
SubscriptionType
}
from
'
@/types
'
import
type
{
Admin
Group
,
GroupPlatform
,
SubscriptionType
}
from
'
@/types
'
import
type
{
Column
}
from
'
@/components/common/types
'
import
type
{
Column
}
from
'
@/components/common/types
'
import
AppLayout
from
'
@/components/layout/AppLayout.vue
'
import
AppLayout
from
'
@/components/layout/AppLayout.vue
'
import
TablePageLayout
from
'
@/components/layout/TablePageLayout.vue
'
import
TablePageLayout
from
'
@/components/layout/TablePageLayout.vue
'
...
@@ -1202,7 +1202,7 @@ const fallbackGroupOptionsForEdit = computed(() => {
...
@@ -1202,7 +1202,7 @@ const fallbackGroupOptionsForEdit = computed(() => {
return
options
return
options
}
)
}
)
const
groups
=
ref
<
Group
[]
>
([])
const
groups
=
ref
<
Admin
Group
[]
>
([])
const
loading
=
ref
(
false
)
const
loading
=
ref
(
false
)
const
searchQuery
=
ref
(
''
)
const
searchQuery
=
ref
(
''
)
const
filters
=
reactive
({
const
filters
=
reactive
({
...
@@ -1223,8 +1223,8 @@ const showCreateModal = ref(false)
...
@@ -1223,8 +1223,8 @@ const showCreateModal = ref(false)
const
showEditModal
=
ref
(
false
)
const
showEditModal
=
ref
(
false
)
const
showDeleteDialog
=
ref
(
false
)
const
showDeleteDialog
=
ref
(
false
)
const
submitting
=
ref
(
false
)
const
submitting
=
ref
(
false
)
const
editingGroup
=
ref
<
Group
|
null
>
(
null
)
const
editingGroup
=
ref
<
Admin
Group
|
null
>
(
null
)
const
deletingGroup
=
ref
<
Group
|
null
>
(
null
)
const
deletingGroup
=
ref
<
Admin
Group
|
null
>
(
null
)
const
createForm
=
reactive
({
const
createForm
=
reactive
({
name
:
''
,
name
:
''
,
...
@@ -1529,7 +1529,7 @@ const handleCreateGroup = async () => {
...
@@ -1529,7 +1529,7 @@ const handleCreateGroup = async () => {
}
}
}
}
const
handleEdit
=
async
(
group
:
Group
)
=>
{
const
handleEdit
=
async
(
group
:
Admin
Group
)
=>
{
editingGroup
.
value
=
group
editingGroup
.
value
=
group
editForm
.
name
=
group
.
name
editForm
.
name
=
group
.
name
editForm
.
description
=
group
.
description
||
''
editForm
.
description
=
group
.
description
||
''
...
@@ -1585,7 +1585,7 @@ const handleUpdateGroup = async () => {
...
@@ -1585,7 +1585,7 @@ const handleUpdateGroup = async () => {
}
}
}
}
const
handleDelete
=
(
group
:
Group
)
=>
{
const
handleDelete
=
(
group
:
Admin
Group
)
=>
{
deletingGroup
.
value
=
group
deletingGroup
.
value
=
group
showDeleteDialog
.
value
=
true
showDeleteDialog
.
value
=
true
}
}
...
...
frontend/src/views/admin/UsageView.vue
View file @
e7bc6250
...
@@ -42,11 +42,11 @@ import UsageStatsCards from '@/components/admin/usage/UsageStatsCards.vue'; impo
...
@@ -42,11 +42,11 @@ import UsageStatsCards from '@/components/admin/usage/UsageStatsCards.vue'; impo
import
UsageTable
from
'
@/components/admin/usage/UsageTable.vue
'
;
import
UsageExportProgress
from
'
@/components/admin/usage/UsageExportProgress.vue
'
import
UsageTable
from
'
@/components/admin/usage/UsageTable.vue
'
;
import
UsageExportProgress
from
'
@/components/admin/usage/UsageExportProgress.vue
'
import
UsageCleanupDialog
from
'
@/components/admin/usage/UsageCleanupDialog.vue
'
import
UsageCleanupDialog
from
'
@/components/admin/usage/UsageCleanupDialog.vue
'
import
ModelDistributionChart
from
'
@/components/charts/ModelDistributionChart.vue
'
;
import
TokenUsageTrend
from
'
@/components/charts/TokenUsageTrend.vue
'
import
ModelDistributionChart
from
'
@/components/charts/ModelDistributionChart.vue
'
;
import
TokenUsageTrend
from
'
@/components/charts/TokenUsageTrend.vue
'
import
type
{
UsageLog
,
TrendDataPoint
,
ModelStat
}
from
'
@/types
'
;
import
type
{
AdminUsageStatsResponse
,
AdminUsageQueryParams
}
from
'
@/api/admin/usage
'
import
type
{
Admin
UsageLog
,
TrendDataPoint
,
ModelStat
}
from
'
@/types
'
;
import
type
{
AdminUsageStatsResponse
,
AdminUsageQueryParams
}
from
'
@/api/admin/usage
'
const
{
t
}
=
useI18n
()
const
{
t
}
=
useI18n
()
const
appStore
=
useAppStore
()
const
appStore
=
useAppStore
()
const
usageStats
=
ref
<
AdminUsageStatsResponse
|
null
>
(
null
);
const
usageLogs
=
ref
<
UsageLog
[]
>
([]);
const
loading
=
ref
(
false
);
const
exporting
=
ref
(
false
)
const
usageStats
=
ref
<
AdminUsageStatsResponse
|
null
>
(
null
);
const
usageLogs
=
ref
<
Admin
UsageLog
[]
>
([]);
const
loading
=
ref
(
false
);
const
exporting
=
ref
(
false
)
const
trendData
=
ref
<
TrendDataPoint
[]
>
([]);
const
modelStats
=
ref
<
ModelStat
[]
>
([]);
const
chartsLoading
=
ref
(
false
);
const
granularity
=
ref
<
'
day
'
|
'
hour
'
>
(
'
day
'
)
const
trendData
=
ref
<
TrendDataPoint
[]
>
([]);
const
modelStats
=
ref
<
ModelStat
[]
>
([]);
const
chartsLoading
=
ref
(
false
);
const
granularity
=
ref
<
'
day
'
|
'
hour
'
>
(
'
day
'
)
let
abortController
:
AbortController
|
null
=
null
;
let
exportAbortController
:
AbortController
|
null
=
null
let
abortController
:
AbortController
|
null
=
null
;
let
exportAbortController
:
AbortController
|
null
=
null
const
exportProgress
=
reactive
({
show
:
false
,
progress
:
0
,
current
:
0
,
total
:
0
,
estimatedTime
:
''
})
const
exportProgress
=
reactive
({
show
:
false
,
progress
:
0
,
current
:
0
,
total
:
0
,
estimatedTime
:
''
})
...
@@ -92,7 +92,7 @@ const exportToExcel = async () => {
...
@@ -92,7 +92,7 @@ const exportToExcel = async () => {
if
(
exporting
.
value
)
return
;
exporting
.
value
=
true
;
exportProgress
.
show
=
true
if
(
exporting
.
value
)
return
;
exporting
.
value
=
true
;
exportProgress
.
show
=
true
const
c
=
new
AbortController
();
exportAbortController
=
c
const
c
=
new
AbortController
();
exportAbortController
=
c
try
{
try
{
const
all
:
UsageLog
[]
=
[];
let
p
=
1
;
let
total
=
pagination
.
total
const
all
:
Admin
UsageLog
[]
=
[];
let
p
=
1
;
let
total
=
pagination
.
total
while
(
true
)
{
while
(
true
)
{
const
res
=
await
adminUsageAPI
.
list
({
page
:
p
,
page_size
:
100
,
...
filters
.
value
},
{
signal
:
c
.
signal
})
const
res
=
await
adminUsageAPI
.
list
({
page
:
p
,
page_size
:
100
,
...
filters
.
value
},
{
signal
:
c
.
signal
})
if
(
c
.
signal
.
aborted
)
break
;
if
(
p
===
1
)
{
total
=
res
.
total
;
exportProgress
.
total
=
total
}
if
(
c
.
signal
.
aborted
)
break
;
if
(
p
===
1
)
{
total
=
res
.
total
;
exportProgress
.
total
=
total
}
...
...
frontend/src/views/admin/UsersView.vue
View file @
e7bc6250
...
@@ -492,7 +492,7 @@ import Icon from '@/components/icons/Icon.vue'
...
@@ -492,7 +492,7 @@ import Icon from '@/components/icons/Icon.vue'
const
{
t
}
=
useI18n
()
const
{
t
}
=
useI18n
()
import
{
adminAPI
}
from
'
@/api/admin
'
import
{
adminAPI
}
from
'
@/api/admin
'
import
type
{
User
,
UserAttributeDefinition
}
from
'
@/types
'
import
type
{
Admin
User
,
UserAttributeDefinition
}
from
'
@/types
'
import
type
{
BatchUserUsageStats
}
from
'
@/api/admin/dashboard
'
import
type
{
BatchUserUsageStats
}
from
'
@/api/admin/dashboard
'
import
type
{
Column
}
from
'
@/components/common/types
'
import
type
{
Column
}
from
'
@/components/common/types
'
import
AppLayout
from
'
@/components/layout/AppLayout.vue
'
import
AppLayout
from
'
@/components/layout/AppLayout.vue
'
...
@@ -637,7 +637,7 @@ const columns = computed<Column[]>(() =>
...
@@ -637,7 +637,7 @@ const columns = computed<Column[]>(() =>
)
)
)
)
const
users
=
ref
<
User
[]
>
([])
const
users
=
ref
<
Admin
User
[]
>
([])
const
loading
=
ref
(
false
)
const
loading
=
ref
(
false
)
const
searchQuery
=
ref
(
''
)
const
searchQuery
=
ref
(
''
)
...
@@ -736,16 +736,16 @@ const showEditModal = ref(false)
...
@@ -736,16 +736,16 @@ const showEditModal = ref(false)
const
showDeleteDialog
=
ref
(
false
)
const
showDeleteDialog
=
ref
(
false
)
const
showApiKeysModal
=
ref
(
false
)
const
showApiKeysModal
=
ref
(
false
)
const
showAttributesModal
=
ref
(
false
)
const
showAttributesModal
=
ref
(
false
)
const
editingUser
=
ref
<
User
|
null
>
(
null
)
const
editingUser
=
ref
<
Admin
User
|
null
>
(
null
)
const
deletingUser
=
ref
<
User
|
null
>
(
null
)
const
deletingUser
=
ref
<
Admin
User
|
null
>
(
null
)
const
viewingUser
=
ref
<
User
|
null
>
(
null
)
const
viewingUser
=
ref
<
Admin
User
|
null
>
(
null
)
let
abortController
:
AbortController
|
null
=
null
let
abortController
:
AbortController
|
null
=
null
// Action Menu State
// Action Menu State
const
activeMenuId
=
ref
<
number
|
null
>
(
null
)
const
activeMenuId
=
ref
<
number
|
null
>
(
null
)
const
menuPosition
=
ref
<
{
top
:
number
;
left
:
number
}
|
null
>
(
null
)
const
menuPosition
=
ref
<
{
top
:
number
;
left
:
number
}
|
null
>
(
null
)
const
openActionMenu
=
(
user
:
User
,
e
:
MouseEvent
)
=>
{
const
openActionMenu
=
(
user
:
Admin
User
,
e
:
MouseEvent
)
=>
{
if
(
activeMenuId
.
value
===
user
.
id
)
{
if
(
activeMenuId
.
value
===
user
.
id
)
{
closeActionMenu
()
closeActionMenu
()
}
else
{
}
else
{
...
@@ -821,11 +821,11 @@ const handleClickOutside = (event: MouseEvent) => {
...
@@ -821,11 +821,11 @@ const handleClickOutside = (event: MouseEvent) => {
// Allowed groups modal state
// Allowed groups modal state
const
showAllowedGroupsModal
=
ref
(
false
)
const
showAllowedGroupsModal
=
ref
(
false
)
const
allowedGroupsUser
=
ref
<
User
|
null
>
(
null
)
const
allowedGroupsUser
=
ref
<
Admin
User
|
null
>
(
null
)
// Balance (Deposit/Withdraw) modal state
// Balance (Deposit/Withdraw) modal state
const
showBalanceModal
=
ref
(
false
)
const
showBalanceModal
=
ref
(
false
)
const
balanceUser
=
ref
<
User
|
null
>
(
null
)
const
balanceUser
=
ref
<
Admin
User
|
null
>
(
null
)
const
balanceOperation
=
ref
<
'
add
'
|
'
subtract
'
>
(
'
add
'
)
const
balanceOperation
=
ref
<
'
add
'
|
'
subtract
'
>
(
'
add
'
)
// 计算剩余天数
// 计算剩余天数
...
@@ -998,7 +998,7 @@ const applyFilter = () => {
...
@@ -998,7 +998,7 @@ const applyFilter = () => {
loadUsers
()
loadUsers
()
}
}
const
handleEdit
=
(
user
:
User
)
=>
{
const
handleEdit
=
(
user
:
Admin
User
)
=>
{
editingUser
.
value
=
user
editingUser
.
value
=
user
showEditModal
.
value
=
true
showEditModal
.
value
=
true
}
}
...
@@ -1008,7 +1008,7 @@ const closeEditModal = () => {
...
@@ -1008,7 +1008,7 @@ const closeEditModal = () => {
editingUser
.
value
=
null
editingUser
.
value
=
null
}
}
const
handleToggleStatus
=
async
(
user
:
User
)
=>
{
const
handleToggleStatus
=
async
(
user
:
Admin
User
)
=>
{
const
newStatus
=
user
.
status
===
'
active
'
?
'
disabled
'
:
'
active
'
const
newStatus
=
user
.
status
===
'
active
'
?
'
disabled
'
:
'
active
'
try
{
try
{
await
adminAPI
.
users
.
toggleStatus
(
user
.
id
,
newStatus
)
await
adminAPI
.
users
.
toggleStatus
(
user
.
id
,
newStatus
)
...
@@ -1022,7 +1022,7 @@ const handleToggleStatus = async (user: User) => {
...
@@ -1022,7 +1022,7 @@ const handleToggleStatus = async (user: User) => {
}
}
}
}
const
handleViewApiKeys
=
(
user
:
User
)
=>
{
const
handleViewApiKeys
=
(
user
:
Admin
User
)
=>
{
viewingUser
.
value
=
user
viewingUser
.
value
=
user
showApiKeysModal
.
value
=
true
showApiKeysModal
.
value
=
true
}
}
...
@@ -1032,7 +1032,7 @@ const closeApiKeysModal = () => {
...
@@ -1032,7 +1032,7 @@ const closeApiKeysModal = () => {
viewingUser
.
value
=
null
viewingUser
.
value
=
null
}
}
const
handleAllowedGroups
=
(
user
:
User
)
=>
{
const
handleAllowedGroups
=
(
user
:
Admin
User
)
=>
{
allowedGroupsUser
.
value
=
user
allowedGroupsUser
.
value
=
user
showAllowedGroupsModal
.
value
=
true
showAllowedGroupsModal
.
value
=
true
}
}
...
@@ -1042,7 +1042,7 @@ const closeAllowedGroupsModal = () => {
...
@@ -1042,7 +1042,7 @@ const closeAllowedGroupsModal = () => {
allowedGroupsUser
.
value
=
null
allowedGroupsUser
.
value
=
null
}
}
const
handleDelete
=
(
user
:
User
)
=>
{
const
handleDelete
=
(
user
:
Admin
User
)
=>
{
deletingUser
.
value
=
user
deletingUser
.
value
=
user
showDeleteDialog
.
value
=
true
showDeleteDialog
.
value
=
true
}
}
...
@@ -1061,13 +1061,13 @@ const confirmDelete = async () => {
...
@@ -1061,13 +1061,13 @@ const confirmDelete = async () => {
}
}
}
}
const
handleDeposit
=
(
user
:
User
)
=>
{
const
handleDeposit
=
(
user
:
Admin
User
)
=>
{
balanceUser
.
value
=
user
balanceUser
.
value
=
user
balanceOperation
.
value
=
'
add
'
balanceOperation
.
value
=
'
add
'
showBalanceModal
.
value
=
true
showBalanceModal
.
value
=
true
}
}
const
handleWithdraw
=
(
user
:
User
)
=>
{
const
handleWithdraw
=
(
user
:
Admin
User
)
=>
{
balanceUser
.
value
=
user
balanceUser
.
value
=
user
balanceOperation
.
value
=
'
subtract
'
balanceOperation
.
value
=
'
subtract
'
showBalanceModal
.
value
=
true
showBalanceModal
.
value
=
true
...
...
Prev
1
2
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment