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
Hide whitespace changes
Inline
Side-by-side
frontend/src/components/common/GroupSelector.vue
View file @
e7bc6250
...
...
@@ -42,13 +42,13 @@
import
{
computed
}
from
'
vue
'
import
{
useI18n
}
from
'
vue-i18n
'
import
GroupBadge
from
'
./GroupBadge.vue
'
import
type
{
Group
,
GroupPlatform
}
from
'
@/types
'
import
type
{
Admin
Group
,
GroupPlatform
}
from
'
@/types
'
const
{
t
}
=
useI18n
()
interface
Props
{
modelValue
:
number
[]
groups
:
Group
[]
groups
:
Admin
Group
[]
platform
?:
GroupPlatform
// Optional platform filter
mixedScheduling
?:
boolean
// For antigravity accounts: allow anthropic/gemini groups
}
...
...
frontend/src/types/index.ts
View file @
e7bc6250
...
...
@@ -27,7 +27,6 @@ export interface FetchOptions {
export
interface
User
{
id
:
number
username
:
string
notes
:
string
email
:
string
role
:
'
admin
'
|
'
user
'
// User role for authorization
balance
:
number
// User balance for API usage
...
...
@@ -39,6 +38,11 @@ export interface User {
updated_at
:
string
}
export
interface
AdminUser
extends
User
{
// 管理员备注(普通用户接口不返回)
notes
:
string
}
export
interface
LoginRequest
{
email
:
string
password
:
string
...
...
@@ -270,12 +274,17 @@ export interface Group {
// Claude Code 客户端限制
claude_code_only
:
boolean
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_enabled
:
boolean
// 分组下账号数量(仅管理员可见)
account_count
?:
number
created_at
:
string
updated_at
:
string
}
export
interface
ApiKey
{
...
...
@@ -637,7 +646,6 @@ export interface UsageLog {
total_cost
:
number
actual_cost
:
number
rate_multiplier
:
number
account_rate_multiplier
?:
number
|
null
billing_type
:
number
stream
:
boolean
...
...
@@ -651,18 +659,30 @@ export interface UsageLog {
// User-Agent
user_agent
:
string
|
null
// IP 地址(仅管理员可见)
ip_address
:
string
|
null
created_at
:
string
user
?:
User
api_key
?:
ApiKey
account
?:
Account
group
?:
Group
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
{
start_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'
import
PlatformTypeBadge
from
'
@/components/common/PlatformTypeBadge.vue
'
import
Icon
from
'
@/components/icons/Icon.vue
'
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
appStore
=
useAppStore
()
const
authStore
=
useAuthStore
()
const
proxies
=
ref
<
Proxy
[]
>
([])
const
groups
=
ref
<
Group
[]
>
([])
const
groups
=
ref
<
Admin
Group
[]
>
([])
const
selIds
=
ref
<
number
[]
>
([])
const
showCreate
=
ref
(
false
)
const
showEdit
=
ref
(
false
)
...
...
frontend/src/views/admin/GroupsView.vue
View file @
e7bc6250
...
...
@@ -1107,7 +1107,7 @@ import { useI18n } from 'vue-i18n'
import
{
useAppStore
}
from
'
@/stores/app
'
import
{
useOnboardingStore
}
from
'
@/stores/onboarding
'
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
AppLayout
from
'
@/components/layout/AppLayout.vue
'
import
TablePageLayout
from
'
@/components/layout/TablePageLayout.vue
'
...
...
@@ -1202,7 +1202,7 @@ const fallbackGroupOptionsForEdit = computed(() => {
return
options
}
)
const
groups
=
ref
<
Group
[]
>
([])
const
groups
=
ref
<
Admin
Group
[]
>
([])
const
loading
=
ref
(
false
)
const
searchQuery
=
ref
(
''
)
const
filters
=
reactive
({
...
...
@@ -1223,8 +1223,8 @@ const showCreateModal = ref(false)
const
showEditModal
=
ref
(
false
)
const
showDeleteDialog
=
ref
(
false
)
const
submitting
=
ref
(
false
)
const
editingGroup
=
ref
<
Group
|
null
>
(
null
)
const
deletingGroup
=
ref
<
Group
|
null
>
(
null
)
const
editingGroup
=
ref
<
Admin
Group
|
null
>
(
null
)
const
deletingGroup
=
ref
<
Admin
Group
|
null
>
(
null
)
const
createForm
=
reactive
({
name
:
''
,
...
...
@@ -1529,7 +1529,7 @@ const handleCreateGroup = async () => {
}
}
const
handleEdit
=
async
(
group
:
Group
)
=>
{
const
handleEdit
=
async
(
group
:
Admin
Group
)
=>
{
editingGroup
.
value
=
group
editForm
.
name
=
group
.
name
editForm
.
description
=
group
.
description
||
''
...
...
@@ -1585,7 +1585,7 @@ const handleUpdateGroup = async () => {
}
}
const
handleDelete
=
(
group
:
Group
)
=>
{
const
handleDelete
=
(
group
:
Admin
Group
)
=>
{
deletingGroup
.
value
=
group
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
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
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
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
'
)
let
abortController
:
AbortController
|
null
=
null
;
let
exportAbortController
:
AbortController
|
null
=
null
const
exportProgress
=
reactive
({
show
:
false
,
progress
:
0
,
current
:
0
,
total
:
0
,
estimatedTime
:
''
})
...
...
@@ -92,7 +92,7 @@ const exportToExcel = async () => {
if
(
exporting
.
value
)
return
;
exporting
.
value
=
true
;
exportProgress
.
show
=
true
const
c
=
new
AbortController
();
exportAbortController
=
c
try
{
const
all
:
UsageLog
[]
=
[];
let
p
=
1
;
let
total
=
pagination
.
total
const
all
:
Admin
UsageLog
[]
=
[];
let
p
=
1
;
let
total
=
pagination
.
total
while
(
true
)
{
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
}
...
...
frontend/src/views/admin/UsersView.vue
View file @
e7bc6250
...
...
@@ -492,7 +492,7 @@ import Icon from '@/components/icons/Icon.vue'
const
{
t
}
=
useI18n
()
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
{
Column
}
from
'
@/components/common/types
'
import
AppLayout
from
'
@/components/layout/AppLayout.vue
'
...
...
@@ -637,7 +637,7 @@ const columns = computed<Column[]>(() =>
)
)
const
users
=
ref
<
User
[]
>
([])
const
users
=
ref
<
Admin
User
[]
>
([])
const
loading
=
ref
(
false
)
const
searchQuery
=
ref
(
''
)
...
...
@@ -736,16 +736,16 @@ const showEditModal = ref(false)
const
showDeleteDialog
=
ref
(
false
)
const
showApiKeysModal
=
ref
(
false
)
const
showAttributesModal
=
ref
(
false
)
const
editingUser
=
ref
<
User
|
null
>
(
null
)
const
deletingUser
=
ref
<
User
|
null
>
(
null
)
const
viewingUser
=
ref
<
User
|
null
>
(
null
)
const
editingUser
=
ref
<
Admin
User
|
null
>
(
null
)
const
deletingUser
=
ref
<
Admin
User
|
null
>
(
null
)
const
viewingUser
=
ref
<
Admin
User
|
null
>
(
null
)
let
abortController
:
AbortController
|
null
=
null
// Action Menu State
const
activeMenuId
=
ref
<
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
)
{
closeActionMenu
()
}
else
{
...
...
@@ -821,11 +821,11 @@ const handleClickOutside = (event: MouseEvent) => {
// Allowed groups modal state
const
showAllowedGroupsModal
=
ref
(
false
)
const
allowedGroupsUser
=
ref
<
User
|
null
>
(
null
)
const
allowedGroupsUser
=
ref
<
Admin
User
|
null
>
(
null
)
// Balance (Deposit/Withdraw) modal state
const
showBalanceModal
=
ref
(
false
)
const
balanceUser
=
ref
<
User
|
null
>
(
null
)
const
balanceUser
=
ref
<
Admin
User
|
null
>
(
null
)
const
balanceOperation
=
ref
<
'
add
'
|
'
subtract
'
>
(
'
add
'
)
// 计算剩余天数
...
...
@@ -998,7 +998,7 @@ const applyFilter = () => {
loadUsers
()
}
const
handleEdit
=
(
user
:
User
)
=>
{
const
handleEdit
=
(
user
:
Admin
User
)
=>
{
editingUser
.
value
=
user
showEditModal
.
value
=
true
}
...
...
@@ -1008,7 +1008,7 @@ const closeEditModal = () => {
editingUser
.
value
=
null
}
const
handleToggleStatus
=
async
(
user
:
User
)
=>
{
const
handleToggleStatus
=
async
(
user
:
Admin
User
)
=>
{
const
newStatus
=
user
.
status
===
'
active
'
?
'
disabled
'
:
'
active
'
try
{
await
adminAPI
.
users
.
toggleStatus
(
user
.
id
,
newStatus
)
...
...
@@ -1022,7 +1022,7 @@ const handleToggleStatus = async (user: User) => {
}
}
const
handleViewApiKeys
=
(
user
:
User
)
=>
{
const
handleViewApiKeys
=
(
user
:
Admin
User
)
=>
{
viewingUser
.
value
=
user
showApiKeysModal
.
value
=
true
}
...
...
@@ -1032,7 +1032,7 @@ const closeApiKeysModal = () => {
viewingUser
.
value
=
null
}
const
handleAllowedGroups
=
(
user
:
User
)
=>
{
const
handleAllowedGroups
=
(
user
:
Admin
User
)
=>
{
allowedGroupsUser
.
value
=
user
showAllowedGroupsModal
.
value
=
true
}
...
...
@@ -1042,7 +1042,7 @@ const closeAllowedGroupsModal = () => {
allowedGroupsUser
.
value
=
null
}
const
handleDelete
=
(
user
:
User
)
=>
{
const
handleDelete
=
(
user
:
Admin
User
)
=>
{
deletingUser
.
value
=
user
showDeleteDialog
.
value
=
true
}
...
...
@@ -1061,13 +1061,13 @@ const confirmDelete = async () => {
}
}
const
handleDeposit
=
(
user
:
User
)
=>
{
const
handleDeposit
=
(
user
:
Admin
User
)
=>
{
balanceUser
.
value
=
user
balanceOperation
.
value
=
'
add
'
showBalanceModal
.
value
=
true
}
const
handleWithdraw
=
(
user
:
User
)
=>
{
const
handleWithdraw
=
(
user
:
Admin
User
)
=>
{
balanceUser
.
value
=
user
balanceOperation
.
value
=
'
subtract
'
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