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
c86d445c
Commit
c86d445c
authored
Jan 04, 2026
by
IanShaw027
Browse files
fix(frontend): sync with main and finalize i18n & component optimizations
parents
6c036d7b
e78c8646
Changes
186
Hide whitespace changes
Inline
Side-by-side
backend/ent/user_create.go
View file @
c86d445c
...
@@ -166,14 +166,14 @@ func (_c *UserCreate) SetNillableNotes(v *string) *UserCreate {
...
@@ -166,14 +166,14 @@ func (_c *UserCreate) SetNillableNotes(v *string) *UserCreate {
return
_c
return
_c
}
}
// AddAPIKeyIDs adds the "api_keys" edge to the A
pi
Key entity by IDs.
// AddAPIKeyIDs adds the "api_keys" edge to the A
PI
Key entity by IDs.
func
(
_c
*
UserCreate
)
AddAPIKeyIDs
(
ids
...
int64
)
*
UserCreate
{
func
(
_c
*
UserCreate
)
AddAPIKeyIDs
(
ids
...
int64
)
*
UserCreate
{
_c
.
mutation
.
AddAPIKeyIDs
(
ids
...
)
_c
.
mutation
.
AddAPIKeyIDs
(
ids
...
)
return
_c
return
_c
}
}
// AddAPIKeys adds the "api_keys" edges to the A
pi
Key entity.
// AddAPIKeys adds the "api_keys" edges to the A
PI
Key entity.
func
(
_c
*
UserCreate
)
AddAPIKeys
(
v
...*
A
pi
Key
)
*
UserCreate
{
func
(
_c
*
UserCreate
)
AddAPIKeys
(
v
...*
A
PI
Key
)
*
UserCreate
{
ids
:=
make
([]
int64
,
len
(
v
))
ids
:=
make
([]
int64
,
len
(
v
))
for
i
:=
range
v
{
for
i
:=
range
v
{
ids
[
i
]
=
v
[
i
]
.
ID
ids
[
i
]
=
v
[
i
]
.
ID
...
...
backend/ent/user_query.go
View file @
c86d445c
...
@@ -30,7 +30,7 @@ type UserQuery struct {
...
@@ -30,7 +30,7 @@ type UserQuery struct {
order
[]
user
.
OrderOption
order
[]
user
.
OrderOption
inters
[]
Interceptor
inters
[]
Interceptor
predicates
[]
predicate
.
User
predicates
[]
predicate
.
User
withAPIKeys
*
A
pi
KeyQuery
withAPIKeys
*
A
PI
KeyQuery
withRedeemCodes
*
RedeemCodeQuery
withRedeemCodes
*
RedeemCodeQuery
withSubscriptions
*
UserSubscriptionQuery
withSubscriptions
*
UserSubscriptionQuery
withAssignedSubscriptions
*
UserSubscriptionQuery
withAssignedSubscriptions
*
UserSubscriptionQuery
...
@@ -75,8 +75,8 @@ func (_q *UserQuery) Order(o ...user.OrderOption) *UserQuery {
...
@@ -75,8 +75,8 @@ func (_q *UserQuery) Order(o ...user.OrderOption) *UserQuery {
}
}
// QueryAPIKeys chains the current query on the "api_keys" edge.
// QueryAPIKeys chains the current query on the "api_keys" edge.
func
(
_q
*
UserQuery
)
QueryAPIKeys
()
*
A
pi
KeyQuery
{
func
(
_q
*
UserQuery
)
QueryAPIKeys
()
*
A
PI
KeyQuery
{
query
:=
(
&
A
pi
KeyClient
{
config
:
_q
.
config
})
.
Query
()
query
:=
(
&
A
PI
KeyClient
{
config
:
_q
.
config
})
.
Query
()
query
.
path
=
func
(
ctx
context
.
Context
)
(
fromU
*
sql
.
Selector
,
err
error
)
{
query
.
path
=
func
(
ctx
context
.
Context
)
(
fromU
*
sql
.
Selector
,
err
error
)
{
if
err
:=
_q
.
prepareQuery
(
ctx
);
err
!=
nil
{
if
err
:=
_q
.
prepareQuery
(
ctx
);
err
!=
nil
{
return
nil
,
err
return
nil
,
err
...
@@ -458,8 +458,8 @@ func (_q *UserQuery) Clone() *UserQuery {
...
@@ -458,8 +458,8 @@ func (_q *UserQuery) Clone() *UserQuery {
// WithAPIKeys tells the query-builder to eager-load the nodes that are connected to
// WithAPIKeys tells the query-builder to eager-load the nodes that are connected to
// the "api_keys" edge. The optional arguments are used to configure the query builder of the edge.
// the "api_keys" edge. The optional arguments are used to configure the query builder of the edge.
func
(
_q
*
UserQuery
)
WithAPIKeys
(
opts
...
func
(
*
A
pi
KeyQuery
))
*
UserQuery
{
func
(
_q
*
UserQuery
)
WithAPIKeys
(
opts
...
func
(
*
A
PI
KeyQuery
))
*
UserQuery
{
query
:=
(
&
A
pi
KeyClient
{
config
:
_q
.
config
})
.
Query
()
query
:=
(
&
A
PI
KeyClient
{
config
:
_q
.
config
})
.
Query
()
for
_
,
opt
:=
range
opts
{
for
_
,
opt
:=
range
opts
{
opt
(
query
)
opt
(
query
)
}
}
...
@@ -653,8 +653,8 @@ func (_q *UserQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*User, e
...
@@ -653,8 +653,8 @@ func (_q *UserQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*User, e
}
}
if
query
:=
_q
.
withAPIKeys
;
query
!=
nil
{
if
query
:=
_q
.
withAPIKeys
;
query
!=
nil
{
if
err
:=
_q
.
loadAPIKeys
(
ctx
,
query
,
nodes
,
if
err
:=
_q
.
loadAPIKeys
(
ctx
,
query
,
nodes
,
func
(
n
*
User
)
{
n
.
Edges
.
APIKeys
=
[]
*
A
pi
Key
{}
},
func
(
n
*
User
)
{
n
.
Edges
.
APIKeys
=
[]
*
A
PI
Key
{}
},
func
(
n
*
User
,
e
*
A
pi
Key
)
{
n
.
Edges
.
APIKeys
=
append
(
n
.
Edges
.
APIKeys
,
e
)
});
err
!=
nil
{
func
(
n
*
User
,
e
*
A
PI
Key
)
{
n
.
Edges
.
APIKeys
=
append
(
n
.
Edges
.
APIKeys
,
e
)
});
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
}
}
...
@@ -712,7 +712,7 @@ func (_q *UserQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*User, e
...
@@ -712,7 +712,7 @@ func (_q *UserQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*User, e
return
nodes
,
nil
return
nodes
,
nil
}
}
func
(
_q
*
UserQuery
)
loadAPIKeys
(
ctx
context
.
Context
,
query
*
A
pi
KeyQuery
,
nodes
[]
*
User
,
init
func
(
*
User
),
assign
func
(
*
User
,
*
A
pi
Key
))
error
{
func
(
_q
*
UserQuery
)
loadAPIKeys
(
ctx
context
.
Context
,
query
*
A
PI
KeyQuery
,
nodes
[]
*
User
,
init
func
(
*
User
),
assign
func
(
*
User
,
*
A
PI
Key
))
error
{
fks
:=
make
([]
driver
.
Value
,
0
,
len
(
nodes
))
fks
:=
make
([]
driver
.
Value
,
0
,
len
(
nodes
))
nodeids
:=
make
(
map
[
int64
]
*
User
)
nodeids
:=
make
(
map
[
int64
]
*
User
)
for
i
:=
range
nodes
{
for
i
:=
range
nodes
{
...
@@ -725,7 +725,7 @@ func (_q *UserQuery) loadAPIKeys(ctx context.Context, query *ApiKeyQuery, nodes
...
@@ -725,7 +725,7 @@ func (_q *UserQuery) loadAPIKeys(ctx context.Context, query *ApiKeyQuery, nodes
if
len
(
query
.
ctx
.
Fields
)
>
0
{
if
len
(
query
.
ctx
.
Fields
)
>
0
{
query
.
ctx
.
AppendFieldOnce
(
apikey
.
FieldUserID
)
query
.
ctx
.
AppendFieldOnce
(
apikey
.
FieldUserID
)
}
}
query
.
Where
(
predicate
.
A
pi
Key
(
func
(
s
*
sql
.
Selector
)
{
query
.
Where
(
predicate
.
A
PI
Key
(
func
(
s
*
sql
.
Selector
)
{
s
.
Where
(
sql
.
InValues
(
s
.
C
(
user
.
APIKeysColumn
),
fks
...
))
s
.
Where
(
sql
.
InValues
(
s
.
C
(
user
.
APIKeysColumn
),
fks
...
))
}))
}))
neighbors
,
err
:=
query
.
All
(
ctx
)
neighbors
,
err
:=
query
.
All
(
ctx
)
...
...
backend/ent/user_update.go
View file @
c86d445c
...
@@ -186,14 +186,14 @@ func (_u *UserUpdate) SetNillableNotes(v *string) *UserUpdate {
...
@@ -186,14 +186,14 @@ func (_u *UserUpdate) SetNillableNotes(v *string) *UserUpdate {
return
_u
return
_u
}
}
// AddAPIKeyIDs adds the "api_keys" edge to the A
pi
Key entity by IDs.
// AddAPIKeyIDs adds the "api_keys" edge to the A
PI
Key entity by IDs.
func
(
_u
*
UserUpdate
)
AddAPIKeyIDs
(
ids
...
int64
)
*
UserUpdate
{
func
(
_u
*
UserUpdate
)
AddAPIKeyIDs
(
ids
...
int64
)
*
UserUpdate
{
_u
.
mutation
.
AddAPIKeyIDs
(
ids
...
)
_u
.
mutation
.
AddAPIKeyIDs
(
ids
...
)
return
_u
return
_u
}
}
// AddAPIKeys adds the "api_keys" edges to the A
pi
Key entity.
// AddAPIKeys adds the "api_keys" edges to the A
PI
Key entity.
func
(
_u
*
UserUpdate
)
AddAPIKeys
(
v
...*
A
pi
Key
)
*
UserUpdate
{
func
(
_u
*
UserUpdate
)
AddAPIKeys
(
v
...*
A
PI
Key
)
*
UserUpdate
{
ids
:=
make
([]
int64
,
len
(
v
))
ids
:=
make
([]
int64
,
len
(
v
))
for
i
:=
range
v
{
for
i
:=
range
v
{
ids
[
i
]
=
v
[
i
]
.
ID
ids
[
i
]
=
v
[
i
]
.
ID
...
@@ -296,20 +296,20 @@ func (_u *UserUpdate) Mutation() *UserMutation {
...
@@ -296,20 +296,20 @@ func (_u *UserUpdate) Mutation() *UserMutation {
return
_u
.
mutation
return
_u
.
mutation
}
}
// ClearAPIKeys clears all "api_keys" edges to the A
pi
Key entity.
// ClearAPIKeys clears all "api_keys" edges to the A
PI
Key entity.
func
(
_u
*
UserUpdate
)
ClearAPIKeys
()
*
UserUpdate
{
func
(
_u
*
UserUpdate
)
ClearAPIKeys
()
*
UserUpdate
{
_u
.
mutation
.
ClearAPIKeys
()
_u
.
mutation
.
ClearAPIKeys
()
return
_u
return
_u
}
}
// RemoveAPIKeyIDs removes the "api_keys" edge to A
pi
Key entities by IDs.
// RemoveAPIKeyIDs removes the "api_keys" edge to A
PI
Key entities by IDs.
func
(
_u
*
UserUpdate
)
RemoveAPIKeyIDs
(
ids
...
int64
)
*
UserUpdate
{
func
(
_u
*
UserUpdate
)
RemoveAPIKeyIDs
(
ids
...
int64
)
*
UserUpdate
{
_u
.
mutation
.
RemoveAPIKeyIDs
(
ids
...
)
_u
.
mutation
.
RemoveAPIKeyIDs
(
ids
...
)
return
_u
return
_u
}
}
// RemoveAPIKeys removes "api_keys" edges to A
pi
Key entities.
// RemoveAPIKeys removes "api_keys" edges to A
PI
Key entities.
func
(
_u
*
UserUpdate
)
RemoveAPIKeys
(
v
...*
A
pi
Key
)
*
UserUpdate
{
func
(
_u
*
UserUpdate
)
RemoveAPIKeys
(
v
...*
A
PI
Key
)
*
UserUpdate
{
ids
:=
make
([]
int64
,
len
(
v
))
ids
:=
make
([]
int64
,
len
(
v
))
for
i
:=
range
v
{
for
i
:=
range
v
{
ids
[
i
]
=
v
[
i
]
.
ID
ids
[
i
]
=
v
[
i
]
.
ID
...
@@ -1065,14 +1065,14 @@ func (_u *UserUpdateOne) SetNillableNotes(v *string) *UserUpdateOne {
...
@@ -1065,14 +1065,14 @@ func (_u *UserUpdateOne) SetNillableNotes(v *string) *UserUpdateOne {
return
_u
return
_u
}
}
// AddAPIKeyIDs adds the "api_keys" edge to the A
pi
Key entity by IDs.
// AddAPIKeyIDs adds the "api_keys" edge to the A
PI
Key entity by IDs.
func
(
_u
*
UserUpdateOne
)
AddAPIKeyIDs
(
ids
...
int64
)
*
UserUpdateOne
{
func
(
_u
*
UserUpdateOne
)
AddAPIKeyIDs
(
ids
...
int64
)
*
UserUpdateOne
{
_u
.
mutation
.
AddAPIKeyIDs
(
ids
...
)
_u
.
mutation
.
AddAPIKeyIDs
(
ids
...
)
return
_u
return
_u
}
}
// AddAPIKeys adds the "api_keys" edges to the A
pi
Key entity.
// AddAPIKeys adds the "api_keys" edges to the A
PI
Key entity.
func
(
_u
*
UserUpdateOne
)
AddAPIKeys
(
v
...*
A
pi
Key
)
*
UserUpdateOne
{
func
(
_u
*
UserUpdateOne
)
AddAPIKeys
(
v
...*
A
PI
Key
)
*
UserUpdateOne
{
ids
:=
make
([]
int64
,
len
(
v
))
ids
:=
make
([]
int64
,
len
(
v
))
for
i
:=
range
v
{
for
i
:=
range
v
{
ids
[
i
]
=
v
[
i
]
.
ID
ids
[
i
]
=
v
[
i
]
.
ID
...
@@ -1175,20 +1175,20 @@ func (_u *UserUpdateOne) Mutation() *UserMutation {
...
@@ -1175,20 +1175,20 @@ func (_u *UserUpdateOne) Mutation() *UserMutation {
return
_u
.
mutation
return
_u
.
mutation
}
}
// ClearAPIKeys clears all "api_keys" edges to the A
pi
Key entity.
// ClearAPIKeys clears all "api_keys" edges to the A
PI
Key entity.
func
(
_u
*
UserUpdateOne
)
ClearAPIKeys
()
*
UserUpdateOne
{
func
(
_u
*
UserUpdateOne
)
ClearAPIKeys
()
*
UserUpdateOne
{
_u
.
mutation
.
ClearAPIKeys
()
_u
.
mutation
.
ClearAPIKeys
()
return
_u
return
_u
}
}
// RemoveAPIKeyIDs removes the "api_keys" edge to A
pi
Key entities by IDs.
// RemoveAPIKeyIDs removes the "api_keys" edge to A
PI
Key entities by IDs.
func
(
_u
*
UserUpdateOne
)
RemoveAPIKeyIDs
(
ids
...
int64
)
*
UserUpdateOne
{
func
(
_u
*
UserUpdateOne
)
RemoveAPIKeyIDs
(
ids
...
int64
)
*
UserUpdateOne
{
_u
.
mutation
.
RemoveAPIKeyIDs
(
ids
...
)
_u
.
mutation
.
RemoveAPIKeyIDs
(
ids
...
)
return
_u
return
_u
}
}
// RemoveAPIKeys removes "api_keys" edges to A
pi
Key entities.
// RemoveAPIKeys removes "api_keys" edges to A
PI
Key entities.
func
(
_u
*
UserUpdateOne
)
RemoveAPIKeys
(
v
...*
A
pi
Key
)
*
UserUpdateOne
{
func
(
_u
*
UserUpdateOne
)
RemoveAPIKeys
(
v
...*
A
PI
Key
)
*
UserUpdateOne
{
ids
:=
make
([]
int64
,
len
(
v
))
ids
:=
make
([]
int64
,
len
(
v
))
for
i
:=
range
v
{
for
i
:=
range
v
{
ids
[
i
]
=
v
[
i
]
.
ID
ids
[
i
]
=
v
[
i
]
.
ID
...
...
backend/internal/config/config.go
View file @
c86d445c
// Package config provides configuration loading, defaults, and validation.
package
config
package
config
import
(
import
(
...
@@ -139,7 +140,7 @@ type GatewayConfig struct {
...
@@ -139,7 +140,7 @@ type GatewayConfig struct {
LogUpstreamErrorBodyMaxBytes
int
`mapstructure:"log_upstream_error_body_max_bytes"`
LogUpstreamErrorBodyMaxBytes
int
`mapstructure:"log_upstream_error_body_max_bytes"`
// API-key 账号在客户端未提供 anthropic-beta 时,是否按需自动补齐(默认关闭以保持兼容)
// API-key 账号在客户端未提供 anthropic-beta 时,是否按需自动补齐(默认关闭以保持兼容)
InjectBetaForA
pi
Key
bool
`mapstructure:"inject_beta_for_apikey"`
InjectBetaForA
PI
Key
bool
`mapstructure:"inject_beta_for_apikey"`
// 是否允许对部分 400 错误触发 failover(默认关闭以避免改变语义)
// 是否允许对部分 400 错误触发 failover(默认关闭以避免改变语义)
FailoverOn400
bool
`mapstructure:"failover_on_400"`
FailoverOn400
bool
`mapstructure:"failover_on_400"`
...
@@ -241,7 +242,7 @@ type DefaultConfig struct {
...
@@ -241,7 +242,7 @@ type DefaultConfig struct {
AdminPassword
string
`mapstructure:"admin_password"`
AdminPassword
string
`mapstructure:"admin_password"`
UserConcurrency
int
`mapstructure:"user_concurrency"`
UserConcurrency
int
`mapstructure:"user_concurrency"`
UserBalance
float64
`mapstructure:"user_balance"`
UserBalance
float64
`mapstructure:"user_balance"`
A
pi
KeyPrefix
string
`mapstructure:"api_key_prefix"`
A
PI
KeyPrefix
string
`mapstructure:"api_key_prefix"`
RateMultiplier
float64
`mapstructure:"rate_multiplier"`
RateMultiplier
float64
`mapstructure:"rate_multiplier"`
}
}
...
...
backend/internal/handler/admin/account_handler.go
View file @
c86d445c
// Package admin provides HTTP handlers for administrative operations.
package
admin
package
admin
import
(
import
(
"errors"
"strconv"
"strconv"
"strings"
"strings"
"sync"
"sync"
"time"
"github.com/Wei-Shaw/sub2api/internal/handler/dto"
"github.com/Wei-Shaw/sub2api/internal/handler/dto"
"github.com/Wei-Shaw/sub2api/internal/pkg/claude"
"github.com/Wei-Shaw/sub2api/internal/pkg/claude"
...
@@ -69,42 +72,45 @@ func NewAccountHandler(
...
@@ -69,42 +72,45 @@ func NewAccountHandler(
// CreateAccountRequest represents create account request
// CreateAccountRequest represents create account request
type
CreateAccountRequest
struct
{
type
CreateAccountRequest
struct
{
Name
string
`json:"name" binding:"required"`
Name
string
`json:"name" binding:"required"`
Platform
string
`json:"platform" binding:"required"`
Platform
string
`json:"platform" binding:"required"`
Type
string
`json:"type" binding:"required,oneof=oauth setup-token apikey"`
Type
string
`json:"type" binding:"required,oneof=oauth setup-token apikey"`
Credentials
map
[
string
]
any
`json:"credentials" binding:"required"`
Credentials
map
[
string
]
any
`json:"credentials" binding:"required"`
Extra
map
[
string
]
any
`json:"extra"`
Extra
map
[
string
]
any
`json:"extra"`
ProxyID
*
int64
`json:"proxy_id"`
ProxyID
*
int64
`json:"proxy_id"`
Concurrency
int
`json:"concurrency"`
Concurrency
int
`json:"concurrency"`
Priority
int
`json:"priority"`
Priority
int
`json:"priority"`
GroupIDs
[]
int64
`json:"group_ids"`
GroupIDs
[]
int64
`json:"group_ids"`
ConfirmMixedChannelRisk
*
bool
`json:"confirm_mixed_channel_risk"`
// 用户确认混合渠道风险
}
}
// UpdateAccountRequest represents update account request
// UpdateAccountRequest represents update account request
// 使用指针类型来区分"未提供"和"设置为0"
// 使用指针类型来区分"未提供"和"设置为0"
type
UpdateAccountRequest
struct
{
type
UpdateAccountRequest
struct
{
Name
string
`json:"name"`
Name
string
`json:"name"`
Type
string
`json:"type" binding:"omitempty,oneof=oauth setup-token apikey"`
Type
string
`json:"type" binding:"omitempty,oneof=oauth setup-token apikey"`
Credentials
map
[
string
]
any
`json:"credentials"`
Credentials
map
[
string
]
any
`json:"credentials"`
Extra
map
[
string
]
any
`json:"extra"`
Extra
map
[
string
]
any
`json:"extra"`
ProxyID
*
int64
`json:"proxy_id"`
ProxyID
*
int64
`json:"proxy_id"`
Concurrency
*
int
`json:"concurrency"`
Concurrency
*
int
`json:"concurrency"`
Priority
*
int
`json:"priority"`
Priority
*
int
`json:"priority"`
Status
string
`json:"status" binding:"omitempty,oneof=active inactive"`
Status
string
`json:"status" binding:"omitempty,oneof=active inactive"`
GroupIDs
*
[]
int64
`json:"group_ids"`
GroupIDs
*
[]
int64
`json:"group_ids"`
ConfirmMixedChannelRisk
*
bool
`json:"confirm_mixed_channel_risk"`
// 用户确认混合渠道风险
}
}
// BulkUpdateAccountsRequest represents the payload for bulk editing accounts
// BulkUpdateAccountsRequest represents the payload for bulk editing accounts
type
BulkUpdateAccountsRequest
struct
{
type
BulkUpdateAccountsRequest
struct
{
AccountIDs
[]
int64
`json:"account_ids" binding:"required,min=1"`
AccountIDs
[]
int64
`json:"account_ids" binding:"required,min=1"`
Name
string
`json:"name"`
Name
string
`json:"name"`
ProxyID
*
int64
`json:"proxy_id"`
ProxyID
*
int64
`json:"proxy_id"`
Concurrency
*
int
`json:"concurrency"`
Concurrency
*
int
`json:"concurrency"`
Priority
*
int
`json:"priority"`
Priority
*
int
`json:"priority"`
Status
string
`json:"status" binding:"omitempty,oneof=active inactive error"`
Status
string
`json:"status" binding:"omitempty,oneof=active inactive error"`
GroupIDs
*
[]
int64
`json:"group_ids"`
GroupIDs
*
[]
int64
`json:"group_ids"`
Credentials
map
[
string
]
any
`json:"credentials"`
Credentials
map
[
string
]
any
`json:"credentials"`
Extra
map
[
string
]
any
`json:"extra"`
Extra
map
[
string
]
any
`json:"extra"`
ConfirmMixedChannelRisk
*
bool
`json:"confirm_mixed_channel_risk"`
// 用户确认混合渠道风险
}
}
// AccountWithConcurrency extends Account with real-time concurrency info
// AccountWithConcurrency extends Account with real-time concurrency info
...
@@ -179,18 +185,40 @@ func (h *AccountHandler) Create(c *gin.Context) {
...
@@ -179,18 +185,40 @@ func (h *AccountHandler) Create(c *gin.Context) {
return
return
}
}
// 确定是否跳过混合渠道检查
skipCheck
:=
req
.
ConfirmMixedChannelRisk
!=
nil
&&
*
req
.
ConfirmMixedChannelRisk
account
,
err
:=
h
.
adminService
.
CreateAccount
(
c
.
Request
.
Context
(),
&
service
.
CreateAccountInput
{
account
,
err
:=
h
.
adminService
.
CreateAccount
(
c
.
Request
.
Context
(),
&
service
.
CreateAccountInput
{
Name
:
req
.
Name
,
Name
:
req
.
Name
,
Platform
:
req
.
Platform
,
Platform
:
req
.
Platform
,
Type
:
req
.
Type
,
Type
:
req
.
Type
,
Credentials
:
req
.
Credentials
,
Credentials
:
req
.
Credentials
,
Extra
:
req
.
Extra
,
Extra
:
req
.
Extra
,
ProxyID
:
req
.
ProxyID
,
ProxyID
:
req
.
ProxyID
,
Concurrency
:
req
.
Concurrency
,
Concurrency
:
req
.
Concurrency
,
Priority
:
req
.
Priority
,
Priority
:
req
.
Priority
,
GroupIDs
:
req
.
GroupIDs
,
GroupIDs
:
req
.
GroupIDs
,
SkipMixedChannelCheck
:
skipCheck
,
})
})
if
err
!=
nil
{
if
err
!=
nil
{
// 检查是否为混合渠道错误
var
mixedErr
*
service
.
MixedChannelError
if
errors
.
As
(
err
,
&
mixedErr
)
{
// 返回特殊错误码要求确认
c
.
JSON
(
409
,
gin
.
H
{
"error"
:
"mixed_channel_warning"
,
"message"
:
mixedErr
.
Error
(),
"details"
:
gin
.
H
{
"group_id"
:
mixedErr
.
GroupID
,
"group_name"
:
mixedErr
.
GroupName
,
"current_platform"
:
mixedErr
.
CurrentPlatform
,
"other_platform"
:
mixedErr
.
OtherPlatform
,
},
"require_confirmation"
:
true
,
})
return
}
response
.
ErrorFrom
(
c
,
err
)
response
.
ErrorFrom
(
c
,
err
)
return
return
}
}
...
@@ -213,18 +241,40 @@ func (h *AccountHandler) Update(c *gin.Context) {
...
@@ -213,18 +241,40 @@ func (h *AccountHandler) Update(c *gin.Context) {
return
return
}
}
// 确定是否跳过混合渠道检查
skipCheck
:=
req
.
ConfirmMixedChannelRisk
!=
nil
&&
*
req
.
ConfirmMixedChannelRisk
account
,
err
:=
h
.
adminService
.
UpdateAccount
(
c
.
Request
.
Context
(),
accountID
,
&
service
.
UpdateAccountInput
{
account
,
err
:=
h
.
adminService
.
UpdateAccount
(
c
.
Request
.
Context
(),
accountID
,
&
service
.
UpdateAccountInput
{
Name
:
req
.
Name
,
Name
:
req
.
Name
,
Type
:
req
.
Type
,
Type
:
req
.
Type
,
Credentials
:
req
.
Credentials
,
Credentials
:
req
.
Credentials
,
Extra
:
req
.
Extra
,
Extra
:
req
.
Extra
,
ProxyID
:
req
.
ProxyID
,
ProxyID
:
req
.
ProxyID
,
Concurrency
:
req
.
Concurrency
,
// 指针类型,nil 表示未提供
Concurrency
:
req
.
Concurrency
,
// 指针类型,nil 表示未提供
Priority
:
req
.
Priority
,
// 指针类型,nil 表示未提供
Priority
:
req
.
Priority
,
// 指针类型,nil 表示未提供
Status
:
req
.
Status
,
Status
:
req
.
Status
,
GroupIDs
:
req
.
GroupIDs
,
GroupIDs
:
req
.
GroupIDs
,
SkipMixedChannelCheck
:
skipCheck
,
})
})
if
err
!=
nil
{
if
err
!=
nil
{
// 检查是否为混合渠道错误
var
mixedErr
*
service
.
MixedChannelError
if
errors
.
As
(
err
,
&
mixedErr
)
{
// 返回特殊错误码要求确认
c
.
JSON
(
409
,
gin
.
H
{
"error"
:
"mixed_channel_warning"
,
"message"
:
mixedErr
.
Error
(),
"details"
:
gin
.
H
{
"group_id"
:
mixedErr
.
GroupID
,
"group_name"
:
mixedErr
.
GroupName
,
"current_platform"
:
mixedErr
.
CurrentPlatform
,
"other_platform"
:
mixedErr
.
OtherPlatform
,
},
"require_confirmation"
:
true
,
})
return
}
response
.
ErrorFrom
(
c
,
err
)
response
.
ErrorFrom
(
c
,
err
)
return
return
}
}
...
@@ -568,6 +618,9 @@ func (h *AccountHandler) BulkUpdate(c *gin.Context) {
...
@@ -568,6 +618,9 @@ func (h *AccountHandler) BulkUpdate(c *gin.Context) {
return
return
}
}
// 确定是否跳过混合渠道检查
skipCheck
:=
req
.
ConfirmMixedChannelRisk
!=
nil
&&
*
req
.
ConfirmMixedChannelRisk
hasUpdates
:=
req
.
Name
!=
""
||
hasUpdates
:=
req
.
Name
!=
""
||
req
.
ProxyID
!=
nil
||
req
.
ProxyID
!=
nil
||
req
.
Concurrency
!=
nil
||
req
.
Concurrency
!=
nil
||
...
@@ -583,15 +636,16 @@ func (h *AccountHandler) BulkUpdate(c *gin.Context) {
...
@@ -583,15 +636,16 @@ func (h *AccountHandler) BulkUpdate(c *gin.Context) {
}
}
result
,
err
:=
h
.
adminService
.
BulkUpdateAccounts
(
c
.
Request
.
Context
(),
&
service
.
BulkUpdateAccountsInput
{
result
,
err
:=
h
.
adminService
.
BulkUpdateAccounts
(
c
.
Request
.
Context
(),
&
service
.
BulkUpdateAccountsInput
{
AccountIDs
:
req
.
AccountIDs
,
AccountIDs
:
req
.
AccountIDs
,
Name
:
req
.
Name
,
Name
:
req
.
Name
,
ProxyID
:
req
.
ProxyID
,
ProxyID
:
req
.
ProxyID
,
Concurrency
:
req
.
Concurrency
,
Concurrency
:
req
.
Concurrency
,
Priority
:
req
.
Priority
,
Priority
:
req
.
Priority
,
Status
:
req
.
Status
,
Status
:
req
.
Status
,
GroupIDs
:
req
.
GroupIDs
,
GroupIDs
:
req
.
GroupIDs
,
Credentials
:
req
.
Credentials
,
Credentials
:
req
.
Credentials
,
Extra
:
req
.
Extra
,
Extra
:
req
.
Extra
,
SkipMixedChannelCheck
:
skipCheck
,
})
})
if
err
!=
nil
{
if
err
!=
nil
{
response
.
ErrorFrom
(
c
,
err
)
response
.
ErrorFrom
(
c
,
err
)
...
@@ -781,6 +835,49 @@ func (h *AccountHandler) ClearRateLimit(c *gin.Context) {
...
@@ -781,6 +835,49 @@ func (h *AccountHandler) ClearRateLimit(c *gin.Context) {
response
.
Success
(
c
,
gin
.
H
{
"message"
:
"Rate limit cleared successfully"
})
response
.
Success
(
c
,
gin
.
H
{
"message"
:
"Rate limit cleared successfully"
})
}
}
// GetTempUnschedulable handles getting temporary unschedulable status
// GET /api/v1/admin/accounts/:id/temp-unschedulable
func
(
h
*
AccountHandler
)
GetTempUnschedulable
(
c
*
gin
.
Context
)
{
accountID
,
err
:=
strconv
.
ParseInt
(
c
.
Param
(
"id"
),
10
,
64
)
if
err
!=
nil
{
response
.
BadRequest
(
c
,
"Invalid account ID"
)
return
}
state
,
err
:=
h
.
rateLimitService
.
GetTempUnschedStatus
(
c
.
Request
.
Context
(),
accountID
)
if
err
!=
nil
{
response
.
ErrorFrom
(
c
,
err
)
return
}
if
state
==
nil
||
state
.
UntilUnix
<=
time
.
Now
()
.
Unix
()
{
response
.
Success
(
c
,
gin
.
H
{
"active"
:
false
})
return
}
response
.
Success
(
c
,
gin
.
H
{
"active"
:
true
,
"state"
:
state
,
})
}
// ClearTempUnschedulable handles clearing temporary unschedulable status
// DELETE /api/v1/admin/accounts/:id/temp-unschedulable
func
(
h
*
AccountHandler
)
ClearTempUnschedulable
(
c
*
gin
.
Context
)
{
accountID
,
err
:=
strconv
.
ParseInt
(
c
.
Param
(
"id"
),
10
,
64
)
if
err
!=
nil
{
response
.
BadRequest
(
c
,
"Invalid account ID"
)
return
}
if
err
:=
h
.
rateLimitService
.
ClearTempUnschedulable
(
c
.
Request
.
Context
(),
accountID
);
err
!=
nil
{
response
.
ErrorFrom
(
c
,
err
)
return
}
response
.
Success
(
c
,
gin
.
H
{
"message"
:
"Temp unschedulable cleared successfully"
})
}
// GetTodayStats handles getting account today statistics
// GetTodayStats handles getting account today statistics
// GET /api/v1/admin/accounts/:id/today-stats
// GET /api/v1/admin/accounts/:id/today-stats
func
(
h
*
AccountHandler
)
GetTodayStats
(
c
*
gin
.
Context
)
{
func
(
h
*
AccountHandler
)
GetTodayStats
(
c
*
gin
.
Context
)
{
...
...
backend/internal/handler/admin/dashboard_handler.go
View file @
c86d445c
...
@@ -75,8 +75,8 @@ func (h *DashboardHandler) GetStats(c *gin.Context) {
...
@@ -75,8 +75,8 @@ func (h *DashboardHandler) GetStats(c *gin.Context) {
"active_users"
:
stats
.
ActiveUsers
,
"active_users"
:
stats
.
ActiveUsers
,
// API Key 统计
// API Key 统计
"total_api_keys"
:
stats
.
TotalA
pi
Keys
,
"total_api_keys"
:
stats
.
TotalA
PI
Keys
,
"active_api_keys"
:
stats
.
ActiveA
pi
Keys
,
"active_api_keys"
:
stats
.
ActiveA
PI
Keys
,
// 账户统计
// 账户统计
"total_accounts"
:
stats
.
TotalAccounts
,
"total_accounts"
:
stats
.
TotalAccounts
,
...
@@ -193,10 +193,10 @@ func (h *DashboardHandler) GetModelStats(c *gin.Context) {
...
@@ -193,10 +193,10 @@ func (h *DashboardHandler) GetModelStats(c *gin.Context) {
})
})
}
}
// GetA
pi
KeyUsageTrend handles getting API key usage trend data
// GetA
PI
KeyUsageTrend handles getting API key usage trend data
// GET /api/v1/admin/dashboard/api-keys-trend
// GET /api/v1/admin/dashboard/api-keys-trend
// Query params: start_date, end_date (YYYY-MM-DD), granularity (day/hour), limit (default 5)
// Query params: start_date, end_date (YYYY-MM-DD), granularity (day/hour), limit (default 5)
func
(
h
*
DashboardHandler
)
GetA
pi
KeyUsageTrend
(
c
*
gin
.
Context
)
{
func
(
h
*
DashboardHandler
)
GetA
PI
KeyUsageTrend
(
c
*
gin
.
Context
)
{
startTime
,
endTime
:=
parseTimeRange
(
c
)
startTime
,
endTime
:=
parseTimeRange
(
c
)
granularity
:=
c
.
DefaultQuery
(
"granularity"
,
"day"
)
granularity
:=
c
.
DefaultQuery
(
"granularity"
,
"day"
)
limitStr
:=
c
.
DefaultQuery
(
"limit"
,
"5"
)
limitStr
:=
c
.
DefaultQuery
(
"limit"
,
"5"
)
...
@@ -205,7 +205,7 @@ func (h *DashboardHandler) GetApiKeyUsageTrend(c *gin.Context) {
...
@@ -205,7 +205,7 @@ func (h *DashboardHandler) GetApiKeyUsageTrend(c *gin.Context) {
limit
=
5
limit
=
5
}
}
trend
,
err
:=
h
.
dashboardService
.
GetA
pi
KeyUsageTrend
(
c
.
Request
.
Context
(),
startTime
,
endTime
,
granularity
,
limit
)
trend
,
err
:=
h
.
dashboardService
.
GetA
PI
KeyUsageTrend
(
c
.
Request
.
Context
(),
startTime
,
endTime
,
granularity
,
limit
)
if
err
!=
nil
{
if
err
!=
nil
{
response
.
Error
(
c
,
500
,
"Failed to get API key usage trend"
)
response
.
Error
(
c
,
500
,
"Failed to get API key usage trend"
)
return
return
...
@@ -273,26 +273,26 @@ func (h *DashboardHandler) GetBatchUsersUsage(c *gin.Context) {
...
@@ -273,26 +273,26 @@ func (h *DashboardHandler) GetBatchUsersUsage(c *gin.Context) {
response
.
Success
(
c
,
gin
.
H
{
"stats"
:
stats
})
response
.
Success
(
c
,
gin
.
H
{
"stats"
:
stats
})
}
}
// BatchA
pi
KeysUsageRequest represents the request body for batch api key usage stats
// BatchA
PI
KeysUsageRequest represents the request body for batch api key usage stats
type
BatchA
pi
KeysUsageRequest
struct
{
type
BatchA
PI
KeysUsageRequest
struct
{
A
pi
KeyIDs
[]
int64
`json:"api_key_ids" binding:"required"`
A
PI
KeyIDs
[]
int64
`json:"api_key_ids" binding:"required"`
}
}
// GetBatchA
pi
KeysUsage handles getting usage stats for multiple API keys
// GetBatchA
PI
KeysUsage handles getting usage stats for multiple API keys
// POST /api/v1/admin/dashboard/api-keys-usage
// POST /api/v1/admin/dashboard/api-keys-usage
func
(
h
*
DashboardHandler
)
GetBatchA
pi
KeysUsage
(
c
*
gin
.
Context
)
{
func
(
h
*
DashboardHandler
)
GetBatchA
PI
KeysUsage
(
c
*
gin
.
Context
)
{
var
req
BatchA
pi
KeysUsageRequest
var
req
BatchA
PI
KeysUsageRequest
if
err
:=
c
.
ShouldBindJSON
(
&
req
);
err
!=
nil
{
if
err
:=
c
.
ShouldBindJSON
(
&
req
);
err
!=
nil
{
response
.
BadRequest
(
c
,
"Invalid request: "
+
err
.
Error
())
response
.
BadRequest
(
c
,
"Invalid request: "
+
err
.
Error
())
return
return
}
}
if
len
(
req
.
A
pi
KeyIDs
)
==
0
{
if
len
(
req
.
A
PI
KeyIDs
)
==
0
{
response
.
Success
(
c
,
gin
.
H
{
"stats"
:
map
[
string
]
any
{}})
response
.
Success
(
c
,
gin
.
H
{
"stats"
:
map
[
string
]
any
{}})
return
return
}
}
stats
,
err
:=
h
.
dashboardService
.
GetBatchA
pi
KeyUsageStats
(
c
.
Request
.
Context
(),
req
.
A
pi
KeyIDs
)
stats
,
err
:=
h
.
dashboardService
.
GetBatchA
PI
KeyUsageStats
(
c
.
Request
.
Context
(),
req
.
A
PI
KeyIDs
)
if
err
!=
nil
{
if
err
!=
nil
{
response
.
Error
(
c
,
500
,
"Failed to get API key usage stats"
)
response
.
Error
(
c
,
500
,
"Failed to get API key usage stats"
)
return
return
...
...
backend/internal/handler/admin/gemini_oauth_handler.go
View file @
c86d445c
...
@@ -18,6 +18,7 @@ func NewGeminiOAuthHandler(geminiOAuthService *service.GeminiOAuthService) *Gemi
...
@@ -18,6 +18,7 @@ func NewGeminiOAuthHandler(geminiOAuthService *service.GeminiOAuthService) *Gemi
return
&
GeminiOAuthHandler
{
geminiOAuthService
:
geminiOAuthService
}
return
&
GeminiOAuthHandler
{
geminiOAuthService
:
geminiOAuthService
}
}
}
// GetCapabilities returns the Gemini OAuth configuration capabilities.
// GET /api/v1/admin/gemini/oauth/capabilities
// GET /api/v1/admin/gemini/oauth/capabilities
func
(
h
*
GeminiOAuthHandler
)
GetCapabilities
(
c
*
gin
.
Context
)
{
func
(
h
*
GeminiOAuthHandler
)
GetCapabilities
(
c
*
gin
.
Context
)
{
cfg
:=
h
.
geminiOAuthService
.
GetOAuthConfig
()
cfg
:=
h
.
geminiOAuthService
.
GetOAuthConfig
()
...
@@ -30,6 +31,8 @@ type GeminiGenerateAuthURLRequest struct {
...
@@ -30,6 +31,8 @@ type GeminiGenerateAuthURLRequest struct {
// OAuth 类型: "code_assist" (需要 project_id) 或 "ai_studio" (不需要 project_id)
// OAuth 类型: "code_assist" (需要 project_id) 或 "ai_studio" (不需要 project_id)
// 默认为 "code_assist" 以保持向后兼容
// 默认为 "code_assist" 以保持向后兼容
OAuthType
string
`json:"oauth_type"`
OAuthType
string
`json:"oauth_type"`
// TierID is a user-selected tier to be used when auto detection is unavailable or fails.
TierID
string
`json:"tier_id"`
}
}
// GenerateAuthURL generates Google OAuth authorization URL for Gemini.
// GenerateAuthURL generates Google OAuth authorization URL for Gemini.
...
@@ -54,7 +57,7 @@ func (h *GeminiOAuthHandler) GenerateAuthURL(c *gin.Context) {
...
@@ -54,7 +57,7 @@ func (h *GeminiOAuthHandler) GenerateAuthURL(c *gin.Context) {
// Always pass the "hosted" callback URI; the OAuth service may override it depending on
// Always pass the "hosted" callback URI; the OAuth service may override it depending on
// oauth_type and whether the built-in Gemini CLI OAuth client is used.
// oauth_type and whether the built-in Gemini CLI OAuth client is used.
redirectURI
:=
deriveGeminiRedirectURI
(
c
)
redirectURI
:=
deriveGeminiRedirectURI
(
c
)
result
,
err
:=
h
.
geminiOAuthService
.
GenerateAuthURL
(
c
.
Request
.
Context
(),
req
.
ProxyID
,
redirectURI
,
req
.
ProjectID
,
oauthType
)
result
,
err
:=
h
.
geminiOAuthService
.
GenerateAuthURL
(
c
.
Request
.
Context
(),
req
.
ProxyID
,
redirectURI
,
req
.
ProjectID
,
oauthType
,
req
.
TierID
)
if
err
!=
nil
{
if
err
!=
nil
{
msg
:=
err
.
Error
()
msg
:=
err
.
Error
()
// Treat missing/invalid OAuth client configuration as a user/config error.
// Treat missing/invalid OAuth client configuration as a user/config error.
...
@@ -76,6 +79,9 @@ type GeminiExchangeCodeRequest struct {
...
@@ -76,6 +79,9 @@ type GeminiExchangeCodeRequest struct {
ProxyID
*
int64
`json:"proxy_id"`
ProxyID
*
int64
`json:"proxy_id"`
// OAuth 类型: "code_assist" 或 "ai_studio",需要与 GenerateAuthURL 时的类型一致
// OAuth 类型: "code_assist" 或 "ai_studio",需要与 GenerateAuthURL 时的类型一致
OAuthType
string
`json:"oauth_type"`
OAuthType
string
`json:"oauth_type"`
// TierID is a user-selected tier to be used when auto detection is unavailable or fails.
// This field is optional; when omitted, the server uses the tier stored in the OAuth session.
TierID
string
`json:"tier_id"`
}
}
// ExchangeCode exchanges authorization code for tokens.
// ExchangeCode exchanges authorization code for tokens.
...
@@ -103,6 +109,7 @@ func (h *GeminiOAuthHandler) ExchangeCode(c *gin.Context) {
...
@@ -103,6 +109,7 @@ func (h *GeminiOAuthHandler) ExchangeCode(c *gin.Context) {
Code
:
req
.
Code
,
Code
:
req
.
Code
,
ProxyID
:
req
.
ProxyID
,
ProxyID
:
req
.
ProxyID
,
OAuthType
:
oauthType
,
OAuthType
:
oauthType
,
TierID
:
req
.
TierID
,
})
})
if
err
!=
nil
{
if
err
!=
nil
{
response
.
BadRequest
(
c
,
"Failed to exchange code: "
+
err
.
Error
())
response
.
BadRequest
(
c
,
"Failed to exchange code: "
+
err
.
Error
())
...
...
backend/internal/handler/admin/group_handler.go
View file @
c86d445c
...
@@ -237,9 +237,9 @@ func (h *GroupHandler) GetGroupAPIKeys(c *gin.Context) {
...
@@ -237,9 +237,9 @@ func (h *GroupHandler) GetGroupAPIKeys(c *gin.Context) {
return
return
}
}
outKeys
:=
make
([]
dto
.
A
pi
Key
,
0
,
len
(
keys
))
outKeys
:=
make
([]
dto
.
A
PI
Key
,
0
,
len
(
keys
))
for
i
:=
range
keys
{
for
i
:=
range
keys
{
outKeys
=
append
(
outKeys
,
*
dto
.
A
pi
KeyFromService
(
&
keys
[
i
]))
outKeys
=
append
(
outKeys
,
*
dto
.
A
PI
KeyFromService
(
&
keys
[
i
]))
}
}
response
.
Paginated
(
c
,
outKeys
,
total
,
page
,
pageSize
)
response
.
Paginated
(
c
,
outKeys
,
total
,
page
,
pageSize
)
}
}
backend/internal/handler/admin/setting_handler.go
View file @
c86d445c
...
@@ -34,26 +34,31 @@ func (h *SettingHandler) GetSettings(c *gin.Context) {
...
@@ -34,26 +34,31 @@ func (h *SettingHandler) GetSettings(c *gin.Context) {
}
}
response
.
Success
(
c
,
dto
.
SystemSettings
{
response
.
Success
(
c
,
dto
.
SystemSettings
{
RegistrationEnabled
:
settings
.
RegistrationEnabled
,
RegistrationEnabled
:
settings
.
RegistrationEnabled
,
EmailVerifyEnabled
:
settings
.
EmailVerifyEnabled
,
EmailVerifyEnabled
:
settings
.
EmailVerifyEnabled
,
SmtpHost
:
settings
.
SmtpHost
,
SMTPHost
:
settings
.
SMTPHost
,
SmtpPort
:
settings
.
SmtpPort
,
SMTPPort
:
settings
.
SMTPPort
,
SmtpUsername
:
settings
.
SmtpUsername
,
SMTPUsername
:
settings
.
SMTPUsername
,
SmtpPassword
:
settings
.
SmtpPassword
,
SMTPPassword
:
settings
.
SMTPPassword
,
SmtpFrom
:
settings
.
SmtpFrom
,
SMTPFrom
:
settings
.
SMTPFrom
,
SmtpFromName
:
settings
.
SmtpFromName
,
SMTPFromName
:
settings
.
SMTPFromName
,
SmtpUseTLS
:
settings
.
SmtpUseTLS
,
SMTPUseTLS
:
settings
.
SMTPUseTLS
,
TurnstileEnabled
:
settings
.
TurnstileEnabled
,
TurnstileEnabled
:
settings
.
TurnstileEnabled
,
TurnstileSiteKey
:
settings
.
TurnstileSiteKey
,
TurnstileSiteKey
:
settings
.
TurnstileSiteKey
,
TurnstileSecretKey
:
settings
.
TurnstileSecretKey
,
TurnstileSecretKey
:
settings
.
TurnstileSecretKey
,
SiteName
:
settings
.
SiteName
,
SiteName
:
settings
.
SiteName
,
SiteLogo
:
settings
.
SiteLogo
,
SiteLogo
:
settings
.
SiteLogo
,
SiteSubtitle
:
settings
.
SiteSubtitle
,
SiteSubtitle
:
settings
.
SiteSubtitle
,
ApiBaseUrl
:
settings
.
ApiBaseUrl
,
APIBaseURL
:
settings
.
APIBaseURL
,
ContactInfo
:
settings
.
ContactInfo
,
ContactInfo
:
settings
.
ContactInfo
,
DocUrl
:
settings
.
DocUrl
,
DocURL
:
settings
.
DocURL
,
DefaultConcurrency
:
settings
.
DefaultConcurrency
,
DefaultConcurrency
:
settings
.
DefaultConcurrency
,
DefaultBalance
:
settings
.
DefaultBalance
,
DefaultBalance
:
settings
.
DefaultBalance
,
EnableModelFallback
:
settings
.
EnableModelFallback
,
FallbackModelAnthropic
:
settings
.
FallbackModelAnthropic
,
FallbackModelOpenAI
:
settings
.
FallbackModelOpenAI
,
FallbackModelGemini
:
settings
.
FallbackModelGemini
,
FallbackModelAntigravity
:
settings
.
FallbackModelAntigravity
,
})
})
}
}
...
@@ -64,13 +69,13 @@ type UpdateSettingsRequest struct {
...
@@ -64,13 +69,13 @@ type UpdateSettingsRequest struct {
EmailVerifyEnabled
bool
`json:"email_verify_enabled"`
EmailVerifyEnabled
bool
`json:"email_verify_enabled"`
// 邮件服务设置
// 邮件服务设置
S
mtp
Host
string
`json:"smtp_host"`
S
MTP
Host
string
`json:"smtp_host"`
S
mtp
Port
int
`json:"smtp_port"`
S
MTP
Port
int
`json:"smtp_port"`
S
mtp
Username
string
`json:"smtp_username"`
S
MTP
Username
string
`json:"smtp_username"`
S
mtp
Password
string
`json:"smtp_password"`
S
MTP
Password
string
`json:"smtp_password"`
S
mtp
From
string
`json:"smtp_from_email"`
S
MTP
From
string
`json:"smtp_from_email"`
S
mtp
FromName
string
`json:"smtp_from_name"`
S
MTP
FromName
string
`json:"smtp_from_name"`
S
mtp
UseTLS
bool
`json:"smtp_use_tls"`
S
MTP
UseTLS
bool
`json:"smtp_use_tls"`
// Cloudflare Turnstile 设置
// Cloudflare Turnstile 设置
TurnstileEnabled
bool
`json:"turnstile_enabled"`
TurnstileEnabled
bool
`json:"turnstile_enabled"`
...
@@ -81,13 +86,20 @@ type UpdateSettingsRequest struct {
...
@@ -81,13 +86,20 @@ type UpdateSettingsRequest struct {
SiteName
string
`json:"site_name"`
SiteName
string
`json:"site_name"`
SiteLogo
string
`json:"site_logo"`
SiteLogo
string
`json:"site_logo"`
SiteSubtitle
string
`json:"site_subtitle"`
SiteSubtitle
string
`json:"site_subtitle"`
A
pi
BaseU
rl
string
`json:"api_base_url"`
A
PI
BaseU
RL
string
`json:"api_base_url"`
ContactInfo
string
`json:"contact_info"`
ContactInfo
string
`json:"contact_info"`
DocU
rl
string
`json:"doc_url"`
DocU
RL
string
`json:"doc_url"`
// 默认配置
// 默认配置
DefaultConcurrency
int
`json:"default_concurrency"`
DefaultConcurrency
int
`json:"default_concurrency"`
DefaultBalance
float64
`json:"default_balance"`
DefaultBalance
float64
`json:"default_balance"`
// Model fallback configuration
EnableModelFallback
bool
`json:"enable_model_fallback"`
FallbackModelAnthropic
string
`json:"fallback_model_anthropic"`
FallbackModelOpenAI
string
`json:"fallback_model_openai"`
FallbackModelGemini
string
`json:"fallback_model_gemini"`
FallbackModelAntigravity
string
`json:"fallback_model_antigravity"`
}
}
// UpdateSettings 更新系统设置
// UpdateSettings 更新系统设置
...
@@ -106,8 +118,8 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
...
@@ -106,8 +118,8 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
if
req
.
DefaultBalance
<
0
{
if
req
.
DefaultBalance
<
0
{
req
.
DefaultBalance
=
0
req
.
DefaultBalance
=
0
}
}
if
req
.
S
mtp
Port
<=
0
{
if
req
.
S
MTP
Port
<=
0
{
req
.
S
mtp
Port
=
587
req
.
S
MTP
Port
=
587
}
}
// Turnstile 参数验证
// Turnstile 参数验证
...
@@ -141,26 +153,31 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
...
@@ -141,26 +153,31 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
}
}
settings
:=
&
service
.
SystemSettings
{
settings
:=
&
service
.
SystemSettings
{
RegistrationEnabled
:
req
.
RegistrationEnabled
,
RegistrationEnabled
:
req
.
RegistrationEnabled
,
EmailVerifyEnabled
:
req
.
EmailVerifyEnabled
,
EmailVerifyEnabled
:
req
.
EmailVerifyEnabled
,
SmtpHost
:
req
.
SmtpHost
,
SMTPHost
:
req
.
SMTPHost
,
SmtpPort
:
req
.
SmtpPort
,
SMTPPort
:
req
.
SMTPPort
,
SmtpUsername
:
req
.
SmtpUsername
,
SMTPUsername
:
req
.
SMTPUsername
,
SmtpPassword
:
req
.
SmtpPassword
,
SMTPPassword
:
req
.
SMTPPassword
,
SmtpFrom
:
req
.
SmtpFrom
,
SMTPFrom
:
req
.
SMTPFrom
,
SmtpFromName
:
req
.
SmtpFromName
,
SMTPFromName
:
req
.
SMTPFromName
,
SmtpUseTLS
:
req
.
SmtpUseTLS
,
SMTPUseTLS
:
req
.
SMTPUseTLS
,
TurnstileEnabled
:
req
.
TurnstileEnabled
,
TurnstileEnabled
:
req
.
TurnstileEnabled
,
TurnstileSiteKey
:
req
.
TurnstileSiteKey
,
TurnstileSiteKey
:
req
.
TurnstileSiteKey
,
TurnstileSecretKey
:
req
.
TurnstileSecretKey
,
TurnstileSecretKey
:
req
.
TurnstileSecretKey
,
SiteName
:
req
.
SiteName
,
SiteName
:
req
.
SiteName
,
SiteLogo
:
req
.
SiteLogo
,
SiteLogo
:
req
.
SiteLogo
,
SiteSubtitle
:
req
.
SiteSubtitle
,
SiteSubtitle
:
req
.
SiteSubtitle
,
ApiBaseUrl
:
req
.
ApiBaseUrl
,
APIBaseURL
:
req
.
APIBaseURL
,
ContactInfo
:
req
.
ContactInfo
,
ContactInfo
:
req
.
ContactInfo
,
DocUrl
:
req
.
DocUrl
,
DocURL
:
req
.
DocURL
,
DefaultConcurrency
:
req
.
DefaultConcurrency
,
DefaultConcurrency
:
req
.
DefaultConcurrency
,
DefaultBalance
:
req
.
DefaultBalance
,
DefaultBalance
:
req
.
DefaultBalance
,
EnableModelFallback
:
req
.
EnableModelFallback
,
FallbackModelAnthropic
:
req
.
FallbackModelAnthropic
,
FallbackModelOpenAI
:
req
.
FallbackModelOpenAI
,
FallbackModelGemini
:
req
.
FallbackModelGemini
,
FallbackModelAntigravity
:
req
.
FallbackModelAntigravity
,
}
}
if
err
:=
h
.
settingService
.
UpdateSettings
(
c
.
Request
.
Context
(),
settings
);
err
!=
nil
{
if
err
:=
h
.
settingService
.
UpdateSettings
(
c
.
Request
.
Context
(),
settings
);
err
!=
nil
{
...
@@ -176,69 +193,74 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
...
@@ -176,69 +193,74 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
}
}
response
.
Success
(
c
,
dto
.
SystemSettings
{
response
.
Success
(
c
,
dto
.
SystemSettings
{
RegistrationEnabled
:
updatedSettings
.
RegistrationEnabled
,
RegistrationEnabled
:
updatedSettings
.
RegistrationEnabled
,
EmailVerifyEnabled
:
updatedSettings
.
EmailVerifyEnabled
,
EmailVerifyEnabled
:
updatedSettings
.
EmailVerifyEnabled
,
SmtpHost
:
updatedSettings
.
SmtpHost
,
SMTPHost
:
updatedSettings
.
SMTPHost
,
SmtpPort
:
updatedSettings
.
SmtpPort
,
SMTPPort
:
updatedSettings
.
SMTPPort
,
SmtpUsername
:
updatedSettings
.
SmtpUsername
,
SMTPUsername
:
updatedSettings
.
SMTPUsername
,
SmtpPassword
:
updatedSettings
.
SmtpPassword
,
SMTPPassword
:
updatedSettings
.
SMTPPassword
,
SmtpFrom
:
updatedSettings
.
SmtpFrom
,
SMTPFrom
:
updatedSettings
.
SMTPFrom
,
SmtpFromName
:
updatedSettings
.
SmtpFromName
,
SMTPFromName
:
updatedSettings
.
SMTPFromName
,
SmtpUseTLS
:
updatedSettings
.
SmtpUseTLS
,
SMTPUseTLS
:
updatedSettings
.
SMTPUseTLS
,
TurnstileEnabled
:
updatedSettings
.
TurnstileEnabled
,
TurnstileEnabled
:
updatedSettings
.
TurnstileEnabled
,
TurnstileSiteKey
:
updatedSettings
.
TurnstileSiteKey
,
TurnstileSiteKey
:
updatedSettings
.
TurnstileSiteKey
,
TurnstileSecretKey
:
updatedSettings
.
TurnstileSecretKey
,
TurnstileSecretKey
:
updatedSettings
.
TurnstileSecretKey
,
SiteName
:
updatedSettings
.
SiteName
,
SiteName
:
updatedSettings
.
SiteName
,
SiteLogo
:
updatedSettings
.
SiteLogo
,
SiteLogo
:
updatedSettings
.
SiteLogo
,
SiteSubtitle
:
updatedSettings
.
SiteSubtitle
,
SiteSubtitle
:
updatedSettings
.
SiteSubtitle
,
ApiBaseUrl
:
updatedSettings
.
ApiBaseUrl
,
APIBaseURL
:
updatedSettings
.
APIBaseURL
,
ContactInfo
:
updatedSettings
.
ContactInfo
,
ContactInfo
:
updatedSettings
.
ContactInfo
,
DocUrl
:
updatedSettings
.
DocUrl
,
DocURL
:
updatedSettings
.
DocURL
,
DefaultConcurrency
:
updatedSettings
.
DefaultConcurrency
,
DefaultConcurrency
:
updatedSettings
.
DefaultConcurrency
,
DefaultBalance
:
updatedSettings
.
DefaultBalance
,
DefaultBalance
:
updatedSettings
.
DefaultBalance
,
EnableModelFallback
:
updatedSettings
.
EnableModelFallback
,
FallbackModelAnthropic
:
updatedSettings
.
FallbackModelAnthropic
,
FallbackModelOpenAI
:
updatedSettings
.
FallbackModelOpenAI
,
FallbackModelGemini
:
updatedSettings
.
FallbackModelGemini
,
FallbackModelAntigravity
:
updatedSettings
.
FallbackModelAntigravity
,
})
})
}
}
// TestS
mtp
Request 测试SMTP连接请求
// TestS
MTP
Request 测试SMTP连接请求
type
TestS
mtp
Request
struct
{
type
TestS
MTP
Request
struct
{
S
mtp
Host
string
`json:"smtp_host" binding:"required"`
S
MTP
Host
string
`json:"smtp_host" binding:"required"`
S
mtp
Port
int
`json:"smtp_port"`
S
MTP
Port
int
`json:"smtp_port"`
S
mtp
Username
string
`json:"smtp_username"`
S
MTP
Username
string
`json:"smtp_username"`
S
mtp
Password
string
`json:"smtp_password"`
S
MTP
Password
string
`json:"smtp_password"`
S
mtp
UseTLS
bool
`json:"smtp_use_tls"`
S
MTP
UseTLS
bool
`json:"smtp_use_tls"`
}
}
// TestS
mtp
Connection 测试SMTP连接
// TestS
MTP
Connection 测试SMTP连接
// POST /api/v1/admin/settings/test-smtp
// POST /api/v1/admin/settings/test-smtp
func
(
h
*
SettingHandler
)
TestS
mtp
Connection
(
c
*
gin
.
Context
)
{
func
(
h
*
SettingHandler
)
TestS
MTP
Connection
(
c
*
gin
.
Context
)
{
var
req
TestS
mtp
Request
var
req
TestS
MTP
Request
if
err
:=
c
.
ShouldBindJSON
(
&
req
);
err
!=
nil
{
if
err
:=
c
.
ShouldBindJSON
(
&
req
);
err
!=
nil
{
response
.
BadRequest
(
c
,
"Invalid request: "
+
err
.
Error
())
response
.
BadRequest
(
c
,
"Invalid request: "
+
err
.
Error
())
return
return
}
}
if
req
.
S
mtp
Port
<=
0
{
if
req
.
S
MTP
Port
<=
0
{
req
.
S
mtp
Port
=
587
req
.
S
MTP
Port
=
587
}
}
// 如果未提供密码,从数据库获取已保存的密码
// 如果未提供密码,从数据库获取已保存的密码
password
:=
req
.
S
mtp
Password
password
:=
req
.
S
MTP
Password
if
password
==
""
{
if
password
==
""
{
savedConfig
,
err
:=
h
.
emailService
.
GetS
mtp
Config
(
c
.
Request
.
Context
())
savedConfig
,
err
:=
h
.
emailService
.
GetS
MTP
Config
(
c
.
Request
.
Context
())
if
err
==
nil
&&
savedConfig
!=
nil
{
if
err
==
nil
&&
savedConfig
!=
nil
{
password
=
savedConfig
.
Password
password
=
savedConfig
.
Password
}
}
}
}
config
:=
&
service
.
S
mtp
Config
{
config
:=
&
service
.
S
MTP
Config
{
Host
:
req
.
S
mtp
Host
,
Host
:
req
.
S
MTP
Host
,
Port
:
req
.
S
mtp
Port
,
Port
:
req
.
S
MTP
Port
,
Username
:
req
.
S
mtp
Username
,
Username
:
req
.
S
MTP
Username
,
Password
:
password
,
Password
:
password
,
UseTLS
:
req
.
S
mtp
UseTLS
,
UseTLS
:
req
.
S
MTP
UseTLS
,
}
}
err
:=
h
.
emailService
.
TestS
mtp
ConnectionWithConfig
(
config
)
err
:=
h
.
emailService
.
TestS
MTP
ConnectionWithConfig
(
config
)
if
err
!=
nil
{
if
err
!=
nil
{
response
.
ErrorFrom
(
c
,
err
)
response
.
ErrorFrom
(
c
,
err
)
return
return
...
@@ -250,13 +272,13 @@ func (h *SettingHandler) TestSmtpConnection(c *gin.Context) {
...
@@ -250,13 +272,13 @@ func (h *SettingHandler) TestSmtpConnection(c *gin.Context) {
// SendTestEmailRequest 发送测试邮件请求
// SendTestEmailRequest 发送测试邮件请求
type
SendTestEmailRequest
struct
{
type
SendTestEmailRequest
struct
{
Email
string
`json:"email" binding:"required,email"`
Email
string
`json:"email" binding:"required,email"`
S
mtp
Host
string
`json:"smtp_host" binding:"required"`
S
MTP
Host
string
`json:"smtp_host" binding:"required"`
S
mtp
Port
int
`json:"smtp_port"`
S
MTP
Port
int
`json:"smtp_port"`
S
mtp
Username
string
`json:"smtp_username"`
S
MTP
Username
string
`json:"smtp_username"`
S
mtp
Password
string
`json:"smtp_password"`
S
MTP
Password
string
`json:"smtp_password"`
S
mtp
From
string
`json:"smtp_from_email"`
S
MTP
From
string
`json:"smtp_from_email"`
S
mtp
FromName
string
`json:"smtp_from_name"`
S
MTP
FromName
string
`json:"smtp_from_name"`
S
mtp
UseTLS
bool
`json:"smtp_use_tls"`
S
MTP
UseTLS
bool
`json:"smtp_use_tls"`
}
}
// SendTestEmail 发送测试邮件
// SendTestEmail 发送测试邮件
...
@@ -268,27 +290,27 @@ func (h *SettingHandler) SendTestEmail(c *gin.Context) {
...
@@ -268,27 +290,27 @@ func (h *SettingHandler) SendTestEmail(c *gin.Context) {
return
return
}
}
if
req
.
S
mtp
Port
<=
0
{
if
req
.
S
MTP
Port
<=
0
{
req
.
S
mtp
Port
=
587
req
.
S
MTP
Port
=
587
}
}
// 如果未提供密码,从数据库获取已保存的密码
// 如果未提供密码,从数据库获取已保存的密码
password
:=
req
.
S
mtp
Password
password
:=
req
.
S
MTP
Password
if
password
==
""
{
if
password
==
""
{
savedConfig
,
err
:=
h
.
emailService
.
GetS
mtp
Config
(
c
.
Request
.
Context
())
savedConfig
,
err
:=
h
.
emailService
.
GetS
MTP
Config
(
c
.
Request
.
Context
())
if
err
==
nil
&&
savedConfig
!=
nil
{
if
err
==
nil
&&
savedConfig
!=
nil
{
password
=
savedConfig
.
Password
password
=
savedConfig
.
Password
}
}
}
}
config
:=
&
service
.
S
mtp
Config
{
config
:=
&
service
.
S
MTP
Config
{
Host
:
req
.
S
mtp
Host
,
Host
:
req
.
S
MTP
Host
,
Port
:
req
.
S
mtp
Port
,
Port
:
req
.
S
MTP
Port
,
Username
:
req
.
S
mtp
Username
,
Username
:
req
.
S
MTP
Username
,
Password
:
password
,
Password
:
password
,
From
:
req
.
S
mtp
From
,
From
:
req
.
S
MTP
From
,
FromName
:
req
.
S
mtp
FromName
,
FromName
:
req
.
S
MTP
FromName
,
UseTLS
:
req
.
S
mtp
UseTLS
,
UseTLS
:
req
.
S
MTP
UseTLS
,
}
}
siteName
:=
h
.
settingService
.
GetSiteName
(
c
.
Request
.
Context
())
siteName
:=
h
.
settingService
.
GetSiteName
(
c
.
Request
.
Context
())
...
@@ -333,10 +355,10 @@ func (h *SettingHandler) SendTestEmail(c *gin.Context) {
...
@@ -333,10 +355,10 @@ func (h *SettingHandler) SendTestEmail(c *gin.Context) {
response
.
Success
(
c
,
gin
.
H
{
"message"
:
"Test email sent successfully"
})
response
.
Success
(
c
,
gin
.
H
{
"message"
:
"Test email sent successfully"
})
}
}
// GetAdminA
pi
Key 获取管理员 API Key 状态
// GetAdminA
PI
Key 获取管理员 API Key 状态
// GET /api/v1/admin/settings/admin-api-key
// GET /api/v1/admin/settings/admin-api-key
func
(
h
*
SettingHandler
)
GetAdminA
pi
Key
(
c
*
gin
.
Context
)
{
func
(
h
*
SettingHandler
)
GetAdminA
PI
Key
(
c
*
gin
.
Context
)
{
maskedKey
,
exists
,
err
:=
h
.
settingService
.
GetAdminA
pi
KeyStatus
(
c
.
Request
.
Context
())
maskedKey
,
exists
,
err
:=
h
.
settingService
.
GetAdminA
PI
KeyStatus
(
c
.
Request
.
Context
())
if
err
!=
nil
{
if
err
!=
nil
{
response
.
ErrorFrom
(
c
,
err
)
response
.
ErrorFrom
(
c
,
err
)
return
return
...
@@ -348,10 +370,10 @@ func (h *SettingHandler) GetAdminApiKey(c *gin.Context) {
...
@@ -348,10 +370,10 @@ func (h *SettingHandler) GetAdminApiKey(c *gin.Context) {
})
})
}
}
// RegenerateAdminA
pi
Key 生成/重新生成管理员 API Key
// RegenerateAdminA
PI
Key 生成/重新生成管理员 API Key
// POST /api/v1/admin/settings/admin-api-key/regenerate
// POST /api/v1/admin/settings/admin-api-key/regenerate
func
(
h
*
SettingHandler
)
RegenerateAdminA
pi
Key
(
c
*
gin
.
Context
)
{
func
(
h
*
SettingHandler
)
RegenerateAdminA
PI
Key
(
c
*
gin
.
Context
)
{
key
,
err
:=
h
.
settingService
.
GenerateAdminA
pi
Key
(
c
.
Request
.
Context
())
key
,
err
:=
h
.
settingService
.
GenerateAdminA
PI
Key
(
c
.
Request
.
Context
())
if
err
!=
nil
{
if
err
!=
nil
{
response
.
ErrorFrom
(
c
,
err
)
response
.
ErrorFrom
(
c
,
err
)
return
return
...
@@ -362,10 +384,10 @@ func (h *SettingHandler) RegenerateAdminApiKey(c *gin.Context) {
...
@@ -362,10 +384,10 @@ func (h *SettingHandler) RegenerateAdminApiKey(c *gin.Context) {
})
})
}
}
// DeleteAdminA
pi
Key 删除管理员 API Key
// DeleteAdminA
PI
Key 删除管理员 API Key
// DELETE /api/v1/admin/settings/admin-api-key
// DELETE /api/v1/admin/settings/admin-api-key
func
(
h
*
SettingHandler
)
DeleteAdminA
pi
Key
(
c
*
gin
.
Context
)
{
func
(
h
*
SettingHandler
)
DeleteAdminA
PI
Key
(
c
*
gin
.
Context
)
{
if
err
:=
h
.
settingService
.
DeleteAdminA
pi
Key
(
c
.
Request
.
Context
());
err
!=
nil
{
if
err
:=
h
.
settingService
.
DeleteAdminA
PI
Key
(
c
.
Request
.
Context
());
err
!=
nil
{
response
.
ErrorFrom
(
c
,
err
)
response
.
ErrorFrom
(
c
,
err
)
return
return
}
}
...
...
backend/internal/handler/admin/usage_handler.go
View file @
c86d445c
...
@@ -17,14 +17,14 @@ import (
...
@@ -17,14 +17,14 @@ import (
// UsageHandler handles admin usage-related requests
// UsageHandler handles admin usage-related requests
type
UsageHandler
struct
{
type
UsageHandler
struct
{
usageService
*
service
.
UsageService
usageService
*
service
.
UsageService
apiKeyService
*
service
.
A
pi
KeyService
apiKeyService
*
service
.
A
PI
KeyService
adminService
service
.
AdminService
adminService
service
.
AdminService
}
}
// NewUsageHandler creates a new admin usage handler
// NewUsageHandler creates a new admin usage handler
func
NewUsageHandler
(
func
NewUsageHandler
(
usageService
*
service
.
UsageService
,
usageService
*
service
.
UsageService
,
apiKeyService
*
service
.
A
pi
KeyService
,
apiKeyService
*
service
.
A
PI
KeyService
,
adminService
service
.
AdminService
,
adminService
service
.
AdminService
,
)
*
UsageHandler
{
)
*
UsageHandler
{
return
&
UsageHandler
{
return
&
UsageHandler
{
...
@@ -125,7 +125,7 @@ func (h *UsageHandler) List(c *gin.Context) {
...
@@ -125,7 +125,7 @@ func (h *UsageHandler) List(c *gin.Context) {
params
:=
pagination
.
PaginationParams
{
Page
:
page
,
PageSize
:
pageSize
}
params
:=
pagination
.
PaginationParams
{
Page
:
page
,
PageSize
:
pageSize
}
filters
:=
usagestats
.
UsageLogFilters
{
filters
:=
usagestats
.
UsageLogFilters
{
UserID
:
userID
,
UserID
:
userID
,
A
pi
KeyID
:
apiKeyID
,
A
PI
KeyID
:
apiKeyID
,
AccountID
:
accountID
,
AccountID
:
accountID
,
GroupID
:
groupID
,
GroupID
:
groupID
,
Model
:
model
,
Model
:
model
,
...
@@ -207,7 +207,7 @@ func (h *UsageHandler) Stats(c *gin.Context) {
...
@@ -207,7 +207,7 @@ func (h *UsageHandler) Stats(c *gin.Context) {
}
}
if
apiKeyID
>
0
{
if
apiKeyID
>
0
{
stats
,
err
:=
h
.
usageService
.
GetStatsByA
pi
Key
(
c
.
Request
.
Context
(),
apiKeyID
,
startTime
,
endTime
)
stats
,
err
:=
h
.
usageService
.
GetStatsByA
PI
Key
(
c
.
Request
.
Context
(),
apiKeyID
,
startTime
,
endTime
)
if
err
!=
nil
{
if
err
!=
nil
{
response
.
ErrorFrom
(
c
,
err
)
response
.
ErrorFrom
(
c
,
err
)
return
return
...
@@ -269,9 +269,9 @@ func (h *UsageHandler) SearchUsers(c *gin.Context) {
...
@@ -269,9 +269,9 @@ func (h *UsageHandler) SearchUsers(c *gin.Context) {
response
.
Success
(
c
,
result
)
response
.
Success
(
c
,
result
)
}
}
// SearchA
pi
Keys handles searching API keys by user
// SearchA
PI
Keys handles searching API keys by user
// GET /api/v1/admin/usage/search-api-keys
// GET /api/v1/admin/usage/search-api-keys
func
(
h
*
UsageHandler
)
SearchA
pi
Keys
(
c
*
gin
.
Context
)
{
func
(
h
*
UsageHandler
)
SearchA
PI
Keys
(
c
*
gin
.
Context
)
{
userIDStr
:=
c
.
Query
(
"user_id"
)
userIDStr
:=
c
.
Query
(
"user_id"
)
keyword
:=
c
.
Query
(
"q"
)
keyword
:=
c
.
Query
(
"q"
)
...
@@ -285,22 +285,22 @@ func (h *UsageHandler) SearchApiKeys(c *gin.Context) {
...
@@ -285,22 +285,22 @@ func (h *UsageHandler) SearchApiKeys(c *gin.Context) {
userID
=
id
userID
=
id
}
}
keys
,
err
:=
h
.
apiKeyService
.
SearchA
pi
Keys
(
c
.
Request
.
Context
(),
userID
,
keyword
,
30
)
keys
,
err
:=
h
.
apiKeyService
.
SearchA
PI
Keys
(
c
.
Request
.
Context
(),
userID
,
keyword
,
30
)
if
err
!=
nil
{
if
err
!=
nil
{
response
.
ErrorFrom
(
c
,
err
)
response
.
ErrorFrom
(
c
,
err
)
return
return
}
}
// Return simplified API key list (only id and name)
// Return simplified API key list (only id and name)
type
SimpleA
pi
Key
struct
{
type
SimpleA
PI
Key
struct
{
ID
int64
`json:"id"`
ID
int64
`json:"id"`
Name
string
`json:"name"`
Name
string
`json:"name"`
UserID
int64
`json:"user_id"`
UserID
int64
`json:"user_id"`
}
}
result
:=
make
([]
SimpleA
pi
Key
,
len
(
keys
))
result
:=
make
([]
SimpleA
PI
Key
,
len
(
keys
))
for
i
,
k
:=
range
keys
{
for
i
,
k
:=
range
keys
{
result
[
i
]
=
SimpleA
pi
Key
{
result
[
i
]
=
SimpleA
PI
Key
{
ID
:
k
.
ID
,
ID
:
k
.
ID
,
Name
:
k
.
Name
,
Name
:
k
.
Name
,
UserID
:
k
.
UserID
,
UserID
:
k
.
UserID
,
...
...
backend/internal/handler/admin/user_handler.go
View file @
c86d445c
...
@@ -243,9 +243,9 @@ func (h *UserHandler) GetUserAPIKeys(c *gin.Context) {
...
@@ -243,9 +243,9 @@ func (h *UserHandler) GetUserAPIKeys(c *gin.Context) {
return
return
}
}
out
:=
make
([]
dto
.
A
pi
Key
,
0
,
len
(
keys
))
out
:=
make
([]
dto
.
A
PI
Key
,
0
,
len
(
keys
))
for
i
:=
range
keys
{
for
i
:=
range
keys
{
out
=
append
(
out
,
*
dto
.
A
pi
KeyFromService
(
&
keys
[
i
]))
out
=
append
(
out
,
*
dto
.
A
PI
KeyFromService
(
&
keys
[
i
]))
}
}
response
.
Paginated
(
c
,
out
,
total
,
page
,
pageSize
)
response
.
Paginated
(
c
,
out
,
total
,
page
,
pageSize
)
}
}
...
...
backend/internal/handler/api_key_handler.go
View file @
c86d445c
// Package handler provides HTTP request handlers for the application.
package
handler
package
handler
import
(
import
(
...
@@ -14,11 +15,11 @@ import (
...
@@ -14,11 +15,11 @@ import (
// APIKeyHandler handles API key-related requests
// APIKeyHandler handles API key-related requests
type
APIKeyHandler
struct
{
type
APIKeyHandler
struct
{
apiKeyService
*
service
.
A
pi
KeyService
apiKeyService
*
service
.
A
PI
KeyService
}
}
// NewAPIKeyHandler creates a new APIKeyHandler
// NewAPIKeyHandler creates a new APIKeyHandler
func
NewAPIKeyHandler
(
apiKeyService
*
service
.
A
pi
KeyService
)
*
APIKeyHandler
{
func
NewAPIKeyHandler
(
apiKeyService
*
service
.
A
PI
KeyService
)
*
APIKeyHandler
{
return
&
APIKeyHandler
{
return
&
APIKeyHandler
{
apiKeyService
:
apiKeyService
,
apiKeyService
:
apiKeyService
,
}
}
...
@@ -56,9 +57,9 @@ func (h *APIKeyHandler) List(c *gin.Context) {
...
@@ -56,9 +57,9 @@ func (h *APIKeyHandler) List(c *gin.Context) {
return
return
}
}
out
:=
make
([]
dto
.
A
pi
Key
,
0
,
len
(
keys
))
out
:=
make
([]
dto
.
A
PI
Key
,
0
,
len
(
keys
))
for
i
:=
range
keys
{
for
i
:=
range
keys
{
out
=
append
(
out
,
*
dto
.
A
pi
KeyFromService
(
&
keys
[
i
]))
out
=
append
(
out
,
*
dto
.
A
PI
KeyFromService
(
&
keys
[
i
]))
}
}
response
.
Paginated
(
c
,
out
,
result
.
Total
,
page
,
pageSize
)
response
.
Paginated
(
c
,
out
,
result
.
Total
,
page
,
pageSize
)
}
}
...
@@ -90,7 +91,7 @@ func (h *APIKeyHandler) GetByID(c *gin.Context) {
...
@@ -90,7 +91,7 @@ func (h *APIKeyHandler) GetByID(c *gin.Context) {
return
return
}
}
response
.
Success
(
c
,
dto
.
A
pi
KeyFromService
(
key
))
response
.
Success
(
c
,
dto
.
A
PI
KeyFromService
(
key
))
}
}
// Create handles creating a new API key
// Create handles creating a new API key
...
@@ -108,7 +109,7 @@ func (h *APIKeyHandler) Create(c *gin.Context) {
...
@@ -108,7 +109,7 @@ func (h *APIKeyHandler) Create(c *gin.Context) {
return
return
}
}
svcReq
:=
service
.
CreateA
pi
KeyRequest
{
svcReq
:=
service
.
CreateA
PI
KeyRequest
{
Name
:
req
.
Name
,
Name
:
req
.
Name
,
GroupID
:
req
.
GroupID
,
GroupID
:
req
.
GroupID
,
CustomKey
:
req
.
CustomKey
,
CustomKey
:
req
.
CustomKey
,
...
@@ -119,7 +120,7 @@ func (h *APIKeyHandler) Create(c *gin.Context) {
...
@@ -119,7 +120,7 @@ func (h *APIKeyHandler) Create(c *gin.Context) {
return
return
}
}
response
.
Success
(
c
,
dto
.
A
pi
KeyFromService
(
key
))
response
.
Success
(
c
,
dto
.
A
PI
KeyFromService
(
key
))
}
}
// Update handles updating an API key
// Update handles updating an API key
...
@@ -143,7 +144,7 @@ func (h *APIKeyHandler) Update(c *gin.Context) {
...
@@ -143,7 +144,7 @@ func (h *APIKeyHandler) Update(c *gin.Context) {
return
return
}
}
svcReq
:=
service
.
UpdateA
pi
KeyRequest
{}
svcReq
:=
service
.
UpdateA
PI
KeyRequest
{}
if
req
.
Name
!=
""
{
if
req
.
Name
!=
""
{
svcReq
.
Name
=
&
req
.
Name
svcReq
.
Name
=
&
req
.
Name
}
}
...
@@ -158,7 +159,7 @@ func (h *APIKeyHandler) Update(c *gin.Context) {
...
@@ -158,7 +159,7 @@ func (h *APIKeyHandler) Update(c *gin.Context) {
return
return
}
}
response
.
Success
(
c
,
dto
.
A
pi
KeyFromService
(
key
))
response
.
Success
(
c
,
dto
.
A
PI
KeyFromService
(
key
))
}
}
// Delete handles deleting an API key
// Delete handles deleting an API key
...
...
backend/internal/handler/dto/mappers.go
View file @
c86d445c
// Package dto provides data transfer objects for HTTP handlers.
package
dto
package
dto
import
"github.com/Wei-Shaw/sub2api/internal/service"
import
"github.com/Wei-Shaw/sub2api/internal/service"
...
@@ -26,11 +27,11 @@ func UserFromService(u *service.User) *User {
...
@@ -26,11 +27,11 @@ func UserFromService(u *service.User) *User {
return
nil
return
nil
}
}
out
:=
UserFromServiceShallow
(
u
)
out
:=
UserFromServiceShallow
(
u
)
if
len
(
u
.
A
pi
Keys
)
>
0
{
if
len
(
u
.
A
PI
Keys
)
>
0
{
out
.
A
pi
Keys
=
make
([]
A
pi
Key
,
0
,
len
(
u
.
A
pi
Keys
))
out
.
A
PI
Keys
=
make
([]
A
PI
Key
,
0
,
len
(
u
.
A
PI
Keys
))
for
i
:=
range
u
.
A
pi
Keys
{
for
i
:=
range
u
.
A
PI
Keys
{
k
:=
u
.
A
pi
Keys
[
i
]
k
:=
u
.
A
PI
Keys
[
i
]
out
.
A
pi
Keys
=
append
(
out
.
A
pi
Keys
,
*
A
pi
KeyFromService
(
&
k
))
out
.
A
PI
Keys
=
append
(
out
.
A
PI
Keys
,
*
A
PI
KeyFromService
(
&
k
))
}
}
}
}
if
len
(
u
.
Subscriptions
)
>
0
{
if
len
(
u
.
Subscriptions
)
>
0
{
...
@@ -43,11 +44,11 @@ func UserFromService(u *service.User) *User {
...
@@ -43,11 +44,11 @@ func UserFromService(u *service.User) *User {
return
out
return
out
}
}
func
A
pi
KeyFromService
(
k
*
service
.
A
pi
Key
)
*
A
pi
Key
{
func
A
PI
KeyFromService
(
k
*
service
.
A
PI
Key
)
*
A
PI
Key
{
if
k
==
nil
{
if
k
==
nil
{
return
nil
return
nil
}
}
return
&
A
pi
Key
{
return
&
A
PI
Key
{
ID
:
k
.
ID
,
ID
:
k
.
ID
,
UserID
:
k
.
UserID
,
UserID
:
k
.
UserID
,
Key
:
k
.
Key
,
Key
:
k
.
Key
,
...
@@ -103,28 +104,30 @@ func AccountFromServiceShallow(a *service.Account) *Account {
...
@@ -103,28 +104,30 @@ func AccountFromServiceShallow(a *service.Account) *Account {
return
nil
return
nil
}
}
return
&
Account
{
return
&
Account
{
ID
:
a
.
ID
,
ID
:
a
.
ID
,
Name
:
a
.
Name
,
Name
:
a
.
Name
,
Platform
:
a
.
Platform
,
Platform
:
a
.
Platform
,
Type
:
a
.
Type
,
Type
:
a
.
Type
,
Credentials
:
a
.
Credentials
,
Credentials
:
a
.
Credentials
,
Extra
:
a
.
Extra
,
Extra
:
a
.
Extra
,
ProxyID
:
a
.
ProxyID
,
ProxyID
:
a
.
ProxyID
,
Concurrency
:
a
.
Concurrency
,
Concurrency
:
a
.
Concurrency
,
Priority
:
a
.
Priority
,
Priority
:
a
.
Priority
,
Status
:
a
.
Status
,
Status
:
a
.
Status
,
ErrorMessage
:
a
.
ErrorMessage
,
ErrorMessage
:
a
.
ErrorMessage
,
LastUsedAt
:
a
.
LastUsedAt
,
LastUsedAt
:
a
.
LastUsedAt
,
CreatedAt
:
a
.
CreatedAt
,
CreatedAt
:
a
.
CreatedAt
,
UpdatedAt
:
a
.
UpdatedAt
,
UpdatedAt
:
a
.
UpdatedAt
,
Schedulable
:
a
.
Schedulable
,
Schedulable
:
a
.
Schedulable
,
RateLimitedAt
:
a
.
RateLimitedAt
,
RateLimitedAt
:
a
.
RateLimitedAt
,
RateLimitResetAt
:
a
.
RateLimitResetAt
,
RateLimitResetAt
:
a
.
RateLimitResetAt
,
OverloadUntil
:
a
.
OverloadUntil
,
OverloadUntil
:
a
.
OverloadUntil
,
SessionWindowStart
:
a
.
SessionWindowStart
,
TempUnschedulableUntil
:
a
.
TempUnschedulableUntil
,
SessionWindowEnd
:
a
.
SessionWindowEnd
,
TempUnschedulableReason
:
a
.
TempUnschedulableReason
,
SessionWindowStatus
:
a
.
SessionWindowStatus
,
SessionWindowStart
:
a
.
SessionWindowStart
,
GroupIDs
:
a
.
GroupIDs
,
SessionWindowEnd
:
a
.
SessionWindowEnd
,
SessionWindowStatus
:
a
.
SessionWindowStatus
,
GroupIDs
:
a
.
GroupIDs
,
}
}
}
}
...
@@ -220,7 +223,7 @@ func UsageLogFromService(l *service.UsageLog) *UsageLog {
...
@@ -220,7 +223,7 @@ func UsageLogFromService(l *service.UsageLog) *UsageLog {
return
&
UsageLog
{
return
&
UsageLog
{
ID
:
l
.
ID
,
ID
:
l
.
ID
,
UserID
:
l
.
UserID
,
UserID
:
l
.
UserID
,
A
pi
KeyID
:
l
.
A
pi
KeyID
,
A
PI
KeyID
:
l
.
A
PI
KeyID
,
AccountID
:
l
.
AccountID
,
AccountID
:
l
.
AccountID
,
RequestID
:
l
.
RequestID
,
RequestID
:
l
.
RequestID
,
Model
:
l
.
Model
,
Model
:
l
.
Model
,
...
@@ -245,7 +248,7 @@ func UsageLogFromService(l *service.UsageLog) *UsageLog {
...
@@ -245,7 +248,7 @@ func UsageLogFromService(l *service.UsageLog) *UsageLog {
FirstTokenMs
:
l
.
FirstTokenMs
,
FirstTokenMs
:
l
.
FirstTokenMs
,
CreatedAt
:
l
.
CreatedAt
,
CreatedAt
:
l
.
CreatedAt
,
User
:
UserFromServiceShallow
(
l
.
User
),
User
:
UserFromServiceShallow
(
l
.
User
),
A
pi
Key
:
A
pi
KeyFromService
(
l
.
A
pi
Key
),
A
PI
Key
:
A
PI
KeyFromService
(
l
.
A
PI
Key
),
Account
:
AccountFromService
(
l
.
Account
),
Account
:
AccountFromService
(
l
.
Account
),
Group
:
GroupFromServiceShallow
(
l
.
Group
),
Group
:
GroupFromServiceShallow
(
l
.
Group
),
Subscription
:
UserSubscriptionFromService
(
l
.
Subscription
),
Subscription
:
UserSubscriptionFromService
(
l
.
Subscription
),
...
...
backend/internal/handler/dto/settings.go
View file @
c86d445c
...
@@ -5,13 +5,13 @@ type SystemSettings struct {
...
@@ -5,13 +5,13 @@ type SystemSettings struct {
RegistrationEnabled
bool
`json:"registration_enabled"`
RegistrationEnabled
bool
`json:"registration_enabled"`
EmailVerifyEnabled
bool
`json:"email_verify_enabled"`
EmailVerifyEnabled
bool
`json:"email_verify_enabled"`
S
mtp
Host
string
`json:"smtp_host"`
S
MTP
Host
string
`json:"smtp_host"`
S
mtp
Port
int
`json:"smtp_port"`
S
MTP
Port
int
`json:"smtp_port"`
S
mtp
Username
string
`json:"smtp_username"`
S
MTP
Username
string
`json:"smtp_username"`
S
mtp
Password
string
`json:"smtp_password,omitempty"`
S
MTP
Password
string
`json:"smtp_password,omitempty"`
S
mtp
From
string
`json:"smtp_from_email"`
S
MTP
From
string
`json:"smtp_from_email"`
S
mtp
FromName
string
`json:"smtp_from_name"`
S
MTP
FromName
string
`json:"smtp_from_name"`
S
mtp
UseTLS
bool
`json:"smtp_use_tls"`
S
MTP
UseTLS
bool
`json:"smtp_use_tls"`
TurnstileEnabled
bool
`json:"turnstile_enabled"`
TurnstileEnabled
bool
`json:"turnstile_enabled"`
TurnstileSiteKey
string
`json:"turnstile_site_key"`
TurnstileSiteKey
string
`json:"turnstile_site_key"`
...
@@ -20,12 +20,19 @@ type SystemSettings struct {
...
@@ -20,12 +20,19 @@ type SystemSettings struct {
SiteName
string
`json:"site_name"`
SiteName
string
`json:"site_name"`
SiteLogo
string
`json:"site_logo"`
SiteLogo
string
`json:"site_logo"`
SiteSubtitle
string
`json:"site_subtitle"`
SiteSubtitle
string
`json:"site_subtitle"`
A
pi
BaseU
rl
string
`json:"api_base_url"`
A
PI
BaseU
RL
string
`json:"api_base_url"`
ContactInfo
string
`json:"contact_info"`
ContactInfo
string
`json:"contact_info"`
DocU
rl
string
`json:"doc_url"`
DocU
RL
string
`json:"doc_url"`
DefaultConcurrency
int
`json:"default_concurrency"`
DefaultConcurrency
int
`json:"default_concurrency"`
DefaultBalance
float64
`json:"default_balance"`
DefaultBalance
float64
`json:"default_balance"`
// Model fallback configuration
EnableModelFallback
bool
`json:"enable_model_fallback"`
FallbackModelAnthropic
string
`json:"fallback_model_anthropic"`
FallbackModelOpenAI
string
`json:"fallback_model_openai"`
FallbackModelGemini
string
`json:"fallback_model_gemini"`
FallbackModelAntigravity
string
`json:"fallback_model_antigravity"`
}
}
type
PublicSettings
struct
{
type
PublicSettings
struct
{
...
@@ -36,8 +43,8 @@ type PublicSettings struct {
...
@@ -36,8 +43,8 @@ type PublicSettings struct {
SiteName
string
`json:"site_name"`
SiteName
string
`json:"site_name"`
SiteLogo
string
`json:"site_logo"`
SiteLogo
string
`json:"site_logo"`
SiteSubtitle
string
`json:"site_subtitle"`
SiteSubtitle
string
`json:"site_subtitle"`
A
pi
BaseU
rl
string
`json:"api_base_url"`
A
PI
BaseU
RL
string
`json:"api_base_url"`
ContactInfo
string
`json:"contact_info"`
ContactInfo
string
`json:"contact_info"`
DocU
rl
string
`json:"doc_url"`
DocU
RL
string
`json:"doc_url"`
Version
string
`json:"version"`
Version
string
`json:"version"`
}
}
backend/internal/handler/dto/types.go
View file @
c86d445c
...
@@ -15,11 +15,11 @@ type User struct {
...
@@ -15,11 +15,11 @@ type User struct {
CreatedAt
time
.
Time
`json:"created_at"`
CreatedAt
time
.
Time
`json:"created_at"`
UpdatedAt
time
.
Time
`json:"updated_at"`
UpdatedAt
time
.
Time
`json:"updated_at"`
A
pi
Keys
[]
A
pi
Key
`json:"api_keys,omitempty"`
A
PI
Keys
[]
A
PI
Key
`json:"api_keys,omitempty"`
Subscriptions
[]
UserSubscription
`json:"subscriptions,omitempty"`
Subscriptions
[]
UserSubscription
`json:"subscriptions,omitempty"`
}
}
type
A
pi
Key
struct
{
type
A
PI
Key
struct
{
ID
int64
`json:"id"`
ID
int64
`json:"id"`
UserID
int64
`json:"user_id"`
UserID
int64
`json:"user_id"`
Key
string
`json:"key"`
Key
string
`json:"key"`
...
@@ -76,6 +76,9 @@ type Account struct {
...
@@ -76,6 +76,9 @@ type Account struct {
RateLimitResetAt
*
time
.
Time
`json:"rate_limit_reset_at"`
RateLimitResetAt
*
time
.
Time
`json:"rate_limit_reset_at"`
OverloadUntil
*
time
.
Time
`json:"overload_until"`
OverloadUntil
*
time
.
Time
`json:"overload_until"`
TempUnschedulableUntil
*
time
.
Time
`json:"temp_unschedulable_until"`
TempUnschedulableReason
string
`json:"temp_unschedulable_reason"`
SessionWindowStart
*
time
.
Time
`json:"session_window_start"`
SessionWindowStart
*
time
.
Time
`json:"session_window_start"`
SessionWindowEnd
*
time
.
Time
`json:"session_window_end"`
SessionWindowEnd
*
time
.
Time
`json:"session_window_end"`
SessionWindowStatus
string
`json:"session_window_status"`
SessionWindowStatus
string
`json:"session_window_status"`
...
@@ -136,7 +139,7 @@ type RedeemCode struct {
...
@@ -136,7 +139,7 @@ type RedeemCode struct {
type
UsageLog
struct
{
type
UsageLog
struct
{
ID
int64
`json:"id"`
ID
int64
`json:"id"`
UserID
int64
`json:"user_id"`
UserID
int64
`json:"user_id"`
A
pi
KeyID
int64
`json:"api_key_id"`
A
PI
KeyID
int64
`json:"api_key_id"`
AccountID
int64
`json:"account_id"`
AccountID
int64
`json:"account_id"`
RequestID
string
`json:"request_id"`
RequestID
string
`json:"request_id"`
Model
string
`json:"model"`
Model
string
`json:"model"`
...
@@ -168,7 +171,7 @@ type UsageLog struct {
...
@@ -168,7 +171,7 @@ type UsageLog struct {
CreatedAt
time
.
Time
`json:"created_at"`
CreatedAt
time
.
Time
`json:"created_at"`
User
*
User
`json:"user,omitempty"`
User
*
User
`json:"user,omitempty"`
A
pi
Key
*
A
pi
Key
`json:"api_key,omitempty"`
A
PI
Key
*
A
PI
Key
`json:"api_key,omitempty"`
Account
*
Account
`json:"account,omitempty"`
Account
*
Account
`json:"account,omitempty"`
Group
*
Group
`json:"group,omitempty"`
Group
*
Group
`json:"group,omitempty"`
Subscription
*
UserSubscription
`json:"subscription,omitempty"`
Subscription
*
UserSubscription
`json:"subscription,omitempty"`
...
...
backend/internal/handler/gateway_handler.go
View file @
c86d445c
...
@@ -53,7 +53,7 @@ func NewGatewayHandler(
...
@@ -53,7 +53,7 @@ func NewGatewayHandler(
// POST /v1/messages
// POST /v1/messages
func
(
h
*
GatewayHandler
)
Messages
(
c
*
gin
.
Context
)
{
func
(
h
*
GatewayHandler
)
Messages
(
c
*
gin
.
Context
)
{
// 从context获取apiKey和user(ApiKeyAuth中间件已设置)
// 从context获取apiKey和user(ApiKeyAuth中间件已设置)
apiKey
,
ok
:=
middleware2
.
GetA
pi
KeyFromContext
(
c
)
apiKey
,
ok
:=
middleware2
.
GetA
PI
KeyFromContext
(
c
)
if
!
ok
{
if
!
ok
{
h
.
errorResponse
(
c
,
http
.
StatusUnauthorized
,
"authentication_error"
,
"Invalid API key"
)
h
.
errorResponse
(
c
,
http
.
StatusUnauthorized
,
"authentication_error"
,
"Invalid API key"
)
return
return
...
@@ -259,7 +259,7 @@ func (h *GatewayHandler) Messages(c *gin.Context) {
...
@@ -259,7 +259,7 @@ func (h *GatewayHandler) Messages(c *gin.Context) {
defer
cancel
()
defer
cancel
()
if
err
:=
h
.
gatewayService
.
RecordUsage
(
ctx
,
&
service
.
RecordUsageInput
{
if
err
:=
h
.
gatewayService
.
RecordUsage
(
ctx
,
&
service
.
RecordUsageInput
{
Result
:
result
,
Result
:
result
,
A
pi
Key
:
apiKey
,
A
PI
Key
:
apiKey
,
User
:
apiKey
.
User
,
User
:
apiKey
.
User
,
Account
:
usedAccount
,
Account
:
usedAccount
,
Subscription
:
subscription
,
Subscription
:
subscription
,
...
@@ -383,7 +383,7 @@ func (h *GatewayHandler) Messages(c *gin.Context) {
...
@@ -383,7 +383,7 @@ func (h *GatewayHandler) Messages(c *gin.Context) {
defer
cancel
()
defer
cancel
()
if
err
:=
h
.
gatewayService
.
RecordUsage
(
ctx
,
&
service
.
RecordUsageInput
{
if
err
:=
h
.
gatewayService
.
RecordUsage
(
ctx
,
&
service
.
RecordUsageInput
{
Result
:
result
,
Result
:
result
,
A
pi
Key
:
apiKey
,
A
PI
Key
:
apiKey
,
User
:
apiKey
.
User
,
User
:
apiKey
.
User
,
Account
:
usedAccount
,
Account
:
usedAccount
,
Subscription
:
subscription
,
Subscription
:
subscription
,
...
@@ -400,7 +400,7 @@ func (h *GatewayHandler) Messages(c *gin.Context) {
...
@@ -400,7 +400,7 @@ func (h *GatewayHandler) Messages(c *gin.Context) {
// Returns models based on account configurations (model_mapping whitelist)
// Returns models based on account configurations (model_mapping whitelist)
// Falls back to default models if no whitelist is configured
// Falls back to default models if no whitelist is configured
func
(
h
*
GatewayHandler
)
Models
(
c
*
gin
.
Context
)
{
func
(
h
*
GatewayHandler
)
Models
(
c
*
gin
.
Context
)
{
apiKey
,
_
:=
middleware2
.
GetA
pi
KeyFromContext
(
c
)
apiKey
,
_
:=
middleware2
.
GetA
PI
KeyFromContext
(
c
)
var
groupID
*
int64
var
groupID
*
int64
var
platform
string
var
platform
string
...
@@ -458,7 +458,7 @@ func (h *GatewayHandler) AntigravityModels(c *gin.Context) {
...
@@ -458,7 +458,7 @@ func (h *GatewayHandler) AntigravityModels(c *gin.Context) {
// Usage handles getting account balance for CC Switch integration
// Usage handles getting account balance for CC Switch integration
// GET /v1/usage
// GET /v1/usage
func
(
h
*
GatewayHandler
)
Usage
(
c
*
gin
.
Context
)
{
func
(
h
*
GatewayHandler
)
Usage
(
c
*
gin
.
Context
)
{
apiKey
,
ok
:=
middleware2
.
GetA
pi
KeyFromContext
(
c
)
apiKey
,
ok
:=
middleware2
.
GetA
PI
KeyFromContext
(
c
)
if
!
ok
{
if
!
ok
{
h
.
errorResponse
(
c
,
http
.
StatusUnauthorized
,
"authentication_error"
,
"Invalid API key"
)
h
.
errorResponse
(
c
,
http
.
StatusUnauthorized
,
"authentication_error"
,
"Invalid API key"
)
return
return
...
@@ -628,7 +628,7 @@ func (h *GatewayHandler) errorResponse(c *gin.Context, status int, errType, mess
...
@@ -628,7 +628,7 @@ func (h *GatewayHandler) errorResponse(c *gin.Context, status int, errType, mess
// 特点:校验订阅/余额,但不计算并发、不记录使用量
// 特点:校验订阅/余额,但不计算并发、不记录使用量
func
(
h
*
GatewayHandler
)
CountTokens
(
c
*
gin
.
Context
)
{
func
(
h
*
GatewayHandler
)
CountTokens
(
c
*
gin
.
Context
)
{
// 从context获取apiKey和user(ApiKeyAuth中间件已设置)
// 从context获取apiKey和user(ApiKeyAuth中间件已设置)
apiKey
,
ok
:=
middleware2
.
GetA
pi
KeyFromContext
(
c
)
apiKey
,
ok
:=
middleware2
.
GetA
PI
KeyFromContext
(
c
)
if
!
ok
{
if
!
ok
{
h
.
errorResponse
(
c
,
http
.
StatusUnauthorized
,
"authentication_error"
,
"Invalid API key"
)
h
.
errorResponse
(
c
,
http
.
StatusUnauthorized
,
"authentication_error"
,
"Invalid API key"
)
return
return
...
...
backend/internal/handler/gemini_v1beta_handler.go
View file @
c86d445c
...
@@ -21,7 +21,7 @@ import (
...
@@ -21,7 +21,7 @@ import (
// GeminiV1BetaListModels proxies:
// GeminiV1BetaListModels proxies:
// GET /v1beta/models
// GET /v1beta/models
func
(
h
*
GatewayHandler
)
GeminiV1BetaListModels
(
c
*
gin
.
Context
)
{
func
(
h
*
GatewayHandler
)
GeminiV1BetaListModels
(
c
*
gin
.
Context
)
{
apiKey
,
ok
:=
middleware
.
GetA
pi
KeyFromContext
(
c
)
apiKey
,
ok
:=
middleware
.
GetA
PI
KeyFromContext
(
c
)
if
!
ok
||
apiKey
==
nil
{
if
!
ok
||
apiKey
==
nil
{
googleError
(
c
,
http
.
StatusUnauthorized
,
"Invalid API key"
)
googleError
(
c
,
http
.
StatusUnauthorized
,
"Invalid API key"
)
return
return
...
@@ -67,7 +67,7 @@ func (h *GatewayHandler) GeminiV1BetaListModels(c *gin.Context) {
...
@@ -67,7 +67,7 @@ func (h *GatewayHandler) GeminiV1BetaListModels(c *gin.Context) {
// GeminiV1BetaGetModel proxies:
// GeminiV1BetaGetModel proxies:
// GET /v1beta/models/{model}
// GET /v1beta/models/{model}
func
(
h
*
GatewayHandler
)
GeminiV1BetaGetModel
(
c
*
gin
.
Context
)
{
func
(
h
*
GatewayHandler
)
GeminiV1BetaGetModel
(
c
*
gin
.
Context
)
{
apiKey
,
ok
:=
middleware
.
GetA
pi
KeyFromContext
(
c
)
apiKey
,
ok
:=
middleware
.
GetA
PI
KeyFromContext
(
c
)
if
!
ok
||
apiKey
==
nil
{
if
!
ok
||
apiKey
==
nil
{
googleError
(
c
,
http
.
StatusUnauthorized
,
"Invalid API key"
)
googleError
(
c
,
http
.
StatusUnauthorized
,
"Invalid API key"
)
return
return
...
@@ -120,7 +120,7 @@ func (h *GatewayHandler) GeminiV1BetaGetModel(c *gin.Context) {
...
@@ -120,7 +120,7 @@ func (h *GatewayHandler) GeminiV1BetaGetModel(c *gin.Context) {
// POST /v1beta/models/{model}:generateContent
// POST /v1beta/models/{model}:generateContent
// POST /v1beta/models/{model}:streamGenerateContent?alt=sse
// POST /v1beta/models/{model}:streamGenerateContent?alt=sse
func
(
h
*
GatewayHandler
)
GeminiV1BetaModels
(
c
*
gin
.
Context
)
{
func
(
h
*
GatewayHandler
)
GeminiV1BetaModels
(
c
*
gin
.
Context
)
{
apiKey
,
ok
:=
middleware
.
GetA
pi
KeyFromContext
(
c
)
apiKey
,
ok
:=
middleware
.
GetA
PI
KeyFromContext
(
c
)
if
!
ok
||
apiKey
==
nil
{
if
!
ok
||
apiKey
==
nil
{
googleError
(
c
,
http
.
StatusUnauthorized
,
"Invalid API key"
)
googleError
(
c
,
http
.
StatusUnauthorized
,
"Invalid API key"
)
return
return
...
@@ -299,7 +299,7 @@ func (h *GatewayHandler) GeminiV1BetaModels(c *gin.Context) {
...
@@ -299,7 +299,7 @@ func (h *GatewayHandler) GeminiV1BetaModels(c *gin.Context) {
defer
cancel
()
defer
cancel
()
if
err
:=
h
.
gatewayService
.
RecordUsage
(
ctx
,
&
service
.
RecordUsageInput
{
if
err
:=
h
.
gatewayService
.
RecordUsage
(
ctx
,
&
service
.
RecordUsageInput
{
Result
:
result
,
Result
:
result
,
A
pi
Key
:
apiKey
,
A
PI
Key
:
apiKey
,
User
:
apiKey
.
User
,
User
:
apiKey
.
User
,
Account
:
usedAccount
,
Account
:
usedAccount
,
Subscription
:
subscription
,
Subscription
:
subscription
,
...
...
backend/internal/handler/openai_gateway_handler.go
View file @
c86d445c
...
@@ -41,7 +41,7 @@ func NewOpenAIGatewayHandler(
...
@@ -41,7 +41,7 @@ func NewOpenAIGatewayHandler(
// POST /openai/v1/responses
// POST /openai/v1/responses
func
(
h
*
OpenAIGatewayHandler
)
Responses
(
c
*
gin
.
Context
)
{
func
(
h
*
OpenAIGatewayHandler
)
Responses
(
c
*
gin
.
Context
)
{
// Get apiKey and user from context (set by ApiKeyAuth middleware)
// Get apiKey and user from context (set by ApiKeyAuth middleware)
apiKey
,
ok
:=
middleware2
.
GetA
pi
KeyFromContext
(
c
)
apiKey
,
ok
:=
middleware2
.
GetA
PI
KeyFromContext
(
c
)
if
!
ok
{
if
!
ok
{
h
.
errorResponse
(
c
,
http
.
StatusUnauthorized
,
"authentication_error"
,
"Invalid API key"
)
h
.
errorResponse
(
c
,
http
.
StatusUnauthorized
,
"authentication_error"
,
"Invalid API key"
)
return
return
...
@@ -235,7 +235,7 @@ func (h *OpenAIGatewayHandler) Responses(c *gin.Context) {
...
@@ -235,7 +235,7 @@ func (h *OpenAIGatewayHandler) Responses(c *gin.Context) {
defer
cancel
()
defer
cancel
()
if
err
:=
h
.
gatewayService
.
RecordUsage
(
ctx
,
&
service
.
OpenAIRecordUsageInput
{
if
err
:=
h
.
gatewayService
.
RecordUsage
(
ctx
,
&
service
.
OpenAIRecordUsageInput
{
Result
:
result
,
Result
:
result
,
A
pi
Key
:
apiKey
,
A
PI
Key
:
apiKey
,
User
:
apiKey
.
User
,
User
:
apiKey
.
User
,
Account
:
usedAccount
,
Account
:
usedAccount
,
Subscription
:
subscription
,
Subscription
:
subscription
,
...
...
backend/internal/handler/setting_handler.go
View file @
c86d445c
...
@@ -39,9 +39,9 @@ func (h *SettingHandler) GetPublicSettings(c *gin.Context) {
...
@@ -39,9 +39,9 @@ func (h *SettingHandler) GetPublicSettings(c *gin.Context) {
SiteName
:
settings
.
SiteName
,
SiteName
:
settings
.
SiteName
,
SiteLogo
:
settings
.
SiteLogo
,
SiteLogo
:
settings
.
SiteLogo
,
SiteSubtitle
:
settings
.
SiteSubtitle
,
SiteSubtitle
:
settings
.
SiteSubtitle
,
A
pi
BaseU
rl
:
settings
.
A
pi
BaseU
rl
,
A
PI
BaseU
RL
:
settings
.
A
PI
BaseU
RL
,
ContactInfo
:
settings
.
ContactInfo
,
ContactInfo
:
settings
.
ContactInfo
,
DocU
rl
:
settings
.
DocU
rl
,
DocU
RL
:
settings
.
DocU
RL
,
Version
:
h
.
version
,
Version
:
h
.
version
,
})
})
}
}
backend/internal/handler/usage_handler.go
View file @
c86d445c
...
@@ -18,11 +18,11 @@ import (
...
@@ -18,11 +18,11 @@ import (
// UsageHandler handles usage-related requests
// UsageHandler handles usage-related requests
type
UsageHandler
struct
{
type
UsageHandler
struct
{
usageService
*
service
.
UsageService
usageService
*
service
.
UsageService
apiKeyService
*
service
.
A
pi
KeyService
apiKeyService
*
service
.
A
PI
KeyService
}
}
// NewUsageHandler creates a new UsageHandler
// NewUsageHandler creates a new UsageHandler
func
NewUsageHandler
(
usageService
*
service
.
UsageService
,
apiKeyService
*
service
.
A
pi
KeyService
)
*
UsageHandler
{
func
NewUsageHandler
(
usageService
*
service
.
UsageService
,
apiKeyService
*
service
.
A
PI
KeyService
)
*
UsageHandler
{
return
&
UsageHandler
{
return
&
UsageHandler
{
usageService
:
usageService
,
usageService
:
usageService
,
apiKeyService
:
apiKeyService
,
apiKeyService
:
apiKeyService
,
...
@@ -111,7 +111,7 @@ func (h *UsageHandler) List(c *gin.Context) {
...
@@ -111,7 +111,7 @@ func (h *UsageHandler) List(c *gin.Context) {
params
:=
pagination
.
PaginationParams
{
Page
:
page
,
PageSize
:
pageSize
}
params
:=
pagination
.
PaginationParams
{
Page
:
page
,
PageSize
:
pageSize
}
filters
:=
usagestats
.
UsageLogFilters
{
filters
:=
usagestats
.
UsageLogFilters
{
UserID
:
subject
.
UserID
,
// Always filter by current user for security
UserID
:
subject
.
UserID
,
// Always filter by current user for security
A
pi
KeyID
:
apiKeyID
,
A
PI
KeyID
:
apiKeyID
,
Model
:
model
,
Model
:
model
,
Stream
:
stream
,
Stream
:
stream
,
BillingType
:
billingType
,
BillingType
:
billingType
,
...
@@ -235,7 +235,7 @@ func (h *UsageHandler) Stats(c *gin.Context) {
...
@@ -235,7 +235,7 @@ func (h *UsageHandler) Stats(c *gin.Context) {
var
stats
*
service
.
UsageStats
var
stats
*
service
.
UsageStats
var
err
error
var
err
error
if
apiKeyID
>
0
{
if
apiKeyID
>
0
{
stats
,
err
=
h
.
usageService
.
GetStatsByA
pi
Key
(
c
.
Request
.
Context
(),
apiKeyID
,
startTime
,
endTime
)
stats
,
err
=
h
.
usageService
.
GetStatsByA
PI
Key
(
c
.
Request
.
Context
(),
apiKeyID
,
startTime
,
endTime
)
}
else
{
}
else
{
stats
,
err
=
h
.
usageService
.
GetStatsByUser
(
c
.
Request
.
Context
(),
subject
.
UserID
,
startTime
,
endTime
)
stats
,
err
=
h
.
usageService
.
GetStatsByUser
(
c
.
Request
.
Context
(),
subject
.
UserID
,
startTime
,
endTime
)
}
}
...
@@ -346,49 +346,49 @@ func (h *UsageHandler) DashboardModels(c *gin.Context) {
...
@@ -346,49 +346,49 @@ func (h *UsageHandler) DashboardModels(c *gin.Context) {
})
})
}
}
// BatchA
pi
KeysUsageRequest represents the request for batch API keys usage
// BatchA
PI
KeysUsageRequest represents the request for batch API keys usage
type
BatchA
pi
KeysUsageRequest
struct
{
type
BatchA
PI
KeysUsageRequest
struct
{
A
pi
KeyIDs
[]
int64
`json:"api_key_ids" binding:"required"`
A
PI
KeyIDs
[]
int64
`json:"api_key_ids" binding:"required"`
}
}
// DashboardA
pi
KeysUsage handles getting usage stats for user's own API keys
// DashboardA
PI
KeysUsage handles getting usage stats for user's own API keys
// POST /api/v1/usage/dashboard/api-keys-usage
// POST /api/v1/usage/dashboard/api-keys-usage
func
(
h
*
UsageHandler
)
DashboardA
pi
KeysUsage
(
c
*
gin
.
Context
)
{
func
(
h
*
UsageHandler
)
DashboardA
PI
KeysUsage
(
c
*
gin
.
Context
)
{
subject
,
ok
:=
middleware2
.
GetAuthSubjectFromContext
(
c
)
subject
,
ok
:=
middleware2
.
GetAuthSubjectFromContext
(
c
)
if
!
ok
{
if
!
ok
{
response
.
Unauthorized
(
c
,
"User not authenticated"
)
response
.
Unauthorized
(
c
,
"User not authenticated"
)
return
return
}
}
var
req
BatchA
pi
KeysUsageRequest
var
req
BatchA
PI
KeysUsageRequest
if
err
:=
c
.
ShouldBindJSON
(
&
req
);
err
!=
nil
{
if
err
:=
c
.
ShouldBindJSON
(
&
req
);
err
!=
nil
{
response
.
BadRequest
(
c
,
"Invalid request: "
+
err
.
Error
())
response
.
BadRequest
(
c
,
"Invalid request: "
+
err
.
Error
())
return
return
}
}
if
len
(
req
.
A
pi
KeyIDs
)
==
0
{
if
len
(
req
.
A
PI
KeyIDs
)
==
0
{
response
.
Success
(
c
,
gin
.
H
{
"stats"
:
map
[
string
]
any
{}})
response
.
Success
(
c
,
gin
.
H
{
"stats"
:
map
[
string
]
any
{}})
return
return
}
}
// Limit the number of API key IDs to prevent SQL parameter overflow
// Limit the number of API key IDs to prevent SQL parameter overflow
if
len
(
req
.
A
pi
KeyIDs
)
>
100
{
if
len
(
req
.
A
PI
KeyIDs
)
>
100
{
response
.
BadRequest
(
c
,
"Too many API key IDs (maximum 100 allowed)"
)
response
.
BadRequest
(
c
,
"Too many API key IDs (maximum 100 allowed)"
)
return
return
}
}
validA
pi
KeyIDs
,
err
:=
h
.
apiKeyService
.
VerifyOwnership
(
c
.
Request
.
Context
(),
subject
.
UserID
,
req
.
A
pi
KeyIDs
)
validA
PI
KeyIDs
,
err
:=
h
.
apiKeyService
.
VerifyOwnership
(
c
.
Request
.
Context
(),
subject
.
UserID
,
req
.
A
PI
KeyIDs
)
if
err
!=
nil
{
if
err
!=
nil
{
response
.
ErrorFrom
(
c
,
err
)
response
.
ErrorFrom
(
c
,
err
)
return
return
}
}
if
len
(
validA
pi
KeyIDs
)
==
0
{
if
len
(
validA
PI
KeyIDs
)
==
0
{
response
.
Success
(
c
,
gin
.
H
{
"stats"
:
map
[
string
]
any
{}})
response
.
Success
(
c
,
gin
.
H
{
"stats"
:
map
[
string
]
any
{}})
return
return
}
}
stats
,
err
:=
h
.
usageService
.
GetBatchA
pi
KeyUsageStats
(
c
.
Request
.
Context
(),
validA
pi
KeyIDs
)
stats
,
err
:=
h
.
usageService
.
GetBatchA
PI
KeyUsageStats
(
c
.
Request
.
Context
(),
validA
PI
KeyIDs
)
if
err
!=
nil
{
if
err
!=
nil
{
response
.
ErrorFrom
(
c
,
err
)
response
.
ErrorFrom
(
c
,
err
)
return
return
...
...
Prev
1
2
3
4
5
6
7
…
10
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