Commit a4953785 authored by IanShaw027's avatar IanShaw027
Browse files

fix(lint): 修复所有 Go 命名规范问题

- 全局替换 ApiKey → APIKey(类型、字段、方法、变量)
- 修复所有 initialism 命名(API, SMTP, HTML, URL 等)
- 添加所有缺失的包注释
- 修复导出符号的注释格式

主要修改:
- ApiKey → APIKey(所有出现的地方)
- ApiKeyID → APIKeyID
- ApiKeyIDs → APIKeyIDs
- TestSmtpConnection → TestSMTPConnection
- HtmlURL → HTMLURL
- 添加 20+ 个包注释
- 修复 10+ 个导出符号注释格式

验证结果:
- ✓ golangci-lint: 0 issues
- ✓ 单元测试: 通过
- ✓ 集成测试: 通过
parent d92e71a1
// 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 时,是否按需自动补齐(默认关闭以保持兼容)
InjectBetaForApiKey bool `mapstructure:"inject_beta_for_apikey"` InjectBetaForAPIKey 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"`
ApiKeyPrefix string `mapstructure:"api_key_prefix"` APIKeyPrefix string `mapstructure:"api_key_prefix"`
RateMultiplier float64 `mapstructure:"rate_multiplier"` RateMultiplier float64 `mapstructure:"rate_multiplier"`
} }
......
// Package admin provides HTTP handlers for administrative operations.
package admin package admin
import ( import (
......
...@@ -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.TotalApiKeys, "total_api_keys": stats.TotalAPIKeys,
"active_api_keys": stats.ActiveApiKeys, "active_api_keys": stats.ActiveAPIKeys,
// 账户统计 // 账户统计
"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) {
}) })
} }
// GetApiKeyUsageTrend handles getting API key usage trend data // GetAPIKeyUsageTrend 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) GetApiKeyUsageTrend(c *gin.Context) { func (h *DashboardHandler) GetAPIKeyUsageTrend(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.GetApiKeyUsageTrend(c.Request.Context(), startTime, endTime, granularity, limit) trend, err := h.dashboardService.GetAPIKeyUsageTrend(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})
} }
// BatchApiKeysUsageRequest represents the request body for batch api key usage stats // BatchAPIKeysUsageRequest represents the request body for batch api key usage stats
type BatchApiKeysUsageRequest struct { type BatchAPIKeysUsageRequest struct {
ApiKeyIDs []int64 `json:"api_key_ids" binding:"required"` 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 // POST /api/v1/admin/dashboard/api-keys-usage
func (h *DashboardHandler) GetBatchApiKeysUsage(c *gin.Context) { func (h *DashboardHandler) GetBatchAPIKeysUsage(c *gin.Context) {
var req BatchApiKeysUsageRequest var req BatchAPIKeysUsageRequest
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.ApiKeyIDs) == 0 { if len(req.APIKeyIDs) == 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.GetBatchApiKeyUsageStats(c.Request.Context(), req.ApiKeyIDs) stats, err := h.dashboardService.GetBatchAPIKeyUsageStats(c.Request.Context(), req.APIKeyIDs)
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
......
...@@ -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()
......
...@@ -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.ApiKey, 0, len(keys)) outKeys := make([]dto.APIKey, 0, len(keys))
for i := range 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) response.Paginated(c, outKeys, total, page, pageSize)
} }
...@@ -230,9 +230,9 @@ type TestSMTPRequest struct { ...@@ -230,9 +230,9 @@ type TestSMTPRequest struct {
SMTPUseTLS bool `json:"smtp_use_tls"` SMTPUseTLS bool `json:"smtp_use_tls"`
} }
// TestSmtpConnection 测试SMTP连接 // TestSMTPConnection 测试SMTP连接
// POST /api/v1/admin/settings/test-smtp // POST /api/v1/admin/settings/test-smtp
func (h *SettingHandler) TestSmtpConnection(c *gin.Context) { func (h *SettingHandler) TestSMTPConnection(c *gin.Context) {
var req TestSMTPRequest var req TestSMTPRequest
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())
...@@ -246,13 +246,13 @@ func (h *SettingHandler) TestSmtpConnection(c *gin.Context) { ...@@ -246,13 +246,13 @@ func (h *SettingHandler) TestSmtpConnection(c *gin.Context) {
// 如果未提供密码,从数据库获取已保存的密码 // 如果未提供密码,从数据库获取已保存的密码
password := req.SMTPPassword password := req.SMTPPassword
if password == "" { if password == "" {
savedConfig, err := h.emailService.GetSmtpConfig(c.Request.Context()) savedConfig, err := h.emailService.GetSMTPConfig(c.Request.Context())
if err == nil && savedConfig != nil { if err == nil && savedConfig != nil {
password = savedConfig.Password password = savedConfig.Password
} }
} }
config := &service.SmtpConfig{ config := &service.SMTPConfig{
Host: req.SMTPHost, Host: req.SMTPHost,
Port: req.SMTPPort, Port: req.SMTPPort,
Username: req.SMTPUsername, Username: req.SMTPUsername,
...@@ -260,7 +260,7 @@ func (h *SettingHandler) TestSmtpConnection(c *gin.Context) { ...@@ -260,7 +260,7 @@ func (h *SettingHandler) TestSmtpConnection(c *gin.Context) {
UseTLS: req.SMTPUseTLS, UseTLS: req.SMTPUseTLS,
} }
err := h.emailService.TestSmtpConnectionWithConfig(config) err := h.emailService.TestSMTPConnectionWithConfig(config)
if err != nil { if err != nil {
response.ErrorFrom(c, err) response.ErrorFrom(c, err)
return return
...@@ -297,13 +297,13 @@ func (h *SettingHandler) SendTestEmail(c *gin.Context) { ...@@ -297,13 +297,13 @@ func (h *SettingHandler) SendTestEmail(c *gin.Context) {
// 如果未提供密码,从数据库获取已保存的密码 // 如果未提供密码,从数据库获取已保存的密码
password := req.SMTPPassword password := req.SMTPPassword
if password == "" { if password == "" {
savedConfig, err := h.emailService.GetSmtpConfig(c.Request.Context()) savedConfig, err := h.emailService.GetSMTPConfig(c.Request.Context())
if err == nil && savedConfig != nil { if err == nil && savedConfig != nil {
password = savedConfig.Password password = savedConfig.Password
} }
} }
config := &service.SmtpConfig{ config := &service.SMTPConfig{
Host: req.SMTPHost, Host: req.SMTPHost,
Port: req.SMTPPort, Port: req.SMTPPort,
Username: req.SMTPUsername, Username: req.SMTPUsername,
...@@ -355,10 +355,10 @@ func (h *SettingHandler) SendTestEmail(c *gin.Context) { ...@@ -355,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"})
} }
// GetAdminApiKey 获取管理员 API Key 状态 // GetAdminAPIKey 获取管理员 API Key 状态
// GET /api/v1/admin/settings/admin-api-key // GET /api/v1/admin/settings/admin-api-key
func (h *SettingHandler) GetAdminApiKey(c *gin.Context) { func (h *SettingHandler) GetAdminAPIKey(c *gin.Context) {
maskedKey, exists, err := h.settingService.GetAdminApiKeyStatus(c.Request.Context()) maskedKey, exists, err := h.settingService.GetAdminAPIKeyStatus(c.Request.Context())
if err != nil { if err != nil {
response.ErrorFrom(c, err) response.ErrorFrom(c, err)
return return
...@@ -370,10 +370,10 @@ func (h *SettingHandler) GetAdminApiKey(c *gin.Context) { ...@@ -370,10 +370,10 @@ func (h *SettingHandler) GetAdminApiKey(c *gin.Context) {
}) })
} }
// RegenerateAdminApiKey 生成/重新生成管理员 API Key // RegenerateAdminAPIKey 生成/重新生成管理员 API Key
// POST /api/v1/admin/settings/admin-api-key/regenerate // POST /api/v1/admin/settings/admin-api-key/regenerate
func (h *SettingHandler) RegenerateAdminApiKey(c *gin.Context) { func (h *SettingHandler) RegenerateAdminAPIKey(c *gin.Context) {
key, err := h.settingService.GenerateAdminApiKey(c.Request.Context()) key, err := h.settingService.GenerateAdminAPIKey(c.Request.Context())
if err != nil { if err != nil {
response.ErrorFrom(c, err) response.ErrorFrom(c, err)
return return
...@@ -384,10 +384,10 @@ func (h *SettingHandler) RegenerateAdminApiKey(c *gin.Context) { ...@@ -384,10 +384,10 @@ func (h *SettingHandler) RegenerateAdminApiKey(c *gin.Context) {
}) })
} }
// DeleteAdminApiKey 删除管理员 API Key // DeleteAdminAPIKey 删除管理员 API Key
// DELETE /api/v1/admin/settings/admin-api-key // DELETE /api/v1/admin/settings/admin-api-key
func (h *SettingHandler) DeleteAdminApiKey(c *gin.Context) { func (h *SettingHandler) DeleteAdminAPIKey(c *gin.Context) {
if err := h.settingService.DeleteAdminApiKey(c.Request.Context()); err != nil { if err := h.settingService.DeleteAdminAPIKey(c.Request.Context()); err != nil {
response.ErrorFrom(c, err) response.ErrorFrom(c, err)
return return
} }
......
...@@ -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.ApiKeyService apiKeyService *service.APIKeyService
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.ApiKeyService, apiKeyService *service.APIKeyService,
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,
ApiKeyID: apiKeyID, APIKeyID: 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.GetStatsByApiKey(c.Request.Context(), apiKeyID, startTime, endTime) stats, err := h.usageService.GetStatsByAPIKey(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)
} }
// SearchApiKeys handles searching API keys by user // SearchAPIKeys 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) SearchApiKeys(c *gin.Context) { func (h *UsageHandler) SearchAPIKeys(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.SearchApiKeys(c.Request.Context(), userID, keyword, 30) keys, err := h.apiKeyService.SearchAPIKeys(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 SimpleApiKey struct { type SimpleAPIKey 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([]SimpleApiKey, len(keys)) result := make([]SimpleAPIKey, len(keys))
for i, k := range keys { for i, k := range keys {
result[i] = SimpleApiKey{ result[i] = SimpleAPIKey{
ID: k.ID, ID: k.ID,
Name: k.Name, Name: k.Name,
UserID: k.UserID, UserID: k.UserID,
......
...@@ -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.ApiKey, 0, len(keys)) out := make([]dto.APIKey, 0, len(keys))
for i := range 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) response.Paginated(c, out, total, page, pageSize)
} }
......
// 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.ApiKeyService apiKeyService *service.APIKeyService
} }
// NewAPIKeyHandler creates a new APIKeyHandler // NewAPIKeyHandler creates a new APIKeyHandler
func NewAPIKeyHandler(apiKeyService *service.ApiKeyService) *APIKeyHandler { func NewAPIKeyHandler(apiKeyService *service.APIKeyService) *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.ApiKey, 0, len(keys)) out := make([]dto.APIKey, 0, len(keys))
for i := range keys { for i := range keys {
out = append(out, *dto.ApiKeyFromService(&keys[i])) out = append(out, *dto.APIKeyFromService(&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.ApiKeyFromService(key)) response.Success(c, dto.APIKeyFromService(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.CreateApiKeyRequest{ svcReq := service.CreateAPIKeyRequest{
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.ApiKeyFromService(key)) response.Success(c, dto.APIKeyFromService(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.UpdateApiKeyRequest{} svcReq := service.UpdateAPIKeyRequest{}
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.ApiKeyFromService(key)) response.Success(c, dto.APIKeyFromService(key))
} }
// Delete handles deleting an API key // Delete handles deleting an API key
......
// 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.ApiKeys) > 0 { if len(u.APIKeys) > 0 {
out.ApiKeys = make([]ApiKey, 0, len(u.ApiKeys)) out.APIKeys = make([]APIKey, 0, len(u.APIKeys))
for i := range u.ApiKeys { for i := range u.APIKeys {
k := u.ApiKeys[i] k := u.APIKeys[i]
out.ApiKeys = append(out.ApiKeys, *ApiKeyFromService(&k)) out.APIKeys = append(out.APIKeys, *APIKeyFromService(&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 ApiKeyFromService(k *service.ApiKey) *ApiKey { func APIKeyFromService(k *service.APIKey) *APIKey {
if k == nil { if k == nil {
return nil return nil
} }
return &ApiKey{ return &APIKey{
ID: k.ID, ID: k.ID,
UserID: k.UserID, UserID: k.UserID,
Key: k.Key, Key: k.Key,
...@@ -222,7 +223,7 @@ func UsageLogFromService(l *service.UsageLog) *UsageLog { ...@@ -222,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,
ApiKeyID: l.ApiKeyID, APIKeyID: l.APIKeyID,
AccountID: l.AccountID, AccountID: l.AccountID,
RequestID: l.RequestID, RequestID: l.RequestID,
Model: l.Model, Model: l.Model,
...@@ -247,7 +248,7 @@ func UsageLogFromService(l *service.UsageLog) *UsageLog { ...@@ -247,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),
ApiKey: ApiKeyFromService(l.ApiKey), APIKey: APIKeyFromService(l.APIKey),
Account: AccountFromService(l.Account), Account: AccountFromService(l.Account),
Group: GroupFromServiceShallow(l.Group), Group: GroupFromServiceShallow(l.Group),
Subscription: UserSubscriptionFromService(l.Subscription), Subscription: UserSubscriptionFromService(l.Subscription),
......
...@@ -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"`
ApiKeys []ApiKey `json:"api_keys,omitempty"` APIKeys []APIKey `json:"api_keys,omitempty"`
Subscriptions []UserSubscription `json:"subscriptions,omitempty"` Subscriptions []UserSubscription `json:"subscriptions,omitempty"`
} }
type ApiKey struct { type APIKey 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"`
...@@ -139,7 +139,7 @@ type RedeemCode struct { ...@@ -139,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"`
ApiKeyID int64 `json:"api_key_id"` APIKeyID 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"`
...@@ -171,7 +171,7 @@ type UsageLog struct { ...@@ -171,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"`
ApiKey *ApiKey `json:"api_key,omitempty"` APIKey *APIKey `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"`
......
...@@ -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.GetApiKeyFromContext(c) apiKey, ok := middleware2.GetAPIKeyFromContext(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,
ApiKey: apiKey, APIKey: 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,
ApiKey: apiKey, APIKey: 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.GetApiKeyFromContext(c) apiKey, _ := middleware2.GetAPIKeyFromContext(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.GetApiKeyFromContext(c) apiKey, ok := middleware2.GetAPIKeyFromContext(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.GetApiKeyFromContext(c) apiKey, ok := middleware2.GetAPIKeyFromContext(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
......
...@@ -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.GetApiKeyFromContext(c) apiKey, ok := middleware.GetAPIKeyFromContext(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.GetApiKeyFromContext(c) apiKey, ok := middleware.GetAPIKeyFromContext(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.GetApiKeyFromContext(c) apiKey, ok := middleware.GetAPIKeyFromContext(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,
ApiKey: apiKey, APIKey: apiKey,
User: apiKey.User, User: apiKey.User,
Account: usedAccount, Account: usedAccount,
Subscription: subscription, Subscription: subscription,
......
...@@ -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.GetApiKeyFromContext(c) apiKey, ok := middleware2.GetAPIKeyFromContext(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,
ApiKey: apiKey, APIKey: apiKey,
User: apiKey.User, User: apiKey.User,
Account: usedAccount, Account: usedAccount,
Subscription: subscription, Subscription: subscription,
......
...@@ -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.ApiKeyService apiKeyService *service.APIKeyService
} }
// NewUsageHandler creates a new UsageHandler // NewUsageHandler creates a new UsageHandler
func NewUsageHandler(usageService *service.UsageService, apiKeyService *service.ApiKeyService) *UsageHandler { func NewUsageHandler(usageService *service.UsageService, apiKeyService *service.APIKeyService) *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
ApiKeyID: apiKeyID, APIKeyID: 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.GetStatsByApiKey(c.Request.Context(), apiKeyID, startTime, endTime) stats, err = h.usageService.GetStatsByAPIKey(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) {
}) })
} }
// BatchApiKeysUsageRequest represents the request for batch API keys usage // BatchAPIKeysUsageRequest represents the request for batch API keys usage
type BatchApiKeysUsageRequest struct { type BatchAPIKeysUsageRequest struct {
ApiKeyIDs []int64 `json:"api_key_ids" binding:"required"` APIKeyIDs []int64 `json:"api_key_ids" binding:"required"`
} }
// DashboardApiKeysUsage handles getting usage stats for user's own API keys // DashboardAPIKeysUsage 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) DashboardApiKeysUsage(c *gin.Context) { func (h *UsageHandler) DashboardAPIKeysUsage(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 BatchApiKeysUsageRequest var req BatchAPIKeysUsageRequest
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.ApiKeyIDs) == 0 { if len(req.APIKeyIDs) == 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.ApiKeyIDs) > 100 { if len(req.APIKeyIDs) > 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
} }
validApiKeyIDs, err := h.apiKeyService.VerifyOwnership(c.Request.Context(), subject.UserID, req.ApiKeyIDs) validAPIKeyIDs, err := h.apiKeyService.VerifyOwnership(c.Request.Context(), subject.UserID, req.APIKeyIDs)
if err != nil { if err != nil {
response.ErrorFrom(c, err) response.ErrorFrom(c, err)
return return
} }
if len(validApiKeyIDs) == 0 { if len(validAPIKeyIDs) == 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.GetBatchApiKeyUsageStats(c.Request.Context(), validApiKeyIDs) stats, err := h.usageService.GetBatchAPIKeyUsageStats(c.Request.Context(), validAPIKeyIDs)
if err != nil { if err != nil {
response.ErrorFrom(c, err) response.ErrorFrom(c, err)
return return
......
// Package antigravity provides a client for the Antigravity API.
package antigravity package antigravity
import ( import (
......
// Package claude provides constants and helpers for Claude API integration.
package claude package claude
// Claude Code 客户端相关常量 // Claude Code 客户端相关常量
...@@ -16,13 +17,13 @@ const DefaultBetaHeader = BetaClaudeCode + "," + BetaOAuth + "," + BetaInterleav ...@@ -16,13 +17,13 @@ const DefaultBetaHeader = BetaClaudeCode + "," + BetaOAuth + "," + BetaInterleav
// HaikuBetaHeader Haiku 模型使用的 anthropic-beta header(不需要 claude-code beta) // HaikuBetaHeader Haiku 模型使用的 anthropic-beta header(不需要 claude-code beta)
const HaikuBetaHeader = BetaOAuth + "," + BetaInterleavedThinking const HaikuBetaHeader = BetaOAuth + "," + BetaInterleavedThinking
// ApiKeyBetaHeader API-key 账号建议使用的 anthropic-beta header(不包含 oauth) // APIKeyBetaHeader API-key 账号建议使用的 anthropic-beta header(不包含 oauth)
const ApiKeyBetaHeader = BetaClaudeCode + "," + BetaInterleavedThinking + "," + BetaFineGrainedToolStreaming const APIKeyBetaHeader = BetaClaudeCode + "," + BetaInterleavedThinking + "," + BetaFineGrainedToolStreaming
// ApiKeyHaikuBetaHeader Haiku 模型在 API-key 账号下使用的 anthropic-beta header(不包含 oauth / claude-code) // APIKeyHaikuBetaHeader Haiku 模型在 API-key 账号下使用的 anthropic-beta header(不包含 oauth / claude-code)
const ApiKeyHaikuBetaHeader = BetaInterleavedThinking const APIKeyHaikuBetaHeader = BetaInterleavedThinking
// Claude Code 客户端默认请求头 // DefaultHeaders 是 Claude Code 客户端默认请求头
var DefaultHeaders = map[string]string{ var DefaultHeaders = map[string]string{
"User-Agent": "claude-cli/2.0.62 (external, cli)", "User-Agent": "claude-cli/2.0.62 (external, cli)",
"X-Stainless-Lang": "js", "X-Stainless-Lang": "js",
......
// Package errors provides application error types and helpers.
// nolint:mnd // nolint:mnd
package errors package errors
......
package gemini // Package gemini provides minimal fallback model metadata for Gemini native endpoints.
// This package provides minimal fallback model metadata for Gemini native endpoints.
// It is used when upstream model listing is unavailable (e.g. OAuth token missing AI Studio scopes). // It is used when upstream model listing is unavailable (e.g. OAuth token missing AI Studio scopes).
package gemini
type Model struct { type Model struct {
Name string `json:"name"` Name string `json:"name"`
......
// Package geminicli provides helpers for interacting with Gemini CLI tools.
package geminicli package geminicli
import "time" import "time"
......
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