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
backend/internal/handler/admin/group_handler.go
View file @
e7bc6250
...
@@ -94,9 +94,9 @@ func (h *GroupHandler) List(c *gin.Context) {
...
@@ -94,9 +94,9 @@ func (h *GroupHandler) List(c *gin.Context) {
return
return
}
}
outGroups
:=
make
([]
dto
.
Group
,
0
,
len
(
groups
))
outGroups
:=
make
([]
dto
.
Admin
Group
,
0
,
len
(
groups
))
for
i
:=
range
groups
{
for
i
:=
range
groups
{
outGroups
=
append
(
outGroups
,
*
dto
.
GroupFromService
(
&
groups
[
i
]))
outGroups
=
append
(
outGroups
,
*
dto
.
GroupFromService
Admin
(
&
groups
[
i
]))
}
}
response
.
Paginated
(
c
,
outGroups
,
total
,
page
,
pageSize
)
response
.
Paginated
(
c
,
outGroups
,
total
,
page
,
pageSize
)
}
}
...
@@ -120,9 +120,9 @@ func (h *GroupHandler) GetAll(c *gin.Context) {
...
@@ -120,9 +120,9 @@ func (h *GroupHandler) GetAll(c *gin.Context) {
return
return
}
}
outGroups
:=
make
([]
dto
.
Group
,
0
,
len
(
groups
))
outGroups
:=
make
([]
dto
.
Admin
Group
,
0
,
len
(
groups
))
for
i
:=
range
groups
{
for
i
:=
range
groups
{
outGroups
=
append
(
outGroups
,
*
dto
.
GroupFromService
(
&
groups
[
i
]))
outGroups
=
append
(
outGroups
,
*
dto
.
GroupFromService
Admin
(
&
groups
[
i
]))
}
}
response
.
Success
(
c
,
outGroups
)
response
.
Success
(
c
,
outGroups
)
}
}
...
@@ -142,7 +142,7 @@ func (h *GroupHandler) GetByID(c *gin.Context) {
...
@@ -142,7 +142,7 @@ func (h *GroupHandler) GetByID(c *gin.Context) {
return
return
}
}
response
.
Success
(
c
,
dto
.
GroupFromService
(
group
))
response
.
Success
(
c
,
dto
.
GroupFromService
Admin
(
group
))
}
}
// Create handles creating a new group
// Create handles creating a new group
...
@@ -177,7 +177,7 @@ func (h *GroupHandler) Create(c *gin.Context) {
...
@@ -177,7 +177,7 @@ func (h *GroupHandler) Create(c *gin.Context) {
return
return
}
}
response
.
Success
(
c
,
dto
.
GroupFromService
(
group
))
response
.
Success
(
c
,
dto
.
GroupFromService
Admin
(
group
))
}
}
// Update handles updating a group
// Update handles updating a group
...
@@ -219,7 +219,7 @@ func (h *GroupHandler) Update(c *gin.Context) {
...
@@ -219,7 +219,7 @@ func (h *GroupHandler) Update(c *gin.Context) {
return
return
}
}
response
.
Success
(
c
,
dto
.
GroupFromService
(
group
))
response
.
Success
(
c
,
dto
.
GroupFromService
Admin
(
group
))
}
}
// Delete handles deleting a group
// Delete handles deleting a group
...
...
backend/internal/handler/admin/redeem_handler.go
View file @
e7bc6250
...
@@ -54,9 +54,9 @@ func (h *RedeemHandler) List(c *gin.Context) {
...
@@ -54,9 +54,9 @@ func (h *RedeemHandler) List(c *gin.Context) {
return
return
}
}
out
:=
make
([]
dto
.
RedeemCode
,
0
,
len
(
codes
))
out
:=
make
([]
dto
.
Admin
RedeemCode
,
0
,
len
(
codes
))
for
i
:=
range
codes
{
for
i
:=
range
codes
{
out
=
append
(
out
,
*
dto
.
RedeemCodeFromService
(
&
codes
[
i
]))
out
=
append
(
out
,
*
dto
.
RedeemCodeFromService
Admin
(
&
codes
[
i
]))
}
}
response
.
Paginated
(
c
,
out
,
total
,
page
,
pageSize
)
response
.
Paginated
(
c
,
out
,
total
,
page
,
pageSize
)
}
}
...
@@ -76,7 +76,7 @@ func (h *RedeemHandler) GetByID(c *gin.Context) {
...
@@ -76,7 +76,7 @@ func (h *RedeemHandler) GetByID(c *gin.Context) {
return
return
}
}
response
.
Success
(
c
,
dto
.
RedeemCodeFromService
(
code
))
response
.
Success
(
c
,
dto
.
RedeemCodeFromService
Admin
(
code
))
}
}
// Generate handles generating new redeem codes
// Generate handles generating new redeem codes
...
@@ -100,9 +100,9 @@ func (h *RedeemHandler) Generate(c *gin.Context) {
...
@@ -100,9 +100,9 @@ func (h *RedeemHandler) Generate(c *gin.Context) {
return
return
}
}
out
:=
make
([]
dto
.
RedeemCode
,
0
,
len
(
codes
))
out
:=
make
([]
dto
.
Admin
RedeemCode
,
0
,
len
(
codes
))
for
i
:=
range
codes
{
for
i
:=
range
codes
{
out
=
append
(
out
,
*
dto
.
RedeemCodeFromService
(
&
codes
[
i
]))
out
=
append
(
out
,
*
dto
.
RedeemCodeFromService
Admin
(
&
codes
[
i
]))
}
}
response
.
Success
(
c
,
out
)
response
.
Success
(
c
,
out
)
}
}
...
@@ -163,7 +163,7 @@ func (h *RedeemHandler) Expire(c *gin.Context) {
...
@@ -163,7 +163,7 @@ func (h *RedeemHandler) Expire(c *gin.Context) {
return
return
}
}
response
.
Success
(
c
,
dto
.
RedeemCodeFromService
(
code
))
response
.
Success
(
c
,
dto
.
RedeemCodeFromService
Admin
(
code
))
}
}
// GetStats handles getting redeem code statistics
// GetStats handles getting redeem code statistics
...
...
backend/internal/handler/admin/subscription_handler.go
View file @
e7bc6250
...
@@ -83,9 +83,9 @@ func (h *SubscriptionHandler) List(c *gin.Context) {
...
@@ -83,9 +83,9 @@ func (h *SubscriptionHandler) List(c *gin.Context) {
return
return
}
}
out
:=
make
([]
dto
.
UserSubscription
,
0
,
len
(
subscriptions
))
out
:=
make
([]
dto
.
Admin
UserSubscription
,
0
,
len
(
subscriptions
))
for
i
:=
range
subscriptions
{
for
i
:=
range
subscriptions
{
out
=
append
(
out
,
*
dto
.
UserSubscriptionFromService
(
&
subscriptions
[
i
]))
out
=
append
(
out
,
*
dto
.
UserSubscriptionFromService
Admin
(
&
subscriptions
[
i
]))
}
}
response
.
PaginatedWithResult
(
c
,
out
,
toResponsePagination
(
pagination
))
response
.
PaginatedWithResult
(
c
,
out
,
toResponsePagination
(
pagination
))
}
}
...
@@ -105,7 +105,7 @@ func (h *SubscriptionHandler) GetByID(c *gin.Context) {
...
@@ -105,7 +105,7 @@ func (h *SubscriptionHandler) GetByID(c *gin.Context) {
return
return
}
}
response
.
Success
(
c
,
dto
.
UserSubscriptionFromService
(
subscription
))
response
.
Success
(
c
,
dto
.
UserSubscriptionFromService
Admin
(
subscription
))
}
}
// GetProgress handles getting subscription usage progress
// GetProgress handles getting subscription usage progress
...
@@ -150,7 +150,7 @@ func (h *SubscriptionHandler) Assign(c *gin.Context) {
...
@@ -150,7 +150,7 @@ func (h *SubscriptionHandler) Assign(c *gin.Context) {
return
return
}
}
response
.
Success
(
c
,
dto
.
UserSubscriptionFromService
(
subscription
))
response
.
Success
(
c
,
dto
.
UserSubscriptionFromService
Admin
(
subscription
))
}
}
// BulkAssign handles bulk assigning subscriptions to multiple users
// BulkAssign handles bulk assigning subscriptions to multiple users
...
@@ -201,7 +201,7 @@ func (h *SubscriptionHandler) Extend(c *gin.Context) {
...
@@ -201,7 +201,7 @@ func (h *SubscriptionHandler) Extend(c *gin.Context) {
return
return
}
}
response
.
Success
(
c
,
dto
.
UserSubscriptionFromService
(
subscription
))
response
.
Success
(
c
,
dto
.
UserSubscriptionFromService
Admin
(
subscription
))
}
}
// Revoke handles revoking a subscription
// Revoke handles revoking a subscription
...
@@ -239,9 +239,9 @@ func (h *SubscriptionHandler) ListByGroup(c *gin.Context) {
...
@@ -239,9 +239,9 @@ func (h *SubscriptionHandler) ListByGroup(c *gin.Context) {
return
return
}
}
out
:=
make
([]
dto
.
UserSubscription
,
0
,
len
(
subscriptions
))
out
:=
make
([]
dto
.
Admin
UserSubscription
,
0
,
len
(
subscriptions
))
for
i
:=
range
subscriptions
{
for
i
:=
range
subscriptions
{
out
=
append
(
out
,
*
dto
.
UserSubscriptionFromService
(
&
subscriptions
[
i
]))
out
=
append
(
out
,
*
dto
.
UserSubscriptionFromService
Admin
(
&
subscriptions
[
i
]))
}
}
response
.
PaginatedWithResult
(
c
,
out
,
toResponsePagination
(
pagination
))
response
.
PaginatedWithResult
(
c
,
out
,
toResponsePagination
(
pagination
))
}
}
...
@@ -261,9 +261,9 @@ func (h *SubscriptionHandler) ListByUser(c *gin.Context) {
...
@@ -261,9 +261,9 @@ func (h *SubscriptionHandler) ListByUser(c *gin.Context) {
return
return
}
}
out
:=
make
([]
dto
.
UserSubscription
,
0
,
len
(
subscriptions
))
out
:=
make
([]
dto
.
Admin
UserSubscription
,
0
,
len
(
subscriptions
))
for
i
:=
range
subscriptions
{
for
i
:=
range
subscriptions
{
out
=
append
(
out
,
*
dto
.
UserSubscriptionFromService
(
&
subscriptions
[
i
]))
out
=
append
(
out
,
*
dto
.
UserSubscriptionFromService
Admin
(
&
subscriptions
[
i
]))
}
}
response
.
Success
(
c
,
out
)
response
.
Success
(
c
,
out
)
}
}
...
...
backend/internal/handler/admin/usage_handler.go
View file @
e7bc6250
...
@@ -163,7 +163,7 @@ func (h *UsageHandler) List(c *gin.Context) {
...
@@ -163,7 +163,7 @@ func (h *UsageHandler) List(c *gin.Context) {
return
return
}
}
out
:=
make
([]
dto
.
UsageLog
,
0
,
len
(
records
))
out
:=
make
([]
dto
.
Admin
UsageLog
,
0
,
len
(
records
))
for
i
:=
range
records
{
for
i
:=
range
records
{
out
=
append
(
out
,
*
dto
.
UsageLogFromServiceAdmin
(
&
records
[
i
]))
out
=
append
(
out
,
*
dto
.
UsageLogFromServiceAdmin
(
&
records
[
i
]))
}
}
...
...
backend/internal/handler/admin/user_handler.go
View file @
e7bc6250
...
@@ -84,9 +84,9 @@ func (h *UserHandler) List(c *gin.Context) {
...
@@ -84,9 +84,9 @@ func (h *UserHandler) List(c *gin.Context) {
return
return
}
}
out
:=
make
([]
dto
.
User
,
0
,
len
(
users
))
out
:=
make
([]
dto
.
Admin
User
,
0
,
len
(
users
))
for
i
:=
range
users
{
for
i
:=
range
users
{
out
=
append
(
out
,
*
dto
.
UserFromService
(
&
users
[
i
]))
out
=
append
(
out
,
*
dto
.
UserFromService
Admin
(
&
users
[
i
]))
}
}
response
.
Paginated
(
c
,
out
,
total
,
page
,
pageSize
)
response
.
Paginated
(
c
,
out
,
total
,
page
,
pageSize
)
}
}
...
@@ -129,7 +129,7 @@ func (h *UserHandler) GetByID(c *gin.Context) {
...
@@ -129,7 +129,7 @@ func (h *UserHandler) GetByID(c *gin.Context) {
return
return
}
}
response
.
Success
(
c
,
dto
.
UserFromService
(
user
))
response
.
Success
(
c
,
dto
.
UserFromService
Admin
(
user
))
}
}
// Create handles creating a new user
// Create handles creating a new user
...
@@ -155,7 +155,7 @@ func (h *UserHandler) Create(c *gin.Context) {
...
@@ -155,7 +155,7 @@ func (h *UserHandler) Create(c *gin.Context) {
return
return
}
}
response
.
Success
(
c
,
dto
.
UserFromService
(
user
))
response
.
Success
(
c
,
dto
.
UserFromService
Admin
(
user
))
}
}
// Update handles updating a user
// Update handles updating a user
...
@@ -189,7 +189,7 @@ func (h *UserHandler) Update(c *gin.Context) {
...
@@ -189,7 +189,7 @@ func (h *UserHandler) Update(c *gin.Context) {
return
return
}
}
response
.
Success
(
c
,
dto
.
UserFromService
(
user
))
response
.
Success
(
c
,
dto
.
UserFromService
Admin
(
user
))
}
}
// Delete handles deleting a user
// Delete handles deleting a user
...
@@ -231,7 +231,7 @@ func (h *UserHandler) UpdateBalance(c *gin.Context) {
...
@@ -231,7 +231,7 @@ func (h *UserHandler) UpdateBalance(c *gin.Context) {
return
return
}
}
response
.
Success
(
c
,
dto
.
UserFromService
(
user
))
response
.
Success
(
c
,
dto
.
UserFromService
Admin
(
user
))
}
}
// GetUserAPIKeys handles getting user's API keys
// GetUserAPIKeys handles getting user's API keys
...
...
backend/internal/handler/dto/mappers.go
View file @
e7bc6250
...
@@ -15,7 +15,6 @@ func UserFromServiceShallow(u *service.User) *User {
...
@@ -15,7 +15,6 @@ func UserFromServiceShallow(u *service.User) *User {
ID
:
u
.
ID
,
ID
:
u
.
ID
,
Email
:
u
.
Email
,
Email
:
u
.
Email
,
Username
:
u
.
Username
,
Username
:
u
.
Username
,
Notes
:
u
.
Notes
,
Role
:
u
.
Role
,
Role
:
u
.
Role
,
Balance
:
u
.
Balance
,
Balance
:
u
.
Balance
,
Concurrency
:
u
.
Concurrency
,
Concurrency
:
u
.
Concurrency
,
...
@@ -48,6 +47,22 @@ func UserFromService(u *service.User) *User {
...
@@ -48,6 +47,22 @@ func UserFromService(u *service.User) *User {
return
out
return
out
}
}
// UserFromServiceAdmin converts a service User to DTO for admin users.
// It includes notes - user-facing endpoints must not use this.
func
UserFromServiceAdmin
(
u
*
service
.
User
)
*
AdminUser
{
if
u
==
nil
{
return
nil
}
base
:=
UserFromService
(
u
)
if
base
==
nil
{
return
nil
}
return
&
AdminUser
{
User
:
*
base
,
Notes
:
u
.
Notes
,
}
}
func
APIKeyFromService
(
k
*
service
.
APIKey
)
*
APIKey
{
func
APIKeyFromService
(
k
*
service
.
APIKey
)
*
APIKey
{
if
k
==
nil
{
if
k
==
nil
{
return
nil
return
nil
...
@@ -72,7 +87,41 @@ func GroupFromServiceShallow(g *service.Group) *Group {
...
@@ -72,7 +87,41 @@ func GroupFromServiceShallow(g *service.Group) *Group {
if
g
==
nil
{
if
g
==
nil
{
return
nil
return
nil
}
}
return
&
Group
{
out
:=
groupFromServiceBase
(
g
)
return
&
out
}
func
GroupFromService
(
g
*
service
.
Group
)
*
Group
{
if
g
==
nil
{
return
nil
}
return
GroupFromServiceShallow
(
g
)
}
// GroupFromServiceAdmin converts a service Group to DTO for admin users.
// It includes internal fields like model_routing and account_count.
func
GroupFromServiceAdmin
(
g
*
service
.
Group
)
*
AdminGroup
{
if
g
==
nil
{
return
nil
}
out
:=
&
AdminGroup
{
Group
:
groupFromServiceBase
(
g
),
ModelRouting
:
g
.
ModelRouting
,
ModelRoutingEnabled
:
g
.
ModelRoutingEnabled
,
AccountCount
:
g
.
AccountCount
,
}
if
len
(
g
.
AccountGroups
)
>
0
{
out
.
AccountGroups
=
make
([]
AccountGroup
,
0
,
len
(
g
.
AccountGroups
))
for
i
:=
range
g
.
AccountGroups
{
ag
:=
g
.
AccountGroups
[
i
]
out
.
AccountGroups
=
append
(
out
.
AccountGroups
,
*
AccountGroupFromService
(
&
ag
))
}
}
return
out
}
func
groupFromServiceBase
(
g
*
service
.
Group
)
Group
{
return
Group
{
ID
:
g
.
ID
,
ID
:
g
.
ID
,
Name
:
g
.
Name
,
Name
:
g
.
Name
,
Description
:
g
.
Description
,
Description
:
g
.
Description
,
...
@@ -89,27 +138,9 @@ func GroupFromServiceShallow(g *service.Group) *Group {
...
@@ -89,27 +138,9 @@ func GroupFromServiceShallow(g *service.Group) *Group {
ImagePrice4K
:
g
.
ImagePrice4K
,
ImagePrice4K
:
g
.
ImagePrice4K
,
ClaudeCodeOnly
:
g
.
ClaudeCodeOnly
,
ClaudeCodeOnly
:
g
.
ClaudeCodeOnly
,
FallbackGroupID
:
g
.
FallbackGroupID
,
FallbackGroupID
:
g
.
FallbackGroupID
,
ModelRouting
:
g
.
ModelRouting
,
ModelRoutingEnabled
:
g
.
ModelRoutingEnabled
,
CreatedAt
:
g
.
CreatedAt
,
CreatedAt
:
g
.
CreatedAt
,
UpdatedAt
:
g
.
UpdatedAt
,
UpdatedAt
:
g
.
UpdatedAt
,
AccountCount
:
g
.
AccountCount
,
}
}
func
GroupFromService
(
g
*
service
.
Group
)
*
Group
{
if
g
==
nil
{
return
nil
}
}
out
:=
GroupFromServiceShallow
(
g
)
if
len
(
g
.
AccountGroups
)
>
0
{
out
.
AccountGroups
=
make
([]
AccountGroup
,
0
,
len
(
g
.
AccountGroups
))
for
i
:=
range
g
.
AccountGroups
{
ag
:=
g
.
AccountGroups
[
i
]
out
.
AccountGroups
=
append
(
out
.
AccountGroups
,
*
AccountGroupFromService
(
&
ag
))
}
}
return
out
}
}
func
AccountFromServiceShallow
(
a
*
service
.
Account
)
*
Account
{
func
AccountFromServiceShallow
(
a
*
service
.
Account
)
*
Account
{
...
@@ -273,7 +304,24 @@ func RedeemCodeFromService(rc *service.RedeemCode) *RedeemCode {
...
@@ -273,7 +304,24 @@ func RedeemCodeFromService(rc *service.RedeemCode) *RedeemCode {
if
rc
==
nil
{
if
rc
==
nil
{
return
nil
return
nil
}
}
return
&
RedeemCode
{
out
:=
redeemCodeFromServiceBase
(
rc
)
return
&
out
}
// RedeemCodeFromServiceAdmin converts a service RedeemCode to DTO for admin users.
// It includes notes - user-facing endpoints must not use this.
func
RedeemCodeFromServiceAdmin
(
rc
*
service
.
RedeemCode
)
*
AdminRedeemCode
{
if
rc
==
nil
{
return
nil
}
return
&
AdminRedeemCode
{
RedeemCode
:
redeemCodeFromServiceBase
(
rc
),
Notes
:
rc
.
Notes
,
}
}
func
redeemCodeFromServiceBase
(
rc
*
service
.
RedeemCode
)
RedeemCode
{
return
RedeemCode
{
ID
:
rc
.
ID
,
ID
:
rc
.
ID
,
Code
:
rc
.
Code
,
Code
:
rc
.
Code
,
Type
:
rc
.
Type
,
Type
:
rc
.
Type
,
...
@@ -281,7 +329,6 @@ func RedeemCodeFromService(rc *service.RedeemCode) *RedeemCode {
...
@@ -281,7 +329,6 @@ func RedeemCodeFromService(rc *service.RedeemCode) *RedeemCode {
Status
:
rc
.
Status
,
Status
:
rc
.
Status
,
UsedBy
:
rc
.
UsedBy
,
UsedBy
:
rc
.
UsedBy
,
UsedAt
:
rc
.
UsedAt
,
UsedAt
:
rc
.
UsedAt
,
Notes
:
rc
.
Notes
,
CreatedAt
:
rc
.
CreatedAt
,
CreatedAt
:
rc
.
CreatedAt
,
GroupID
:
rc
.
GroupID
,
GroupID
:
rc
.
GroupID
,
ValidityDays
:
rc
.
ValidityDays
,
ValidityDays
:
rc
.
ValidityDays
,
...
@@ -302,14 +349,9 @@ func AccountSummaryFromService(a *service.Account) *AccountSummary {
...
@@ -302,14 +349,9 @@ func AccountSummaryFromService(a *service.Account) *AccountSummary {
}
}
}
}
// usageLogFromServiceBase is a helper that converts service UsageLog to DTO.
func
usageLogFromServiceUser
(
l
*
service
.
UsageLog
)
UsageLog
{
// The account parameter allows caller to control what Account info is included.
// 普通用户 DTO:严禁包含管理员字段(例如 account_rate_multiplier、ip_address、account)。
// The includeIPAddress parameter controls whether to include the IP address (admin-only).
return
UsageLog
{
func
usageLogFromServiceBase
(
l
*
service
.
UsageLog
,
account
*
AccountSummary
,
includeIPAddress
bool
)
*
UsageLog
{
if
l
==
nil
{
return
nil
}
result
:=
&
UsageLog
{
ID
:
l
.
ID
,
ID
:
l
.
ID
,
UserID
:
l
.
UserID
,
UserID
:
l
.
UserID
,
APIKeyID
:
l
.
APIKeyID
,
APIKeyID
:
l
.
APIKeyID
,
...
@@ -331,7 +373,6 @@ func usageLogFromServiceBase(l *service.UsageLog, account *AccountSummary, inclu
...
@@ -331,7 +373,6 @@ func usageLogFromServiceBase(l *service.UsageLog, account *AccountSummary, inclu
TotalCost
:
l
.
TotalCost
,
TotalCost
:
l
.
TotalCost
,
ActualCost
:
l
.
ActualCost
,
ActualCost
:
l
.
ActualCost
,
RateMultiplier
:
l
.
RateMultiplier
,
RateMultiplier
:
l
.
RateMultiplier
,
AccountRateMultiplier
:
l
.
AccountRateMultiplier
,
BillingType
:
l
.
BillingType
,
BillingType
:
l
.
BillingType
,
Stream
:
l
.
Stream
,
Stream
:
l
.
Stream
,
DurationMs
:
l
.
DurationMs
,
DurationMs
:
l
.
DurationMs
,
...
@@ -342,30 +383,33 @@ func usageLogFromServiceBase(l *service.UsageLog, account *AccountSummary, inclu
...
@@ -342,30 +383,33 @@ func usageLogFromServiceBase(l *service.UsageLog, account *AccountSummary, inclu
CreatedAt
:
l
.
CreatedAt
,
CreatedAt
:
l
.
CreatedAt
,
User
:
UserFromServiceShallow
(
l
.
User
),
User
:
UserFromServiceShallow
(
l
.
User
),
APIKey
:
APIKeyFromService
(
l
.
APIKey
),
APIKey
:
APIKeyFromService
(
l
.
APIKey
),
Account
:
account
,
Group
:
GroupFromServiceShallow
(
l
.
Group
),
Group
:
GroupFromServiceShallow
(
l
.
Group
),
Subscription
:
UserSubscriptionFromService
(
l
.
Subscription
),
Subscription
:
UserSubscriptionFromService
(
l
.
Subscription
),
}
}
// IP 地址仅对管理员可见
if
includeIPAddress
{
result
.
IPAddress
=
l
.
IPAddress
}
return
result
}
}
// UsageLogFromService converts a service UsageLog to DTO for regular users.
// UsageLogFromService converts a service UsageLog to DTO for regular users.
// It excludes Account details and IP address - users should not see these.
// It excludes Account details and IP address - users should not see these.
func
UsageLogFromService
(
l
*
service
.
UsageLog
)
*
UsageLog
{
func
UsageLogFromService
(
l
*
service
.
UsageLog
)
*
UsageLog
{
return
usageLogFromServiceBase
(
l
,
nil
,
false
)
if
l
==
nil
{
return
nil
}
u
:=
usageLogFromServiceUser
(
l
)
return
&
u
}
}
// UsageLogFromServiceAdmin converts a service UsageLog to DTO for admin users.
// UsageLogFromServiceAdmin converts a service UsageLog to DTO for admin users.
// It includes minimal Account info (ID, Name only) and IP address.
// It includes minimal Account info (ID, Name only) and IP address.
func
UsageLogFromServiceAdmin
(
l
*
service
.
UsageLog
)
*
UsageLog
{
func
UsageLogFromServiceAdmin
(
l
*
service
.
UsageLog
)
*
Admin
UsageLog
{
if
l
==
nil
{
if
l
==
nil
{
return
nil
return
nil
}
}
return
usageLogFromServiceBase
(
l
,
AccountSummaryFromService
(
l
.
Account
),
true
)
return
&
AdminUsageLog
{
UsageLog
:
usageLogFromServiceUser
(
l
),
AccountRateMultiplier
:
l
.
AccountRateMultiplier
,
IPAddress
:
l
.
IPAddress
,
Account
:
AccountSummaryFromService
(
l
.
Account
),
}
}
}
func
UsageCleanupTaskFromService
(
task
*
service
.
UsageCleanupTask
)
*
UsageCleanupTask
{
func
UsageCleanupTaskFromService
(
task
*
service
.
UsageCleanupTask
)
*
UsageCleanupTask
{
...
@@ -414,7 +458,27 @@ func UserSubscriptionFromService(sub *service.UserSubscription) *UserSubscriptio
...
@@ -414,7 +458,27 @@ func UserSubscriptionFromService(sub *service.UserSubscription) *UserSubscriptio
if
sub
==
nil
{
if
sub
==
nil
{
return
nil
return
nil
}
}
return
&
UserSubscription
{
out
:=
userSubscriptionFromServiceBase
(
sub
)
return
&
out
}
// UserSubscriptionFromServiceAdmin converts a service UserSubscription to DTO for admin users.
// It includes assignment metadata and notes.
func
UserSubscriptionFromServiceAdmin
(
sub
*
service
.
UserSubscription
)
*
AdminUserSubscription
{
if
sub
==
nil
{
return
nil
}
return
&
AdminUserSubscription
{
UserSubscription
:
userSubscriptionFromServiceBase
(
sub
),
AssignedBy
:
sub
.
AssignedBy
,
AssignedAt
:
sub
.
AssignedAt
,
Notes
:
sub
.
Notes
,
AssignedByUser
:
UserFromServiceShallow
(
sub
.
AssignedByUser
),
}
}
func
userSubscriptionFromServiceBase
(
sub
*
service
.
UserSubscription
)
UserSubscription
{
return
UserSubscription
{
ID
:
sub
.
ID
,
ID
:
sub
.
ID
,
UserID
:
sub
.
UserID
,
UserID
:
sub
.
UserID
,
GroupID
:
sub
.
GroupID
,
GroupID
:
sub
.
GroupID
,
...
@@ -427,14 +491,10 @@ func UserSubscriptionFromService(sub *service.UserSubscription) *UserSubscriptio
...
@@ -427,14 +491,10 @@ func UserSubscriptionFromService(sub *service.UserSubscription) *UserSubscriptio
DailyUsageUSD
:
sub
.
DailyUsageUSD
,
DailyUsageUSD
:
sub
.
DailyUsageUSD
,
WeeklyUsageUSD
:
sub
.
WeeklyUsageUSD
,
WeeklyUsageUSD
:
sub
.
WeeklyUsageUSD
,
MonthlyUsageUSD
:
sub
.
MonthlyUsageUSD
,
MonthlyUsageUSD
:
sub
.
MonthlyUsageUSD
,
AssignedBy
:
sub
.
AssignedBy
,
AssignedAt
:
sub
.
AssignedAt
,
Notes
:
sub
.
Notes
,
CreatedAt
:
sub
.
CreatedAt
,
CreatedAt
:
sub
.
CreatedAt
,
UpdatedAt
:
sub
.
UpdatedAt
,
UpdatedAt
:
sub
.
UpdatedAt
,
User
:
UserFromServiceShallow
(
sub
.
User
),
User
:
UserFromServiceShallow
(
sub
.
User
),
Group
:
GroupFromServiceShallow
(
sub
.
Group
),
Group
:
GroupFromServiceShallow
(
sub
.
Group
),
AssignedByUser
:
UserFromServiceShallow
(
sub
.
AssignedByUser
),
}
}
}
}
...
@@ -442,9 +502,9 @@ func BulkAssignResultFromService(r *service.BulkAssignResult) *BulkAssignResult
...
@@ -442,9 +502,9 @@ func BulkAssignResultFromService(r *service.BulkAssignResult) *BulkAssignResult
if
r
==
nil
{
if
r
==
nil
{
return
nil
return
nil
}
}
subs
:=
make
([]
UserSubscription
,
0
,
len
(
r
.
Subscriptions
))
subs
:=
make
([]
Admin
UserSubscription
,
0
,
len
(
r
.
Subscriptions
))
for
i
:=
range
r
.
Subscriptions
{
for
i
:=
range
r
.
Subscriptions
{
subs
=
append
(
subs
,
*
UserSubscriptionFromService
(
&
r
.
Subscriptions
[
i
]))
subs
=
append
(
subs
,
*
UserSubscriptionFromService
Admin
(
&
r
.
Subscriptions
[
i
]))
}
}
return
&
BulkAssignResult
{
return
&
BulkAssignResult
{
SuccessCount
:
r
.
SuccessCount
,
SuccessCount
:
r
.
SuccessCount
,
...
...
backend/internal/handler/dto/types.go
View file @
e7bc6250
...
@@ -6,7 +6,6 @@ type User struct {
...
@@ -6,7 +6,6 @@ type User struct {
ID
int64
`json:"id"`
ID
int64
`json:"id"`
Email
string
`json:"email"`
Email
string
`json:"email"`
Username
string
`json:"username"`
Username
string
`json:"username"`
Notes
string
`json:"notes"`
Role
string
`json:"role"`
Role
string
`json:"role"`
Balance
float64
`json:"balance"`
Balance
float64
`json:"balance"`
Concurrency
int
`json:"concurrency"`
Concurrency
int
`json:"concurrency"`
...
@@ -19,6 +18,14 @@ type User struct {
...
@@ -19,6 +18,14 @@ type User struct {
Subscriptions
[]
UserSubscription
`json:"subscriptions,omitempty"`
Subscriptions
[]
UserSubscription
`json:"subscriptions,omitempty"`
}
}
// AdminUser 是管理员接口使用的 user DTO(包含敏感/内部字段)。
// 注意:普通用户接口不得返回 notes 等管理员备注信息。
type
AdminUser
struct
{
User
Notes
string
`json:"notes"`
}
type
APIKey
struct
{
type
APIKey
struct
{
ID
int64
`json:"id"`
ID
int64
`json:"id"`
UserID
int64
`json:"user_id"`
UserID
int64
`json:"user_id"`
...
@@ -58,13 +65,19 @@ type Group struct {
...
@@ -58,13 +65,19 @@ type Group struct {
ClaudeCodeOnly
bool
`json:"claude_code_only"`
ClaudeCodeOnly
bool
`json:"claude_code_only"`
FallbackGroupID
*
int64
`json:"fallback_group_id"`
FallbackGroupID
*
int64
`json:"fallback_group_id"`
CreatedAt
time
.
Time
`json:"created_at"`
UpdatedAt
time
.
Time
`json:"updated_at"`
}
// AdminGroup 是管理员接口使用的 group DTO(包含敏感/内部字段)。
// 注意:普通用户接口不得返回 model_routing/account_count/account_groups 等内部信息。
type
AdminGroup
struct
{
Group
// 模型路由配置(仅 anthropic 平台使用)
// 模型路由配置(仅 anthropic 平台使用)
ModelRouting
map
[
string
][]
int64
`json:"model_routing"`
ModelRouting
map
[
string
][]
int64
`json:"model_routing"`
ModelRoutingEnabled
bool
`json:"model_routing_enabled"`
ModelRoutingEnabled
bool
`json:"model_routing_enabled"`
CreatedAt
time
.
Time
`json:"created_at"`
UpdatedAt
time
.
Time
`json:"updated_at"`
AccountGroups
[]
AccountGroup
`json:"account_groups,omitempty"`
AccountGroups
[]
AccountGroup
`json:"account_groups,omitempty"`
AccountCount
int64
`json:"account_count,omitempty"`
AccountCount
int64
`json:"account_count,omitempty"`
}
}
...
@@ -180,7 +193,6 @@ type RedeemCode struct {
...
@@ -180,7 +193,6 @@ type RedeemCode struct {
Status
string
`json:"status"`
Status
string
`json:"status"`
UsedBy
*
int64
`json:"used_by"`
UsedBy
*
int64
`json:"used_by"`
UsedAt
*
time
.
Time
`json:"used_at"`
UsedAt
*
time
.
Time
`json:"used_at"`
Notes
string
`json:"notes"`
CreatedAt
time
.
Time
`json:"created_at"`
CreatedAt
time
.
Time
`json:"created_at"`
GroupID
*
int64
`json:"group_id"`
GroupID
*
int64
`json:"group_id"`
...
@@ -190,6 +202,15 @@ type RedeemCode struct {
...
@@ -190,6 +202,15 @@ type RedeemCode struct {
Group
*
Group
`json:"group,omitempty"`
Group
*
Group
`json:"group,omitempty"`
}
}
// AdminRedeemCode 是管理员接口使用的 redeem code DTO(包含 notes 等字段)。
// 注意:普通用户接口不得返回 notes 等内部信息。
type
AdminRedeemCode
struct
{
RedeemCode
Notes
string
`json:"notes"`
}
// UsageLog 是普通用户接口使用的 usage log DTO(不包含管理员字段)。
type
UsageLog
struct
{
type
UsageLog
struct
{
ID
int64
`json:"id"`
ID
int64
`json:"id"`
UserID
int64
`json:"user_id"`
UserID
int64
`json:"user_id"`
...
@@ -216,7 +237,6 @@ type UsageLog struct {
...
@@ -216,7 +237,6 @@ type UsageLog struct {
TotalCost
float64
`json:"total_cost"`
TotalCost
float64
`json:"total_cost"`
ActualCost
float64
`json:"actual_cost"`
ActualCost
float64
`json:"actual_cost"`
RateMultiplier
float64
`json:"rate_multiplier"`
RateMultiplier
float64
`json:"rate_multiplier"`
AccountRateMultiplier
*
float64
`json:"account_rate_multiplier"`
BillingType
int8
`json:"billing_type"`
BillingType
int8
`json:"billing_type"`
Stream
bool
`json:"stream"`
Stream
bool
`json:"stream"`
...
@@ -230,18 +250,28 @@ type UsageLog struct {
...
@@ -230,18 +250,28 @@ type UsageLog struct {
// User-Agent
// User-Agent
UserAgent
*
string
`json:"user_agent"`
UserAgent
*
string
`json:"user_agent"`
// IP 地址(仅管理员可见)
IPAddress
*
string
`json:"ip_address,omitempty"`
CreatedAt
time
.
Time
`json:"created_at"`
CreatedAt
time
.
Time
`json:"created_at"`
User
*
User
`json:"user,omitempty"`
User
*
User
`json:"user,omitempty"`
APIKey
*
APIKey
`json:"api_key,omitempty"`
APIKey
*
APIKey
`json:"api_key,omitempty"`
Account
*
AccountSummary
`json:"account,omitempty"`
// Use minimal AccountSummary to prevent data leakage
Group
*
Group
`json:"group,omitempty"`
Group
*
Group
`json:"group,omitempty"`
Subscription
*
UserSubscription
`json:"subscription,omitempty"`
Subscription
*
UserSubscription
`json:"subscription,omitempty"`
}
}
// AdminUsageLog 是管理员接口使用的 usage log DTO(包含管理员字段)。
type
AdminUsageLog
struct
{
UsageLog
// AccountRateMultiplier 账号计费倍率快照(nil 表示按 1.0 处理)
AccountRateMultiplier
*
float64
`json:"account_rate_multiplier"`
// IPAddress 用户请求 IP(仅管理员可见)
IPAddress
*
string
`json:"ip_address,omitempty"`
// Account 最小账号信息(避免泄露敏感字段)
Account
*
AccountSummary
`json:"account,omitempty"`
}
type
UsageCleanupFilters
struct
{
type
UsageCleanupFilters
struct
{
StartTime
time
.
Time
`json:"start_time"`
StartTime
time
.
Time
`json:"start_time"`
EndTime
time
.
Time
`json:"end_time"`
EndTime
time
.
Time
`json:"end_time"`
...
@@ -300,22 +330,29 @@ type UserSubscription struct {
...
@@ -300,22 +330,29 @@ type UserSubscription struct {
WeeklyUsageUSD
float64
`json:"weekly_usage_usd"`
WeeklyUsageUSD
float64
`json:"weekly_usage_usd"`
MonthlyUsageUSD
float64
`json:"monthly_usage_usd"`
MonthlyUsageUSD
float64
`json:"monthly_usage_usd"`
AssignedBy
*
int64
`json:"assigned_by"`
AssignedAt
time
.
Time
`json:"assigned_at"`
Notes
string
`json:"notes"`
CreatedAt
time
.
Time
`json:"created_at"`
CreatedAt
time
.
Time
`json:"created_at"`
UpdatedAt
time
.
Time
`json:"updated_at"`
UpdatedAt
time
.
Time
`json:"updated_at"`
User
*
User
`json:"user,omitempty"`
User
*
User
`json:"user,omitempty"`
Group
*
Group
`json:"group,omitempty"`
Group
*
Group
`json:"group,omitempty"`
}
// AdminUserSubscription 是管理员接口使用的订阅 DTO(包含分配信息/备注等字段)。
// 注意:普通用户接口不得返回 assigned_by/assigned_at/notes/assigned_by_user 等管理员字段。
type
AdminUserSubscription
struct
{
UserSubscription
AssignedBy
*
int64
`json:"assigned_by"`
AssignedAt
time
.
Time
`json:"assigned_at"`
Notes
string
`json:"notes"`
AssignedByUser
*
User
`json:"assigned_by_user,omitempty"`
AssignedByUser
*
User
`json:"assigned_by_user,omitempty"`
}
}
type
BulkAssignResult
struct
{
type
BulkAssignResult
struct
{
SuccessCount
int
`json:"success_count"`
SuccessCount
int
`json:"success_count"`
FailedCount
int
`json:"failed_count"`
FailedCount
int
`json:"failed_count"`
Subscriptions
[]
UserSubscription
`json:"subscriptions"`
Subscriptions
[]
Admin
UserSubscription
`json:"subscriptions"`
Errors
[]
string
`json:"errors"`
Errors
[]
string
`json:"errors"`
}
}
...
...
backend/internal/handler/user_handler.go
View file @
e7bc6250
...
@@ -47,9 +47,6 @@ func (h *UserHandler) GetProfile(c *gin.Context) {
...
@@ -47,9 +47,6 @@ func (h *UserHandler) GetProfile(c *gin.Context) {
return
return
}
}
// 清空notes字段,普通用户不应看到备注
userData
.
Notes
=
""
response
.
Success
(
c
,
dto
.
UserFromService
(
userData
))
response
.
Success
(
c
,
dto
.
UserFromService
(
userData
))
}
}
...
@@ -105,8 +102,5 @@ func (h *UserHandler) UpdateProfile(c *gin.Context) {
...
@@ -105,8 +102,5 @@ func (h *UserHandler) UpdateProfile(c *gin.Context) {
return
return
}
}
// 清空notes字段,普通用户不应看到备注
updatedUser
.
Notes
=
""
response
.
Success
(
c
,
dto
.
UserFromService
(
updatedUser
))
response
.
Success
(
c
,
dto
.
UserFromService
(
updatedUser
))
}
}
backend/internal/server/api_contract_test.go
View file @
e7bc6250
...
@@ -51,7 +51,6 @@ func TestAPIContracts(t *testing.T) {
...
@@ -51,7 +51,6 @@ func TestAPIContracts(t *testing.T) {
"id": 1,
"id": 1,
"email": "alice@example.com",
"email": "alice@example.com",
"username": "alice",
"username": "alice",
"notes": "hello",
"role": "user",
"role": "user",
"balance": 12.5,
"balance": 12.5,
"concurrency": 5,
"concurrency": 5,
...
@@ -131,6 +130,153 @@ func TestAPIContracts(t *testing.T) {
...
@@ -131,6 +130,153 @@ func TestAPIContracts(t *testing.T) {
}
}
}`
,
}`
,
},
},
{
name
:
"GET /api/v1/groups/available"
,
setup
:
func
(
t
*
testing
.
T
,
deps
*
contractDeps
)
{
t
.
Helper
()
// 普通用户可见的分组列表不应包含内部字段(如 model_routing/account_count)。
deps
.
groupRepo
.
SetActive
([]
service
.
Group
{
{
ID
:
10
,
Name
:
"Group One"
,
Description
:
"desc"
,
Platform
:
service
.
PlatformAnthropic
,
RateMultiplier
:
1.5
,
IsExclusive
:
false
,
Status
:
service
.
StatusActive
,
SubscriptionType
:
service
.
SubscriptionTypeStandard
,
ModelRoutingEnabled
:
true
,
ModelRouting
:
map
[
string
][]
int64
{
"claude-3-*"
:
[]
int64
{
101
,
102
},
},
AccountCount
:
2
,
CreatedAt
:
deps
.
now
,
UpdatedAt
:
deps
.
now
,
},
})
deps
.
userSubRepo
.
SetActiveByUserID
(
1
,
nil
)
},
method
:
http
.
MethodGet
,
path
:
"/api/v1/groups/available"
,
wantStatus
:
http
.
StatusOK
,
wantJSON
:
`{
"code": 0,
"message": "success",
"data": [
{
"id": 10,
"name": "Group One",
"description": "desc",
"platform": "anthropic",
"rate_multiplier": 1.5,
"is_exclusive": false,
"status": "active",
"subscription_type": "standard",
"daily_limit_usd": null,
"weekly_limit_usd": null,
"monthly_limit_usd": null,
"image_price_1k": null,
"image_price_2k": null,
"image_price_4k": null,
"claude_code_only": false,
"fallback_group_id": null,
"created_at": "2025-01-02T03:04:05Z",
"updated_at": "2025-01-02T03:04:05Z"
}
]
}`
,
},
{
name
:
"GET /api/v1/subscriptions"
,
setup
:
func
(
t
*
testing
.
T
,
deps
*
contractDeps
)
{
t
.
Helper
()
// 普通用户订阅接口不应包含 assigned_* / notes 等管理员字段。
deps
.
userSubRepo
.
SetByUserID
(
1
,
[]
service
.
UserSubscription
{
{
ID
:
501
,
UserID
:
1
,
GroupID
:
10
,
StartsAt
:
deps
.
now
,
ExpiresAt
:
deps
.
now
.
Add
(
24
*
time
.
Hour
),
Status
:
service
.
SubscriptionStatusActive
,
DailyUsageUSD
:
1.23
,
WeeklyUsageUSD
:
2.34
,
MonthlyUsageUSD
:
3.45
,
AssignedBy
:
ptr
(
int64
(
999
)),
AssignedAt
:
deps
.
now
,
Notes
:
"admin-note"
,
CreatedAt
:
deps
.
now
,
UpdatedAt
:
deps
.
now
,
},
})
},
method
:
http
.
MethodGet
,
path
:
"/api/v1/subscriptions"
,
wantStatus
:
http
.
StatusOK
,
wantJSON
:
`{
"code": 0,
"message": "success",
"data": [
{
"id": 501,
"user_id": 1,
"group_id": 10,
"starts_at": "2025-01-02T03:04:05Z",
"expires_at": "2025-01-03T03:04:05Z",
"status": "active",
"daily_window_start": null,
"weekly_window_start": null,
"monthly_window_start": null,
"daily_usage_usd": 1.23,
"weekly_usage_usd": 2.34,
"monthly_usage_usd": 3.45,
"created_at": "2025-01-02T03:04:05Z",
"updated_at": "2025-01-02T03:04:05Z"
}
]
}`
,
},
{
name
:
"GET /api/v1/redeem/history"
,
setup
:
func
(
t
*
testing
.
T
,
deps
*
contractDeps
)
{
t
.
Helper
()
// 普通用户兑换历史不应包含 notes 等内部字段。
deps
.
redeemRepo
.
SetByUser
(
1
,
[]
service
.
RedeemCode
{
{
ID
:
900
,
Code
:
"CODE-123"
,
Type
:
service
.
RedeemTypeBalance
,
Value
:
1.25
,
Status
:
service
.
StatusUsed
,
UsedBy
:
ptr
(
int64
(
1
)),
UsedAt
:
ptr
(
deps
.
now
),
Notes
:
"internal-note"
,
CreatedAt
:
deps
.
now
,
},
})
},
method
:
http
.
MethodGet
,
path
:
"/api/v1/redeem/history"
,
wantStatus
:
http
.
StatusOK
,
wantJSON
:
`{
"code": 0,
"message": "success",
"data": [
{
"id": 900,
"code": "CODE-123",
"type": "balance",
"value": 1.25,
"status": "used",
"used_by": 1,
"used_at": "2025-01-02T03:04:05Z",
"created_at": "2025-01-02T03:04:05Z",
"group_id": null,
"validity_days": 0
}
]
}`
,
},
{
{
name
:
"GET /api/v1/usage/stats"
,
name
:
"GET /api/v1/usage/stats"
,
setup
:
func
(
t
*
testing
.
T
,
deps
*
contractDeps
)
{
setup
:
func
(
t
*
testing
.
T
,
deps
*
contractDeps
)
{
...
@@ -194,6 +340,7 @@ func TestAPIContracts(t *testing.T) {
...
@@ -194,6 +340,7 @@ func TestAPIContracts(t *testing.T) {
UserID
:
1
,
UserID
:
1
,
APIKeyID
:
100
,
APIKeyID
:
100
,
AccountID
:
200
,
AccountID
:
200
,
AccountRateMultiplier
:
ptr
(
0.5
),
RequestID
:
"req_123"
,
RequestID
:
"req_123"
,
Model
:
"claude-3"
,
Model
:
"claude-3"
,
InputTokens
:
10
,
InputTokens
:
10
,
...
@@ -241,7 +388,6 @@ func TestAPIContracts(t *testing.T) {
...
@@ -241,7 +388,6 @@ func TestAPIContracts(t *testing.T) {
"total_cost": 0.5,
"total_cost": 0.5,
"actual_cost": 0.5,
"actual_cost": 0.5,
"rate_multiplier": 1,
"rate_multiplier": 1,
"account_rate_multiplier": null,
"billing_type": 0,
"billing_type": 0,
"stream": true,
"stream": true,
"duration_ms": 100,
"duration_ms": 100,
...
@@ -386,8 +532,11 @@ type contractDeps struct {
...
@@ -386,8 +532,11 @@ type contractDeps struct {
now
time
.
Time
now
time
.
Time
router
http
.
Handler
router
http
.
Handler
apiKeyRepo
*
stubApiKeyRepo
apiKeyRepo
*
stubApiKeyRepo
groupRepo
*
stubGroupRepo
userSubRepo
*
stubUserSubscriptionRepo
usageRepo
*
stubUsageLogRepo
usageRepo
*
stubUsageLogRepo
settingRepo
*
stubSettingRepo
settingRepo
*
stubSettingRepo
redeemRepo
*
stubRedeemCodeRepo
}
}
func
newContractDeps
(
t
*
testing
.
T
)
*
contractDeps
{
func
newContractDeps
(
t
*
testing
.
T
)
*
contractDeps
{
...
@@ -415,11 +564,11 @@ func newContractDeps(t *testing.T) *contractDeps {
...
@@ -415,11 +564,11 @@ func newContractDeps(t *testing.T) *contractDeps {
apiKeyRepo
:=
newStubApiKeyRepo
(
now
)
apiKeyRepo
:=
newStubApiKeyRepo
(
now
)
apiKeyCache
:=
stubApiKeyCache
{}
apiKeyCache
:=
stubApiKeyCache
{}
groupRepo
:=
stubGroupRepo
{}
groupRepo
:=
&
stubGroupRepo
{}
userSubRepo
:=
stubUserSubscriptionRepo
{}
userSubRepo
:=
&
stubUserSubscriptionRepo
{}
accountRepo
:=
stubAccountRepo
{}
accountRepo
:=
stubAccountRepo
{}
proxyRepo
:=
stubProxyRepo
{}
proxyRepo
:=
stubProxyRepo
{}
redeemRepo
:=
stubRedeemCodeRepo
{}
redeemRepo
:=
&
stubRedeemCodeRepo
{}
cfg
:=
&
config
.
Config
{
cfg
:=
&
config
.
Config
{
Default
:
config
.
DefaultConfig
{
Default
:
config
.
DefaultConfig
{
...
@@ -434,6 +583,12 @@ func newContractDeps(t *testing.T) *contractDeps {
...
@@ -434,6 +583,12 @@ func newContractDeps(t *testing.T) *contractDeps {
usageRepo
:=
newStubUsageLogRepo
()
usageRepo
:=
newStubUsageLogRepo
()
usageService
:=
service
.
NewUsageService
(
usageRepo
,
userRepo
,
nil
,
nil
)
usageService
:=
service
.
NewUsageService
(
usageRepo
,
userRepo
,
nil
,
nil
)
subscriptionService
:=
service
.
NewSubscriptionService
(
groupRepo
,
userSubRepo
,
nil
)
subscriptionHandler
:=
handler
.
NewSubscriptionHandler
(
subscriptionService
)
redeemService
:=
service
.
NewRedeemService
(
redeemRepo
,
userRepo
,
subscriptionService
,
nil
,
nil
,
nil
,
nil
)
redeemHandler
:=
handler
.
NewRedeemHandler
(
redeemService
)
settingRepo
:=
newStubSettingRepo
()
settingRepo
:=
newStubSettingRepo
()
settingService
:=
service
.
NewSettingService
(
settingRepo
,
cfg
)
settingService
:=
service
.
NewSettingService
(
settingRepo
,
cfg
)
...
@@ -473,12 +628,21 @@ func newContractDeps(t *testing.T) *contractDeps {
...
@@ -473,12 +628,21 @@ func newContractDeps(t *testing.T) *contractDeps {
v1Keys
.
Use
(
jwtAuth
)
v1Keys
.
Use
(
jwtAuth
)
v1Keys
.
GET
(
"/keys"
,
apiKeyHandler
.
List
)
v1Keys
.
GET
(
"/keys"
,
apiKeyHandler
.
List
)
v1Keys
.
POST
(
"/keys"
,
apiKeyHandler
.
Create
)
v1Keys
.
POST
(
"/keys"
,
apiKeyHandler
.
Create
)
v1Keys
.
GET
(
"/groups/available"
,
apiKeyHandler
.
GetAvailableGroups
)
v1Usage
:=
v1
.
Group
(
""
)
v1Usage
:=
v1
.
Group
(
""
)
v1Usage
.
Use
(
jwtAuth
)
v1Usage
.
Use
(
jwtAuth
)
v1Usage
.
GET
(
"/usage"
,
usageHandler
.
List
)
v1Usage
.
GET
(
"/usage"
,
usageHandler
.
List
)
v1Usage
.
GET
(
"/usage/stats"
,
usageHandler
.
Stats
)
v1Usage
.
GET
(
"/usage/stats"
,
usageHandler
.
Stats
)
v1Subs
:=
v1
.
Group
(
""
)
v1Subs
.
Use
(
jwtAuth
)
v1Subs
.
GET
(
"/subscriptions"
,
subscriptionHandler
.
List
)
v1Redeem
:=
v1
.
Group
(
""
)
v1Redeem
.
Use
(
jwtAuth
)
v1Redeem
.
GET
(
"/redeem/history"
,
redeemHandler
.
GetHistory
)
v1Admin
:=
v1
.
Group
(
"/admin"
)
v1Admin
:=
v1
.
Group
(
"/admin"
)
v1Admin
.
Use
(
adminAuth
)
v1Admin
.
Use
(
adminAuth
)
v1Admin
.
GET
(
"/settings"
,
adminSettingHandler
.
GetSettings
)
v1Admin
.
GET
(
"/settings"
,
adminSettingHandler
.
GetSettings
)
...
@@ -488,8 +652,11 @@ func newContractDeps(t *testing.T) *contractDeps {
...
@@ -488,8 +652,11 @@ func newContractDeps(t *testing.T) *contractDeps {
now
:
now
,
now
:
now
,
router
:
r
,
router
:
r
,
apiKeyRepo
:
apiKeyRepo
,
apiKeyRepo
:
apiKeyRepo
,
groupRepo
:
groupRepo
,
userSubRepo
:
userSubRepo
,
usageRepo
:
usageRepo
,
usageRepo
:
usageRepo
,
settingRepo
:
settingRepo
,
settingRepo
:
settingRepo
,
redeemRepo
:
redeemRepo
,
}
}
}
}
...
@@ -627,7 +794,13 @@ func (stubApiKeyCache) SubscribeAuthCacheInvalidation(ctx context.Context, handl
...
@@ -627,7 +794,13 @@ func (stubApiKeyCache) SubscribeAuthCacheInvalidation(ctx context.Context, handl
return
nil
return
nil
}
}
type
stubGroupRepo
struct
{}
type
stubGroupRepo
struct
{
active
[]
service
.
Group
}
func
(
r
*
stubGroupRepo
)
SetActive
(
groups
[]
service
.
Group
)
{
r
.
active
=
append
([]
service
.
Group
(
nil
),
groups
...
)
}
func
(
stubGroupRepo
)
Create
(
ctx
context
.
Context
,
group
*
service
.
Group
)
error
{
func
(
stubGroupRepo
)
Create
(
ctx
context
.
Context
,
group
*
service
.
Group
)
error
{
return
errors
.
New
(
"not implemented"
)
return
errors
.
New
(
"not implemented"
)
...
@@ -661,12 +834,19 @@ func (stubGroupRepo) ListWithFilters(ctx context.Context, params pagination.Pagi
...
@@ -661,12 +834,19 @@ func (stubGroupRepo) ListWithFilters(ctx context.Context, params pagination.Pagi
return
nil
,
nil
,
errors
.
New
(
"not implemented"
)
return
nil
,
nil
,
errors
.
New
(
"not implemented"
)
}
}
func
(
stubGroupRepo
)
ListActive
(
ctx
context
.
Context
)
([]
service
.
Group
,
error
)
{
func
(
r
*
stubGroupRepo
)
ListActive
(
ctx
context
.
Context
)
([]
service
.
Group
,
error
)
{
return
nil
,
errors
.
New
(
"not implemented"
)
return
append
([]
service
.
Group
(
nil
),
r
.
active
...
),
nil
}
}
func
(
stubGroupRepo
)
ListActiveByPlatform
(
ctx
context
.
Context
,
platform
string
)
([]
service
.
Group
,
error
)
{
func
(
r
*
stubGroupRepo
)
ListActiveByPlatform
(
ctx
context
.
Context
,
platform
string
)
([]
service
.
Group
,
error
)
{
return
nil
,
errors
.
New
(
"not implemented"
)
out
:=
make
([]
service
.
Group
,
0
,
len
(
r
.
active
))
for
i
:=
range
r
.
active
{
g
:=
r
.
active
[
i
]
if
g
.
Platform
==
platform
{
out
=
append
(
out
,
g
)
}
}
return
out
,
nil
}
}
func
(
stubGroupRepo
)
ExistsByName
(
ctx
context
.
Context
,
name
string
)
(
bool
,
error
)
{
func
(
stubGroupRepo
)
ExistsByName
(
ctx
context
.
Context
,
name
string
)
(
bool
,
error
)
{
...
@@ -884,7 +1064,16 @@ func (stubProxyRepo) ListAccountSummariesByProxyID(ctx context.Context, proxyID
...
@@ -884,7 +1064,16 @@ func (stubProxyRepo) ListAccountSummariesByProxyID(ctx context.Context, proxyID
return
nil
,
errors
.
New
(
"not implemented"
)
return
nil
,
errors
.
New
(
"not implemented"
)
}
}
type
stubRedeemCodeRepo
struct
{}
type
stubRedeemCodeRepo
struct
{
byUser
map
[
int64
][]
service
.
RedeemCode
}
func
(
r
*
stubRedeemCodeRepo
)
SetByUser
(
userID
int64
,
codes
[]
service
.
RedeemCode
)
{
if
r
.
byUser
==
nil
{
r
.
byUser
=
make
(
map
[
int64
][]
service
.
RedeemCode
)
}
r
.
byUser
[
userID
]
=
append
([]
service
.
RedeemCode
(
nil
),
codes
...
)
}
func
(
stubRedeemCodeRepo
)
Create
(
ctx
context
.
Context
,
code
*
service
.
RedeemCode
)
error
{
func
(
stubRedeemCodeRepo
)
Create
(
ctx
context
.
Context
,
code
*
service
.
RedeemCode
)
error
{
return
errors
.
New
(
"not implemented"
)
return
errors
.
New
(
"not implemented"
)
...
@@ -922,11 +1111,35 @@ func (stubRedeemCodeRepo) ListWithFilters(ctx context.Context, params pagination
...
@@ -922,11 +1111,35 @@ func (stubRedeemCodeRepo) ListWithFilters(ctx context.Context, params pagination
return
nil
,
nil
,
errors
.
New
(
"not implemented"
)
return
nil
,
nil
,
errors
.
New
(
"not implemented"
)
}
}
func
(
stubRedeemCodeRepo
)
ListByUser
(
ctx
context
.
Context
,
userID
int64
,
limit
int
)
([]
service
.
RedeemCode
,
error
)
{
func
(
r
*
stubRedeemCodeRepo
)
ListByUser
(
ctx
context
.
Context
,
userID
int64
,
limit
int
)
([]
service
.
RedeemCode
,
error
)
{
return
nil
,
errors
.
New
(
"not implemented"
)
if
r
.
byUser
==
nil
{
return
nil
,
nil
}
codes
:=
r
.
byUser
[
userID
]
if
limit
>
0
&&
len
(
codes
)
>
limit
{
codes
=
codes
[
:
limit
]
}
return
append
([]
service
.
RedeemCode
(
nil
),
codes
...
),
nil
}
}
type
stubUserSubscriptionRepo
struct
{}
type
stubUserSubscriptionRepo
struct
{
byUser
map
[
int64
][]
service
.
UserSubscription
activeByUser
map
[
int64
][]
service
.
UserSubscription
}
func
(
r
*
stubUserSubscriptionRepo
)
SetByUserID
(
userID
int64
,
subs
[]
service
.
UserSubscription
)
{
if
r
.
byUser
==
nil
{
r
.
byUser
=
make
(
map
[
int64
][]
service
.
UserSubscription
)
}
r
.
byUser
[
userID
]
=
append
([]
service
.
UserSubscription
(
nil
),
subs
...
)
}
func
(
r
*
stubUserSubscriptionRepo
)
SetActiveByUserID
(
userID
int64
,
subs
[]
service
.
UserSubscription
)
{
if
r
.
activeByUser
==
nil
{
r
.
activeByUser
=
make
(
map
[
int64
][]
service
.
UserSubscription
)
}
r
.
activeByUser
[
userID
]
=
append
([]
service
.
UserSubscription
(
nil
),
subs
...
)
}
func
(
stubUserSubscriptionRepo
)
Create
(
ctx
context
.
Context
,
sub
*
service
.
UserSubscription
)
error
{
func
(
stubUserSubscriptionRepo
)
Create
(
ctx
context
.
Context
,
sub
*
service
.
UserSubscription
)
error
{
return
errors
.
New
(
"not implemented"
)
return
errors
.
New
(
"not implemented"
)
...
@@ -946,11 +1159,17 @@ func (stubUserSubscriptionRepo) Update(ctx context.Context, sub *service.UserSub
...
@@ -946,11 +1159,17 @@ func (stubUserSubscriptionRepo) Update(ctx context.Context, sub *service.UserSub
func
(
stubUserSubscriptionRepo
)
Delete
(
ctx
context
.
Context
,
id
int64
)
error
{
func
(
stubUserSubscriptionRepo
)
Delete
(
ctx
context
.
Context
,
id
int64
)
error
{
return
errors
.
New
(
"not implemented"
)
return
errors
.
New
(
"not implemented"
)
}
}
func
(
stubUserSubscriptionRepo
)
ListByUserID
(
ctx
context
.
Context
,
userID
int64
)
([]
service
.
UserSubscription
,
error
)
{
func
(
r
*
stubUserSubscriptionRepo
)
ListByUserID
(
ctx
context
.
Context
,
userID
int64
)
([]
service
.
UserSubscription
,
error
)
{
return
nil
,
errors
.
New
(
"not implemented"
)
if
r
.
byUser
==
nil
{
return
nil
,
nil
}
return
append
([]
service
.
UserSubscription
(
nil
),
r
.
byUser
[
userID
]
...
),
nil
}
}
func
(
stubUserSubscriptionRepo
)
ListActiveByUserID
(
ctx
context
.
Context
,
userID
int64
)
([]
service
.
UserSubscription
,
error
)
{
func
(
r
*
stubUserSubscriptionRepo
)
ListActiveByUserID
(
ctx
context
.
Context
,
userID
int64
)
([]
service
.
UserSubscription
,
error
)
{
return
nil
,
errors
.
New
(
"not implemented"
)
if
r
.
activeByUser
==
nil
{
return
nil
,
nil
}
return
append
([]
service
.
UserSubscription
(
nil
),
r
.
activeByUser
[
userID
]
...
),
nil
}
}
func
(
stubUserSubscriptionRepo
)
ListByGroupID
(
ctx
context
.
Context
,
groupID
int64
,
params
pagination
.
PaginationParams
)
([]
service
.
UserSubscription
,
*
pagination
.
PaginationResult
,
error
)
{
func
(
stubUserSubscriptionRepo
)
ListByGroupID
(
ctx
context
.
Context
,
groupID
int64
,
params
pagination
.
PaginationParams
)
([]
service
.
UserSubscription
,
*
pagination
.
PaginationResult
,
error
)
{
return
nil
,
nil
,
errors
.
New
(
"not implemented"
)
return
nil
,
nil
,
errors
.
New
(
"not implemented"
)
...
...
frontend/src/api/admin/groups.ts
View file @
e7bc6250
...
@@ -5,7 +5,7 @@
...
@@ -5,7 +5,7 @@
import
{
apiClient
}
from
'
../client
'
import
{
apiClient
}
from
'
../client
'
import
type
{
import
type
{
Group
,
Admin
Group
,
GroupPlatform
,
GroupPlatform
,
CreateGroupRequest
,
CreateGroupRequest
,
UpdateGroupRequest
,
UpdateGroupRequest
,
...
@@ -31,8 +31,8 @@ export async function list(
...
@@ -31,8 +31,8 @@ export async function list(
options
?:
{
options
?:
{
signal
?:
AbortSignal
signal
?:
AbortSignal
}
}
):
Promise
<
PaginatedResponse
<
Group
>>
{
):
Promise
<
PaginatedResponse
<
Admin
Group
>>
{
const
{
data
}
=
await
apiClient
.
get
<
PaginatedResponse
<
Group
>>
(
'
/admin/groups
'
,
{
const
{
data
}
=
await
apiClient
.
get
<
PaginatedResponse
<
Admin
Group
>>
(
'
/admin/groups
'
,
{
params
:
{
params
:
{
page
,
page
,
page_size
:
pageSize
,
page_size
:
pageSize
,
...
@@ -48,8 +48,8 @@ export async function list(
...
@@ -48,8 +48,8 @@ export async function list(
* @param platform - Optional platform filter
* @param platform - Optional platform filter
* @returns List of all active groups
* @returns List of all active groups
*/
*/
export
async
function
getAll
(
platform
?:
GroupPlatform
):
Promise
<
Group
[]
>
{
export
async
function
getAll
(
platform
?:
GroupPlatform
):
Promise
<
Admin
Group
[]
>
{
const
{
data
}
=
await
apiClient
.
get
<
Group
[]
>
(
'
/admin/groups/all
'
,
{
const
{
data
}
=
await
apiClient
.
get
<
Admin
Group
[]
>
(
'
/admin/groups/all
'
,
{
params
:
platform
?
{
platform
}
:
undefined
params
:
platform
?
{
platform
}
:
undefined
})
})
return
data
return
data
...
@@ -60,7 +60,7 @@ export async function getAll(platform?: GroupPlatform): Promise<Group[]> {
...
@@ -60,7 +60,7 @@ export async function getAll(platform?: GroupPlatform): Promise<Group[]> {
* @param platform - Platform to filter by
* @param platform - Platform to filter by
* @returns List of groups for the specified platform
* @returns List of groups for the specified platform
*/
*/
export
async
function
getByPlatform
(
platform
:
GroupPlatform
):
Promise
<
Group
[]
>
{
export
async
function
getByPlatform
(
platform
:
GroupPlatform
):
Promise
<
Admin
Group
[]
>
{
return
getAll
(
platform
)
return
getAll
(
platform
)
}
}
...
@@ -69,8 +69,8 @@ export async function getByPlatform(platform: GroupPlatform): Promise<Group[]> {
...
@@ -69,8 +69,8 @@ export async function getByPlatform(platform: GroupPlatform): Promise<Group[]> {
* @param id - Group ID
* @param id - Group ID
* @returns Group details
* @returns Group details
*/
*/
export
async
function
getById
(
id
:
number
):
Promise
<
Group
>
{
export
async
function
getById
(
id
:
number
):
Promise
<
Admin
Group
>
{
const
{
data
}
=
await
apiClient
.
get
<
Group
>
(
`/admin/groups/
${
id
}
`
)
const
{
data
}
=
await
apiClient
.
get
<
Admin
Group
>
(
`/admin/groups/
${
id
}
`
)
return
data
return
data
}
}
...
@@ -79,8 +79,8 @@ export async function getById(id: number): Promise<Group> {
...
@@ -79,8 +79,8 @@ export async function getById(id: number): Promise<Group> {
* @param groupData - Group data
* @param groupData - Group data
* @returns Created group
* @returns Created group
*/
*/
export
async
function
create
(
groupData
:
CreateGroupRequest
):
Promise
<
Group
>
{
export
async
function
create
(
groupData
:
CreateGroupRequest
):
Promise
<
Admin
Group
>
{
const
{
data
}
=
await
apiClient
.
post
<
Group
>
(
'
/admin/groups
'
,
groupData
)
const
{
data
}
=
await
apiClient
.
post
<
Admin
Group
>
(
'
/admin/groups
'
,
groupData
)
return
data
return
data
}
}
...
@@ -90,8 +90,8 @@ export async function create(groupData: CreateGroupRequest): Promise<Group> {
...
@@ -90,8 +90,8 @@ export async function create(groupData: CreateGroupRequest): Promise<Group> {
* @param updates - Fields to update
* @param updates - Fields to update
* @returns Updated group
* @returns Updated group
*/
*/
export
async
function
update
(
id
:
number
,
updates
:
UpdateGroupRequest
):
Promise
<
Group
>
{
export
async
function
update
(
id
:
number
,
updates
:
UpdateGroupRequest
):
Promise
<
Admin
Group
>
{
const
{
data
}
=
await
apiClient
.
put
<
Group
>
(
`/admin/groups/
${
id
}
`
,
updates
)
const
{
data
}
=
await
apiClient
.
put
<
Admin
Group
>
(
`/admin/groups/
${
id
}
`
,
updates
)
return
data
return
data
}
}
...
@@ -111,7 +111,7 @@ export async function deleteGroup(id: number): Promise<{ message: string }> {
...
@@ -111,7 +111,7 @@ export async function deleteGroup(id: number): Promise<{ message: string }> {
* @param status - New status
* @param status - New status
* @returns Updated group
* @returns Updated group
*/
*/
export
async
function
toggleStatus
(
id
:
number
,
status
:
'
active
'
|
'
inactive
'
):
Promise
<
Group
>
{
export
async
function
toggleStatus
(
id
:
number
,
status
:
'
active
'
|
'
inactive
'
):
Promise
<
Admin
Group
>
{
return
update
(
id
,
{
status
})
return
update
(
id
,
{
status
})
}
}
...
...
frontend/src/api/admin/usage.ts
View file @
e7bc6250
...
@@ -4,7 +4,7 @@
...
@@ -4,7 +4,7 @@
*/
*/
import
{
apiClient
}
from
'
../client
'
import
{
apiClient
}
from
'
../client
'
import
type
{
UsageLog
,
UsageQueryParams
,
PaginatedResponse
}
from
'
@/types
'
import
type
{
Admin
UsageLog
,
UsageQueryParams
,
PaginatedResponse
}
from
'
@/types
'
// ==================== Types ====================
// ==================== Types ====================
...
@@ -85,8 +85,8 @@ export interface AdminUsageQueryParams extends UsageQueryParams {
...
@@ -85,8 +85,8 @@ export interface AdminUsageQueryParams extends UsageQueryParams {
export
async
function
list
(
export
async
function
list
(
params
:
AdminUsageQueryParams
,
params
:
AdminUsageQueryParams
,
options
?:
{
signal
?:
AbortSignal
}
options
?:
{
signal
?:
AbortSignal
}
):
Promise
<
PaginatedResponse
<
UsageLog
>>
{
):
Promise
<
PaginatedResponse
<
Admin
UsageLog
>>
{
const
{
data
}
=
await
apiClient
.
get
<
PaginatedResponse
<
UsageLog
>>
(
'
/admin/usage
'
,
{
const
{
data
}
=
await
apiClient
.
get
<
PaginatedResponse
<
Admin
UsageLog
>>
(
'
/admin/usage
'
,
{
params
,
params
,
signal
:
options
?.
signal
signal
:
options
?.
signal
})
})
...
...
frontend/src/api/admin/users.ts
View file @
e7bc6250
...
@@ -4,7 +4,7 @@
...
@@ -4,7 +4,7 @@
*/
*/
import
{
apiClient
}
from
'
../client
'
import
{
apiClient
}
from
'
../client
'
import
type
{
User
,
UpdateUserRequest
,
PaginatedResponse
}
from
'
@/types
'
import
type
{
Admin
User
,
UpdateUserRequest
,
PaginatedResponse
}
from
'
@/types
'
/**
/**
* List all users with pagination
* List all users with pagination
...
@@ -26,7 +26,7 @@ export async function list(
...
@@ -26,7 +26,7 @@ export async function list(
options
?:
{
options
?:
{
signal
?:
AbortSignal
signal
?:
AbortSignal
}
}
):
Promise
<
PaginatedResponse
<
User
>>
{
):
Promise
<
PaginatedResponse
<
Admin
User
>>
{
// Build params with attribute filters in attr[id]=value format
// Build params with attribute filters in attr[id]=value format
const
params
:
Record
<
string
,
any
>
=
{
const
params
:
Record
<
string
,
any
>
=
{
page
,
page
,
...
@@ -44,8 +44,7 @@ export async function list(
...
@@ -44,8 +44,7 @@ export async function list(
}
}
}
}
}
}
const
{
data
}
=
await
apiClient
.
get
<
PaginatedResponse
<
AdminUser
>>
(
'
/admin/users
'
,
{
const
{
data
}
=
await
apiClient
.
get
<
PaginatedResponse
<
User
>>
(
'
/admin/users
'
,
{
params
,
params
,
signal
:
options
?.
signal
signal
:
options
?.
signal
})
})
...
@@ -57,8 +56,8 @@ export async function list(
...
@@ -57,8 +56,8 @@ export async function list(
* @param id - User ID
* @param id - User ID
* @returns User details
* @returns User details
*/
*/
export
async
function
getById
(
id
:
number
):
Promise
<
User
>
{
export
async
function
getById
(
id
:
number
):
Promise
<
Admin
User
>
{
const
{
data
}
=
await
apiClient
.
get
<
User
>
(
`/admin/users/
${
id
}
`
)
const
{
data
}
=
await
apiClient
.
get
<
Admin
User
>
(
`/admin/users/
${
id
}
`
)
return
data
return
data
}
}
...
@@ -73,8 +72,8 @@ export async function create(userData: {
...
@@ -73,8 +72,8 @@ export async function create(userData: {
balance
?:
number
balance
?:
number
concurrency
?:
number
concurrency
?:
number
allowed_groups
?:
number
[]
|
null
allowed_groups
?:
number
[]
|
null
}):
Promise
<
User
>
{
}):
Promise
<
Admin
User
>
{
const
{
data
}
=
await
apiClient
.
post
<
User
>
(
'
/admin/users
'
,
userData
)
const
{
data
}
=
await
apiClient
.
post
<
Admin
User
>
(
'
/admin/users
'
,
userData
)
return
data
return
data
}
}
...
@@ -84,8 +83,8 @@ export async function create(userData: {
...
@@ -84,8 +83,8 @@ export async function create(userData: {
* @param updates - Fields to update
* @param updates - Fields to update
* @returns Updated user
* @returns Updated user
*/
*/
export
async
function
update
(
id
:
number
,
updates
:
UpdateUserRequest
):
Promise
<
User
>
{
export
async
function
update
(
id
:
number
,
updates
:
UpdateUserRequest
):
Promise
<
Admin
User
>
{
const
{
data
}
=
await
apiClient
.
put
<
User
>
(
`/admin/users/
${
id
}
`
,
updates
)
const
{
data
}
=
await
apiClient
.
put
<
Admin
User
>
(
`/admin/users/
${
id
}
`
,
updates
)
return
data
return
data
}
}
...
@@ -112,8 +111,8 @@ export async function updateBalance(
...
@@ -112,8 +111,8 @@ export async function updateBalance(
balance
:
number
,
balance
:
number
,
operation
:
'
set
'
|
'
add
'
|
'
subtract
'
=
'
set
'
,
operation
:
'
set
'
|
'
add
'
|
'
subtract
'
=
'
set
'
,
notes
?:
string
notes
?:
string
):
Promise
<
User
>
{
):
Promise
<
Admin
User
>
{
const
{
data
}
=
await
apiClient
.
post
<
User
>
(
`/admin/users/
${
id
}
/balance`
,
{
const
{
data
}
=
await
apiClient
.
post
<
Admin
User
>
(
`/admin/users/
${
id
}
/balance`
,
{
balance
,
balance
,
operation
,
operation
,
notes
:
notes
||
''
notes
:
notes
||
''
...
@@ -127,7 +126,7 @@ export async function updateBalance(
...
@@ -127,7 +126,7 @@ export async function updateBalance(
* @param concurrency - New concurrency limit
* @param concurrency - New concurrency limit
* @returns Updated user
* @returns Updated user
*/
*/
export
async
function
updateConcurrency
(
id
:
number
,
concurrency
:
number
):
Promise
<
User
>
{
export
async
function
updateConcurrency
(
id
:
number
,
concurrency
:
number
):
Promise
<
Admin
User
>
{
return
update
(
id
,
{
concurrency
})
return
update
(
id
,
{
concurrency
})
}
}
...
@@ -137,7 +136,7 @@ export async function updateConcurrency(id: number, concurrency: number): Promis
...
@@ -137,7 +136,7 @@ export async function updateConcurrency(id: number, concurrency: number): Promis
* @param status - New status
* @param status - New status
* @returns Updated user
* @returns Updated user
*/
*/
export
async
function
toggleStatus
(
id
:
number
,
status
:
'
active
'
|
'
disabled
'
):
Promise
<
User
>
{
export
async
function
toggleStatus
(
id
:
number
,
status
:
'
active
'
|
'
disabled
'
):
Promise
<
Admin
User
>
{
return
update
(
id
,
{
status
})
return
update
(
id
,
{
status
})
}
}
...
...
frontend/src/components/account/BulkEditAccountModal.vue
View file @
e7bc6250
...
@@ -648,7 +648,7 @@ import { ref, watch, computed } from 'vue'
...
@@ -648,7 +648,7 @@ import { ref, watch, computed } from 'vue'
import
{
useI18n
}
from
'
vue-i18n
'
import
{
useI18n
}
from
'
vue-i18n
'
import
{
useAppStore
}
from
'
@/stores/app
'
import
{
useAppStore
}
from
'
@/stores/app
'
import
{
adminAPI
}
from
'
@/api/admin
'
import
{
adminAPI
}
from
'
@/api/admin
'
import
type
{
Proxy
,
Group
}
from
'
@/types
'
import
type
{
Proxy
,
Admin
Group
}
from
'
@/types
'
import
BaseDialog
from
'
@/components/common/BaseDialog.vue
'
import
BaseDialog
from
'
@/components/common/BaseDialog.vue
'
import
Select
from
'
@/components/common/Select.vue
'
import
Select
from
'
@/components/common/Select.vue
'
import
ProxySelector
from
'
@/components/common/ProxySelector.vue
'
import
ProxySelector
from
'
@/components/common/ProxySelector.vue
'
...
@@ -659,7 +659,7 @@ interface Props {
...
@@ -659,7 +659,7 @@ interface Props {
show
:
boolean
show
:
boolean
accountIds
:
number
[]
accountIds
:
number
[]
proxies
:
Proxy
[]
proxies
:
Proxy
[]
groups
:
Group
[]
groups
:
Admin
Group
[]
}
}
const
props
=
defineProps
<
Props
>
()
const
props
=
defineProps
<
Props
>
()
...
...
frontend/src/components/account/CreateAccountModal.vue
View file @
e7bc6250
...
@@ -1816,7 +1816,7 @@ import {
...
@@ -1816,7 +1816,7 @@ import {
import
{
useOpenAIOAuth
}
from
'
@/composables/useOpenAIOAuth
'
import
{
useOpenAIOAuth
}
from
'
@/composables/useOpenAIOAuth
'
import
{
useGeminiOAuth
}
from
'
@/composables/useGeminiOAuth
'
import
{
useGeminiOAuth
}
from
'
@/composables/useGeminiOAuth
'
import
{
useAntigravityOAuth
}
from
'
@/composables/useAntigravityOAuth
'
import
{
useAntigravityOAuth
}
from
'
@/composables/useAntigravityOAuth
'
import
type
{
Proxy
,
Group
,
AccountPlatform
,
AccountType
}
from
'
@/types
'
import
type
{
Proxy
,
Admin
Group
,
AccountPlatform
,
AccountType
}
from
'
@/types
'
import
BaseDialog
from
'
@/components/common/BaseDialog.vue
'
import
BaseDialog
from
'
@/components/common/BaseDialog.vue
'
import
Icon
from
'
@/components/icons/Icon.vue
'
import
Icon
from
'
@/components/icons/Icon.vue
'
import
ProxySelector
from
'
@/components/common/ProxySelector.vue
'
import
ProxySelector
from
'
@/components/common/ProxySelector.vue
'
...
@@ -1862,7 +1862,7 @@ const apiKeyHint = computed(() => {
...
@@ -1862,7 +1862,7 @@ const apiKeyHint = computed(() => {
interface
Props
{
interface
Props
{
show
:
boolean
show
:
boolean
proxies
:
Proxy
[]
proxies
:
Proxy
[]
groups
:
Group
[]
groups
:
Admin
Group
[]
}
}
const
props
=
defineProps
<
Props
>
()
const
props
=
defineProps
<
Props
>
()
...
...
frontend/src/components/account/EditAccountModal.vue
View file @
e7bc6250
...
@@ -883,7 +883,7 @@ import { useI18n } from 'vue-i18n'
...
@@ -883,7 +883,7 @@ import { useI18n } from 'vue-i18n'
import
{
useAppStore
}
from
'
@/stores/app
'
import
{
useAppStore
}
from
'
@/stores/app
'
import
{
useAuthStore
}
from
'
@/stores/auth
'
import
{
useAuthStore
}
from
'
@/stores/auth
'
import
{
adminAPI
}
from
'
@/api/admin
'
import
{
adminAPI
}
from
'
@/api/admin
'
import
type
{
Account
,
Proxy
,
Group
}
from
'
@/types
'
import
type
{
Account
,
Proxy
,
Admin
Group
}
from
'
@/types
'
import
BaseDialog
from
'
@/components/common/BaseDialog.vue
'
import
BaseDialog
from
'
@/components/common/BaseDialog.vue
'
import
Select
from
'
@/components/common/Select.vue
'
import
Select
from
'
@/components/common/Select.vue
'
import
Icon
from
'
@/components/icons/Icon.vue
'
import
Icon
from
'
@/components/icons/Icon.vue
'
...
@@ -901,7 +901,7 @@ interface Props {
...
@@ -901,7 +901,7 @@ interface Props {
show
:
boolean
show
:
boolean
account
:
Account
|
null
account
:
Account
|
null
proxies
:
Proxy
[]
proxies
:
Proxy
[]
groups
:
Group
[]
groups
:
Admin
Group
[]
}
}
const
props
=
defineProps
<
Props
>
()
const
props
=
defineProps
<
Props
>
()
...
...
frontend/src/components/admin/usage/UsageTable.vue
View file @
e7bc6250
...
@@ -239,7 +239,7 @@ import { formatDateTime } from '@/utils/format'
...
@@ -239,7 +239,7 @@ import { formatDateTime } from '@/utils/format'
import
DataTable
from
'
@/components/common/DataTable.vue
'
import
DataTable
from
'
@/components/common/DataTable.vue
'
import
EmptyState
from
'
@/components/common/EmptyState.vue
'
import
EmptyState
from
'
@/components/common/EmptyState.vue
'
import
Icon
from
'
@/components/icons/Icon.vue
'
import
Icon
from
'
@/components/icons/Icon.vue
'
import
type
{
UsageLog
}
from
'
@/types
'
import
type
{
Admin
UsageLog
}
from
'
@/types
'
defineProps
([
'
data
'
,
'
loading
'
])
defineProps
([
'
data
'
,
'
loading
'
])
const
{
t
}
=
useI18n
()
const
{
t
}
=
useI18n
()
...
@@ -247,12 +247,12 @@ const { t } = useI18n()
...
@@ -247,12 +247,12 @@ const { t } = useI18n()
// Tooltip state - cost
// Tooltip state - cost
const
tooltipVisible
=
ref
(
false
)
const
tooltipVisible
=
ref
(
false
)
const
tooltipPosition
=
ref
({
x
:
0
,
y
:
0
})
const
tooltipPosition
=
ref
({
x
:
0
,
y
:
0
})
const
tooltipData
=
ref
<
UsageLog
|
null
>
(
null
)
const
tooltipData
=
ref
<
Admin
UsageLog
|
null
>
(
null
)
// Tooltip state - token
// Tooltip state - token
const
tokenTooltipVisible
=
ref
(
false
)
const
tokenTooltipVisible
=
ref
(
false
)
const
tokenTooltipPosition
=
ref
({
x
:
0
,
y
:
0
})
const
tokenTooltipPosition
=
ref
({
x
:
0
,
y
:
0
})
const
tokenTooltipData
=
ref
<
UsageLog
|
null
>
(
null
)
const
tokenTooltipData
=
ref
<
Admin
UsageLog
|
null
>
(
null
)
const
cols
=
computed
(()
=>
[
const
cols
=
computed
(()
=>
[
{
key
:
'
user
'
,
label
:
t
(
'
admin.usage.user
'
),
sortable
:
false
},
{
key
:
'
user
'
,
label
:
t
(
'
admin.usage.user
'
),
sortable
:
false
},
...
@@ -296,7 +296,7 @@ const formatDuration = (ms: number | null | undefined): string => {
...
@@ -296,7 +296,7 @@ const formatDuration = (ms: number | null | undefined): string => {
}
}
// Cost tooltip functions
// Cost tooltip functions
const
showTooltip
=
(
event
:
MouseEvent
,
row
:
UsageLog
)
=>
{
const
showTooltip
=
(
event
:
MouseEvent
,
row
:
Admin
UsageLog
)
=>
{
const
target
=
event
.
currentTarget
as
HTMLElement
const
target
=
event
.
currentTarget
as
HTMLElement
const
rect
=
target
.
getBoundingClientRect
()
const
rect
=
target
.
getBoundingClientRect
()
tooltipData
.
value
=
row
tooltipData
.
value
=
row
...
@@ -311,7 +311,7 @@ const hideTooltip = () => {
...
@@ -311,7 +311,7 @@ const hideTooltip = () => {
}
}
// Token tooltip functions
// Token tooltip functions
const
showTokenTooltip
=
(
event
:
MouseEvent
,
row
:
UsageLog
)
=>
{
const
showTokenTooltip
=
(
event
:
MouseEvent
,
row
:
Admin
UsageLog
)
=>
{
const
target
=
event
.
currentTarget
as
HTMLElement
const
target
=
event
.
currentTarget
as
HTMLElement
const
rect
=
target
.
getBoundingClientRect
()
const
rect
=
target
.
getBoundingClientRect
()
tokenTooltipData
.
value
=
row
tokenTooltipData
.
value
=
row
...
...
frontend/src/components/admin/user/UserAllowedGroupsModal.vue
View file @
e7bc6250
...
@@ -39,10 +39,10 @@ import { ref, watch } from 'vue'
...
@@ -39,10 +39,10 @@ import { ref, watch } from 'vue'
import
{
useI18n
}
from
'
vue-i18n
'
import
{
useI18n
}
from
'
vue-i18n
'
import
{
useAppStore
}
from
'
@/stores/app
'
import
{
useAppStore
}
from
'
@/stores/app
'
import
{
adminAPI
}
from
'
@/api/admin
'
import
{
adminAPI
}
from
'
@/api/admin
'
import
type
{
User
,
Group
}
from
'
@/types
'
import
type
{
Admin
User
,
Group
}
from
'
@/types
'
import
BaseDialog
from
'
@/components/common/BaseDialog.vue
'
import
BaseDialog
from
'
@/components/common/BaseDialog.vue
'
const
props
=
defineProps
<
{
show
:
boolean
,
user
:
User
|
null
}
>
()
const
props
=
defineProps
<
{
show
:
boolean
,
user
:
Admin
User
|
null
}
>
()
const
emit
=
defineEmits
([
'
close
'
,
'
success
'
]);
const
{
t
}
=
useI18n
();
const
appStore
=
useAppStore
()
const
emit
=
defineEmits
([
'
close
'
,
'
success
'
]);
const
{
t
}
=
useI18n
();
const
appStore
=
useAppStore
()
const
groups
=
ref
<
Group
[]
>
([]);
const
selectedIds
=
ref
<
number
[]
>
([]);
const
loading
=
ref
(
false
);
const
submitting
=
ref
(
false
)
const
groups
=
ref
<
Group
[]
>
([]);
const
selectedIds
=
ref
<
number
[]
>
([]);
const
loading
=
ref
(
false
);
const
submitting
=
ref
(
false
)
...
...
frontend/src/components/admin/user/UserApiKeysModal.vue
View file @
e7bc6250
...
@@ -32,10 +32,10 @@ import { ref, watch } from 'vue'
...
@@ -32,10 +32,10 @@ import { ref, watch } from 'vue'
import
{
useI18n
}
from
'
vue-i18n
'
import
{
useI18n
}
from
'
vue-i18n
'
import
{
adminAPI
}
from
'
@/api/admin
'
import
{
adminAPI
}
from
'
@/api/admin
'
import
{
formatDateTime
}
from
'
@/utils/format
'
import
{
formatDateTime
}
from
'
@/utils/format
'
import
type
{
User
,
ApiKey
}
from
'
@/types
'
import
type
{
Admin
User
,
ApiKey
}
from
'
@/types
'
import
BaseDialog
from
'
@/components/common/BaseDialog.vue
'
import
BaseDialog
from
'
@/components/common/BaseDialog.vue
'
const
props
=
defineProps
<
{
show
:
boolean
,
user
:
User
|
null
}
>
()
const
props
=
defineProps
<
{
show
:
boolean
,
user
:
Admin
User
|
null
}
>
()
defineEmits
([
'
close
'
]);
const
{
t
}
=
useI18n
()
defineEmits
([
'
close
'
]);
const
{
t
}
=
useI18n
()
const
apiKeys
=
ref
<
ApiKey
[]
>
([]);
const
loading
=
ref
(
false
)
const
apiKeys
=
ref
<
ApiKey
[]
>
([]);
const
loading
=
ref
(
false
)
...
...
frontend/src/components/admin/user/UserBalanceModal.vue
View file @
e7bc6250
...
@@ -29,10 +29,10 @@ import { reactive, ref, watch } from 'vue'
...
@@ -29,10 +29,10 @@ import { reactive, ref, watch } from 'vue'
import
{
useI18n
}
from
'
vue-i18n
'
import
{
useI18n
}
from
'
vue-i18n
'
import
{
useAppStore
}
from
'
@/stores/app
'
import
{
useAppStore
}
from
'
@/stores/app
'
import
{
adminAPI
}
from
'
@/api/admin
'
import
{
adminAPI
}
from
'
@/api/admin
'
import
type
{
User
}
from
'
@/types
'
import
type
{
Admin
User
}
from
'
@/types
'
import
BaseDialog
from
'
@/components/common/BaseDialog.vue
'
import
BaseDialog
from
'
@/components/common/BaseDialog.vue
'
const
props
=
defineProps
<
{
show
:
boolean
,
user
:
User
|
null
,
operation
:
'
add
'
|
'
subtract
'
}
>
()
const
props
=
defineProps
<
{
show
:
boolean
,
user
:
Admin
User
|
null
,
operation
:
'
add
'
|
'
subtract
'
}
>
()
const
emit
=
defineEmits
([
'
close
'
,
'
success
'
]);
const
{
t
}
=
useI18n
();
const
appStore
=
useAppStore
()
const
emit
=
defineEmits
([
'
close
'
,
'
success
'
]);
const
{
t
}
=
useI18n
();
const
appStore
=
useAppStore
()
const
submitting
=
ref
(
false
);
const
form
=
reactive
({
amount
:
0
,
notes
:
''
})
const
submitting
=
ref
(
false
);
const
form
=
reactive
({
amount
:
0
,
notes
:
''
})
...
...
frontend/src/components/admin/user/UserEditModal.vue
View file @
e7bc6250
...
@@ -56,12 +56,12 @@ import { useI18n } from 'vue-i18n'
...
@@ -56,12 +56,12 @@ import { useI18n } from 'vue-i18n'
import
{
useAppStore
}
from
'
@/stores/app
'
import
{
useAppStore
}
from
'
@/stores/app
'
import
{
useClipboard
}
from
'
@/composables/useClipboard
'
import
{
useClipboard
}
from
'
@/composables/useClipboard
'
import
{
adminAPI
}
from
'
@/api/admin
'
import
{
adminAPI
}
from
'
@/api/admin
'
import
type
{
User
,
UserAttributeValuesMap
}
from
'
@/types
'
import
type
{
Admin
User
,
UserAttributeValuesMap
}
from
'
@/types
'
import
BaseDialog
from
'
@/components/common/BaseDialog.vue
'
import
BaseDialog
from
'
@/components/common/BaseDialog.vue
'
import
UserAttributeForm
from
'
@/components/user/UserAttributeForm.vue
'
import
UserAttributeForm
from
'
@/components/user/UserAttributeForm.vue
'
import
Icon
from
'
@/components/icons/Icon.vue
'
import
Icon
from
'
@/components/icons/Icon.vue
'
const
props
=
defineProps
<
{
show
:
boolean
,
user
:
User
|
null
}
>
()
const
props
=
defineProps
<
{
show
:
boolean
,
user
:
Admin
User
|
null
}
>
()
const
emit
=
defineEmits
([
'
close
'
,
'
success
'
])
const
emit
=
defineEmits
([
'
close
'
,
'
success
'
])
const
{
t
}
=
useI18n
();
const
appStore
=
useAppStore
();
const
{
copyToClipboard
}
=
useClipboard
()
const
{
t
}
=
useI18n
();
const
appStore
=
useAppStore
();
const
{
copyToClipboard
}
=
useClipboard
()
...
...
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