Commit c86d445c authored by IanShaw027's avatar IanShaw027
Browse files

fix(frontend): sync with main and finalize i18n & component optimizations

parents 6c036d7b e78c8646
......@@ -166,14 +166,14 @@ func (_c *UserCreate) SetNillableNotes(v *string) *UserCreate {
return _c
}
// AddAPIKeyIDs adds the "api_keys" edge to the ApiKey entity by IDs.
// AddAPIKeyIDs adds the "api_keys" edge to the APIKey entity by IDs.
func (_c *UserCreate) AddAPIKeyIDs(ids ...int64) *UserCreate {
_c.mutation.AddAPIKeyIDs(ids...)
return _c
}
// AddAPIKeys adds the "api_keys" edges to the ApiKey entity.
func (_c *UserCreate) AddAPIKeys(v ...*ApiKey) *UserCreate {
// AddAPIKeys adds the "api_keys" edges to the APIKey entity.
func (_c *UserCreate) AddAPIKeys(v ...*APIKey) *UserCreate {
ids := make([]int64, len(v))
for i := range v {
ids[i] = v[i].ID
......
......@@ -30,7 +30,7 @@ type UserQuery struct {
order []user.OrderOption
inters []Interceptor
predicates []predicate.User
withAPIKeys *ApiKeyQuery
withAPIKeys *APIKeyQuery
withRedeemCodes *RedeemCodeQuery
withSubscriptions *UserSubscriptionQuery
withAssignedSubscriptions *UserSubscriptionQuery
......@@ -75,8 +75,8 @@ func (_q *UserQuery) Order(o ...user.OrderOption) *UserQuery {
}
// QueryAPIKeys chains the current query on the "api_keys" edge.
func (_q *UserQuery) QueryAPIKeys() *ApiKeyQuery {
query := (&ApiKeyClient{config: _q.config}).Query()
func (_q *UserQuery) QueryAPIKeys() *APIKeyQuery {
query := (&APIKeyClient{config: _q.config}).Query()
query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
if err := _q.prepareQuery(ctx); err != nil {
return nil, err
......@@ -458,8 +458,8 @@ func (_q *UserQuery) Clone() *UserQuery {
// 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.
func (_q *UserQuery) WithAPIKeys(opts ...func(*ApiKeyQuery)) *UserQuery {
query := (&ApiKeyClient{config: _q.config}).Query()
func (_q *UserQuery) WithAPIKeys(opts ...func(*APIKeyQuery)) *UserQuery {
query := (&APIKeyClient{config: _q.config}).Query()
for _, opt := range opts {
opt(query)
}
......@@ -653,8 +653,8 @@ func (_q *UserQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*User, e
}
if query := _q.withAPIKeys; query != nil {
if err := _q.loadAPIKeys(ctx, query, nodes,
func(n *User) { n.Edges.APIKeys = []*ApiKey{} },
func(n *User, e *ApiKey) { n.Edges.APIKeys = append(n.Edges.APIKeys, e) }); err != nil {
func(n *User) { n.Edges.APIKeys = []*APIKey{} },
func(n *User, e *APIKey) { n.Edges.APIKeys = append(n.Edges.APIKeys, e) }); err != nil {
return nil, err
}
}
......@@ -712,7 +712,7 @@ func (_q *UserQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*User, e
return nodes, nil
}
func (_q *UserQuery) loadAPIKeys(ctx context.Context, query *ApiKeyQuery, nodes []*User, init func(*User), assign func(*User, *ApiKey)) error {
func (_q *UserQuery) loadAPIKeys(ctx context.Context, query *APIKeyQuery, nodes []*User, init func(*User), assign func(*User, *APIKey)) error {
fks := make([]driver.Value, 0, len(nodes))
nodeids := make(map[int64]*User)
for i := range nodes {
......@@ -725,7 +725,7 @@ func (_q *UserQuery) loadAPIKeys(ctx context.Context, query *ApiKeyQuery, nodes
if len(query.ctx.Fields) > 0 {
query.ctx.AppendFieldOnce(apikey.FieldUserID)
}
query.Where(predicate.ApiKey(func(s *sql.Selector) {
query.Where(predicate.APIKey(func(s *sql.Selector) {
s.Where(sql.InValues(s.C(user.APIKeysColumn), fks...))
}))
neighbors, err := query.All(ctx)
......
......@@ -186,14 +186,14 @@ func (_u *UserUpdate) SetNillableNotes(v *string) *UserUpdate {
return _u
}
// AddAPIKeyIDs adds the "api_keys" edge to the ApiKey entity by IDs.
// AddAPIKeyIDs adds the "api_keys" edge to the APIKey entity by IDs.
func (_u *UserUpdate) AddAPIKeyIDs(ids ...int64) *UserUpdate {
_u.mutation.AddAPIKeyIDs(ids...)
return _u
}
// AddAPIKeys adds the "api_keys" edges to the ApiKey entity.
func (_u *UserUpdate) AddAPIKeys(v ...*ApiKey) *UserUpdate {
// AddAPIKeys adds the "api_keys" edges to the APIKey entity.
func (_u *UserUpdate) AddAPIKeys(v ...*APIKey) *UserUpdate {
ids := make([]int64, len(v))
for i := range v {
ids[i] = v[i].ID
......@@ -296,20 +296,20 @@ func (_u *UserUpdate) Mutation() *UserMutation {
return _u.mutation
}
// ClearAPIKeys clears all "api_keys" edges to the ApiKey entity.
// ClearAPIKeys clears all "api_keys" edges to the APIKey entity.
func (_u *UserUpdate) ClearAPIKeys() *UserUpdate {
_u.mutation.ClearAPIKeys()
return _u
}
// RemoveAPIKeyIDs removes the "api_keys" edge to ApiKey entities by IDs.
// RemoveAPIKeyIDs removes the "api_keys" edge to APIKey entities by IDs.
func (_u *UserUpdate) RemoveAPIKeyIDs(ids ...int64) *UserUpdate {
_u.mutation.RemoveAPIKeyIDs(ids...)
return _u
}
// RemoveAPIKeys removes "api_keys" edges to ApiKey entities.
func (_u *UserUpdate) RemoveAPIKeys(v ...*ApiKey) *UserUpdate {
// RemoveAPIKeys removes "api_keys" edges to APIKey entities.
func (_u *UserUpdate) RemoveAPIKeys(v ...*APIKey) *UserUpdate {
ids := make([]int64, len(v))
for i := range v {
ids[i] = v[i].ID
......@@ -1065,14 +1065,14 @@ func (_u *UserUpdateOne) SetNillableNotes(v *string) *UserUpdateOne {
return _u
}
// AddAPIKeyIDs adds the "api_keys" edge to the ApiKey entity by IDs.
// AddAPIKeyIDs adds the "api_keys" edge to the APIKey entity by IDs.
func (_u *UserUpdateOne) AddAPIKeyIDs(ids ...int64) *UserUpdateOne {
_u.mutation.AddAPIKeyIDs(ids...)
return _u
}
// AddAPIKeys adds the "api_keys" edges to the ApiKey entity.
func (_u *UserUpdateOne) AddAPIKeys(v ...*ApiKey) *UserUpdateOne {
// AddAPIKeys adds the "api_keys" edges to the APIKey entity.
func (_u *UserUpdateOne) AddAPIKeys(v ...*APIKey) *UserUpdateOne {
ids := make([]int64, len(v))
for i := range v {
ids[i] = v[i].ID
......@@ -1175,20 +1175,20 @@ func (_u *UserUpdateOne) Mutation() *UserMutation {
return _u.mutation
}
// ClearAPIKeys clears all "api_keys" edges to the ApiKey entity.
// ClearAPIKeys clears all "api_keys" edges to the APIKey entity.
func (_u *UserUpdateOne) ClearAPIKeys() *UserUpdateOne {
_u.mutation.ClearAPIKeys()
return _u
}
// RemoveAPIKeyIDs removes the "api_keys" edge to ApiKey entities by IDs.
// RemoveAPIKeyIDs removes the "api_keys" edge to APIKey entities by IDs.
func (_u *UserUpdateOne) RemoveAPIKeyIDs(ids ...int64) *UserUpdateOne {
_u.mutation.RemoveAPIKeyIDs(ids...)
return _u
}
// RemoveAPIKeys removes "api_keys" edges to ApiKey entities.
func (_u *UserUpdateOne) RemoveAPIKeys(v ...*ApiKey) *UserUpdateOne {
// RemoveAPIKeys removes "api_keys" edges to APIKey entities.
func (_u *UserUpdateOne) RemoveAPIKeys(v ...*APIKey) *UserUpdateOne {
ids := make([]int64, len(v))
for i := range v {
ids[i] = v[i].ID
......
// Package config provides configuration loading, defaults, and validation.
package config
import (
......@@ -139,7 +140,7 @@ type GatewayConfig struct {
LogUpstreamErrorBodyMaxBytes int `mapstructure:"log_upstream_error_body_max_bytes"`
// API-key 账号在客户端未提供 anthropic-beta 时,是否按需自动补齐(默认关闭以保持兼容)
InjectBetaForApiKey bool `mapstructure:"inject_beta_for_apikey"`
InjectBetaForAPIKey bool `mapstructure:"inject_beta_for_apikey"`
// 是否允许对部分 400 错误触发 failover(默认关闭以避免改变语义)
FailoverOn400 bool `mapstructure:"failover_on_400"`
......@@ -241,7 +242,7 @@ type DefaultConfig struct {
AdminPassword string `mapstructure:"admin_password"`
UserConcurrency int `mapstructure:"user_concurrency"`
UserBalance float64 `mapstructure:"user_balance"`
ApiKeyPrefix string `mapstructure:"api_key_prefix"`
APIKeyPrefix string `mapstructure:"api_key_prefix"`
RateMultiplier float64 `mapstructure:"rate_multiplier"`
}
......
......@@ -75,8 +75,8 @@ func (h *DashboardHandler) GetStats(c *gin.Context) {
"active_users": stats.ActiveUsers,
// API Key 统计
"total_api_keys": stats.TotalApiKeys,
"active_api_keys": stats.ActiveApiKeys,
"total_api_keys": stats.TotalAPIKeys,
"active_api_keys": stats.ActiveAPIKeys,
// 账户统计
"total_accounts": stats.TotalAccounts,
......@@ -193,10 +193,10 @@ func (h *DashboardHandler) GetModelStats(c *gin.Context) {
})
}
// GetApiKeyUsageTrend handles getting API key usage trend data
// GetAPIKeyUsageTrend handles getting API key usage trend data
// GET /api/v1/admin/dashboard/api-keys-trend
// Query params: start_date, end_date (YYYY-MM-DD), granularity (day/hour), limit (default 5)
func (h *DashboardHandler) GetApiKeyUsageTrend(c *gin.Context) {
func (h *DashboardHandler) GetAPIKeyUsageTrend(c *gin.Context) {
startTime, endTime := parseTimeRange(c)
granularity := c.DefaultQuery("granularity", "day")
limitStr := c.DefaultQuery("limit", "5")
......@@ -205,7 +205,7 @@ func (h *DashboardHandler) GetApiKeyUsageTrend(c *gin.Context) {
limit = 5
}
trend, err := h.dashboardService.GetApiKeyUsageTrend(c.Request.Context(), startTime, endTime, granularity, limit)
trend, err := h.dashboardService.GetAPIKeyUsageTrend(c.Request.Context(), startTime, endTime, granularity, limit)
if err != nil {
response.Error(c, 500, "Failed to get API key usage trend")
return
......@@ -273,26 +273,26 @@ func (h *DashboardHandler) GetBatchUsersUsage(c *gin.Context) {
response.Success(c, gin.H{"stats": stats})
}
// BatchApiKeysUsageRequest represents the request body for batch api key usage stats
type BatchApiKeysUsageRequest struct {
ApiKeyIDs []int64 `json:"api_key_ids" binding:"required"`
// BatchAPIKeysUsageRequest represents the request body for batch api key usage stats
type BatchAPIKeysUsageRequest struct {
APIKeyIDs []int64 `json:"api_key_ids" binding:"required"`
}
// GetBatchApiKeysUsage handles getting usage stats for multiple API keys
// GetBatchAPIKeysUsage handles getting usage stats for multiple API keys
// POST /api/v1/admin/dashboard/api-keys-usage
func (h *DashboardHandler) GetBatchApiKeysUsage(c *gin.Context) {
var req BatchApiKeysUsageRequest
func (h *DashboardHandler) GetBatchAPIKeysUsage(c *gin.Context) {
var req BatchAPIKeysUsageRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, "Invalid request: "+err.Error())
return
}
if len(req.ApiKeyIDs) == 0 {
if len(req.APIKeyIDs) == 0 {
response.Success(c, gin.H{"stats": map[string]any{}})
return
}
stats, err := h.dashboardService.GetBatchApiKeyUsageStats(c.Request.Context(), req.ApiKeyIDs)
stats, err := h.dashboardService.GetBatchAPIKeyUsageStats(c.Request.Context(), req.APIKeyIDs)
if err != nil {
response.Error(c, 500, "Failed to get API key usage stats")
return
......
......@@ -18,6 +18,7 @@ func NewGeminiOAuthHandler(geminiOAuthService *service.GeminiOAuthService) *Gemi
return &GeminiOAuthHandler{geminiOAuthService: geminiOAuthService}
}
// GetCapabilities returns the Gemini OAuth configuration capabilities.
// GET /api/v1/admin/gemini/oauth/capabilities
func (h *GeminiOAuthHandler) GetCapabilities(c *gin.Context) {
cfg := h.geminiOAuthService.GetOAuthConfig()
......@@ -30,6 +31,8 @@ type GeminiGenerateAuthURLRequest struct {
// OAuth 类型: "code_assist" (需要 project_id) 或 "ai_studio" (不需要 project_id)
// 默认为 "code_assist" 以保持向后兼容
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.
......@@ -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
// oauth_type and whether the built-in Gemini CLI OAuth client is used.
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 {
msg := err.Error()
// Treat missing/invalid OAuth client configuration as a user/config error.
......@@ -76,6 +79,9 @@ type GeminiExchangeCodeRequest struct {
ProxyID *int64 `json:"proxy_id"`
// OAuth 类型: "code_assist" 或 "ai_studio",需要与 GenerateAuthURL 时的类型一致
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.
......@@ -103,6 +109,7 @@ func (h *GeminiOAuthHandler) ExchangeCode(c *gin.Context) {
Code: req.Code,
ProxyID: req.ProxyID,
OAuthType: oauthType,
TierID: req.TierID,
})
if err != nil {
response.BadRequest(c, "Failed to exchange code: "+err.Error())
......
......@@ -237,9 +237,9 @@ func (h *GroupHandler) GetGroupAPIKeys(c *gin.Context) {
return
}
outKeys := make([]dto.ApiKey, 0, len(keys))
outKeys := make([]dto.APIKey, 0, len(keys))
for i := range keys {
outKeys = append(outKeys, *dto.ApiKeyFromService(&keys[i]))
outKeys = append(outKeys, *dto.APIKeyFromService(&keys[i]))
}
response.Paginated(c, outKeys, total, page, pageSize)
}
......@@ -17,14 +17,14 @@ import (
// UsageHandler handles admin usage-related requests
type UsageHandler struct {
usageService *service.UsageService
apiKeyService *service.ApiKeyService
apiKeyService *service.APIKeyService
adminService service.AdminService
}
// NewUsageHandler creates a new admin usage handler
func NewUsageHandler(
usageService *service.UsageService,
apiKeyService *service.ApiKeyService,
apiKeyService *service.APIKeyService,
adminService service.AdminService,
) *UsageHandler {
return &UsageHandler{
......@@ -125,7 +125,7 @@ func (h *UsageHandler) List(c *gin.Context) {
params := pagination.PaginationParams{Page: page, PageSize: pageSize}
filters := usagestats.UsageLogFilters{
UserID: userID,
ApiKeyID: apiKeyID,
APIKeyID: apiKeyID,
AccountID: accountID,
GroupID: groupID,
Model: model,
......@@ -207,7 +207,7 @@ func (h *UsageHandler) Stats(c *gin.Context) {
}
if apiKeyID > 0 {
stats, err := h.usageService.GetStatsByApiKey(c.Request.Context(), apiKeyID, startTime, endTime)
stats, err := h.usageService.GetStatsByAPIKey(c.Request.Context(), apiKeyID, startTime, endTime)
if err != nil {
response.ErrorFrom(c, err)
return
......@@ -269,9 +269,9 @@ func (h *UsageHandler) SearchUsers(c *gin.Context) {
response.Success(c, result)
}
// SearchApiKeys handles searching API keys by user
// SearchAPIKeys handles searching API keys by user
// GET /api/v1/admin/usage/search-api-keys
func (h *UsageHandler) SearchApiKeys(c *gin.Context) {
func (h *UsageHandler) SearchAPIKeys(c *gin.Context) {
userIDStr := c.Query("user_id")
keyword := c.Query("q")
......@@ -285,22 +285,22 @@ func (h *UsageHandler) SearchApiKeys(c *gin.Context) {
userID = id
}
keys, err := h.apiKeyService.SearchApiKeys(c.Request.Context(), userID, keyword, 30)
keys, err := h.apiKeyService.SearchAPIKeys(c.Request.Context(), userID, keyword, 30)
if err != nil {
response.ErrorFrom(c, err)
return
}
// Return simplified API key list (only id and name)
type SimpleApiKey struct {
type SimpleAPIKey struct {
ID int64 `json:"id"`
Name string `json:"name"`
UserID int64 `json:"user_id"`
}
result := make([]SimpleApiKey, len(keys))
result := make([]SimpleAPIKey, len(keys))
for i, k := range keys {
result[i] = SimpleApiKey{
result[i] = SimpleAPIKey{
ID: k.ID,
Name: k.Name,
UserID: k.UserID,
......
......@@ -243,9 +243,9 @@ func (h *UserHandler) GetUserAPIKeys(c *gin.Context) {
return
}
out := make([]dto.ApiKey, 0, len(keys))
out := make([]dto.APIKey, 0, len(keys))
for i := range keys {
out = append(out, *dto.ApiKeyFromService(&keys[i]))
out = append(out, *dto.APIKeyFromService(&keys[i]))
}
response.Paginated(c, out, total, page, pageSize)
}
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment