Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
8515bbd
chore(backend): update dependencies
alfadb Feb 13, 2026
63d0ab8
chore: update gitignore and deploy config example
alfadb Feb 13, 2026
6cdbb7a
feat(config): add Copilot and device auth settings
alfadb Feb 13, 2026
856a17f
feat(service): add OpenAI responses URL helper
alfadb Feb 13, 2026
8a34e79
feat(service): add Anthropic messages URL helper
alfadb Feb 13, 2026
1bff671
feat(service): add GitHub Copilot token provider
alfadb Feb 13, 2026
e8032f6
feat(repository): add GitHub device session store
alfadb Feb 13, 2026
c53e176
feat(service): add GitHub device auth service
alfadb Feb 13, 2026
4676bda
feat: wire GitHub device auth and Copilot into admin and gateway
alfadb Feb 13, 2026
203c2b3
feat(frontend): add GitHub device auth API and i18n
alfadb Feb 13, 2026
1d35e1d
feat(frontend): add GitHub device auth modal
alfadb Feb 13, 2026
37052f7
feat(frontend): integrate GitHub device auth into accounts UI
alfadb Feb 13, 2026
40999a5
test(frontend): update Vitest config and setup
alfadb Feb 13, 2026
3747043
test(frontend): update data import integration specs
alfadb Feb 13, 2026
3ee8b47
fix(repository): expire in-memory GitHub device sessions
alfadb Feb 13, 2026
a8fb744
fix(service): make GitHub device auth cancel context-aware
alfadb Feb 13, 2026
bfb6202
fix(service): respect ctx while waiting for Copilot token lock
alfadb Feb 13, 2026
04d7892
fix(frontend): align device auth poll types and close behavior
alfadb Feb 13, 2026
d788165
fix(antigravity): annotate fixed endpoints for gosec
alfadb Feb 13, 2026
9e7a023
fix(geminicli): validate resolved IP for Drive quota request
alfadb Feb 13, 2026
3a9b593
fix(repository): document upstream URL validation for gosec
alfadb Feb 13, 2026
f8dcb01
fix(repository): annotate fixed URL requests for gosec
alfadb Feb 13, 2026
11de7b3
fix(service): harden fetchWithETag URL handling
alfadb Feb 13, 2026
9c95df4
fix(service): document validated URLs for gosec
alfadb Feb 13, 2026
689a9bc
fix(service): simplify Claude header guard logic
alfadb Feb 13, 2026
6d74ad8
fix(service): clean up OpenAI URL helpers
alfadb Feb 13, 2026
641a814
test(admin): remove unused device auth tracking
alfadb Feb 13, 2026
2f26c74
feat(service): add copilot/aggregator platform constants
alfadb Feb 14, 2026
a299e4d
feat(service): wire copilot/aggregator into platform defaults
alfadb Feb 14, 2026
bd3c9cd
feat(service): add GitHub Copilot model listing
alfadb Feb 14, 2026
d65b428
feat(service): add gateway GitHub Copilot model listing
alfadb Feb 14, 2026
18bf10d
feat(service): make OpenAI gateway selection platform-aware
alfadb Feb 14, 2026
f7415df
feat: route OpenAI gateway requests for copilot/aggregator
alfadb Feb 14, 2026
53391c0
feat(admin): allow copilot/aggregator in groups and accounts
alfadb Feb 14, 2026
a1081af
feat(service): treat copilot/aggregator as OpenAI accounts
alfadb Feb 14, 2026
0903ef6
feat(frontend): add copilot/aggregator platform types
alfadb Feb 14, 2026
88fffe2
feat(frontend): add copilot/aggregator platform UI
alfadb Feb 14, 2026
05629d1
feat(frontend): support copilot/aggregator account forms
alfadb Feb 14, 2026
b91f4c9
feat: allow copilot/aggregator in error passthrough rules
alfadb Feb 14, 2026
0c95f4d
fix(handler): guard against nil API key
alfadb Feb 14, 2026
29e1fa9
chore(backend): add dist placeholder for go:embed
alfadb Feb 14, 2026
6122471
feat: support namespaced models across gateway protocols
alfadb Feb 15, 2026
06eea46
feat(frontend): add Copilot model refresh action
alfadb Feb 15, 2026
9362228
feat(backend): introduce provider abstraction layer
alfadb Feb 15, 2026
a3f8df7
fix(test): add missing list stubs to api_contract_test
alfadb Feb 16, 2026
5d04fab
fix(test): update multiplatform mocks for new list methods
alfadb Feb 16, 2026
1a76494
fix(test): add missing repo stub methods in service tests
alfadb Feb 16, 2026
ae7e601
fix(api-key): defer billing checks for groupless API keys
alfadb Feb 16, 2026
6819298
fix(handler): resolve effective API key for CountTokens
alfadb Feb 16, 2026
36538ae
fix(service): allow subscription eligibility without subscription object
alfadb Feb 16, 2026
ccf1167
fix(service): backfill subscription when recording usage
alfadb Feb 16, 2026
50dd749
refactor(handler): avoid deprecated http.CloseNotifier
alfadb Feb 16, 2026
92c333d
refactor(admin): simplify Copilot model refresh guard
alfadb Feb 16, 2026
2b3cf74
refactor(service): simplify token param normalization
alfadb Feb 16, 2026
161b9fa
refactor(service): simplify OpenAI connection base URL defaults
alfadb Feb 16, 2026
6c60865
refactor(service): simplify Gemini tool param typing
alfadb Feb 16, 2026
e4cd5d3
fix(domain): map copilot/aggregator provider to platform
alfadb Feb 16, 2026
314f3fc
fix(service): namespace available models by provider
alfadb Feb 16, 2026
c32e430
feat(handler): enrich model list with provider pricing source
alfadb Feb 16, 2026
1b717bc
fix(frontend): allow API key creation without group
alfadb Feb 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ temp/
.cache/
.dev/
.serena/
.sisyphus/

# ===================
# 构建产物
Expand Down Expand Up @@ -129,4 +130,4 @@ deploy/docker-compose.override.yml
.gocache/
vite.config.js
docs/*
.serena/
.serena/
7 changes: 7 additions & 0 deletions backend/cmd/server/wire.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ func provideCleanup(
opsScheduledReport *service.OpsScheduledReportService,
schedulerSnapshot *service.SchedulerSnapshotService,
tokenRefresh *service.TokenRefreshService,
copilotModelRefresh *service.CopilotModelRefreshService,
accountExpiry *service.AccountExpiryService,
subscriptionExpiry *service.SubscriptionExpiryService,
usageCleanup *service.UsageCleanupService,
Expand Down Expand Up @@ -135,6 +136,12 @@ func provideCleanup(
tokenRefresh.Stop()
return nil
}},
{"CopilotModelRefreshService", func() error {
if copilotModelRefresh != nil {
copilotModelRefresh.Stop()
}
return nil
}},
{"AccountExpiryService", func() error {
accountExpiry.Stop()
return nil
Expand Down
26 changes: 19 additions & 7 deletions backend/cmd/server/wire_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions backend/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ require (
github.com/goccy/go-json v0.10.2 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/subcommands v1.2.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/hashicorp/hcl/v2 v2.18.1 // indirect
Expand Down Expand Up @@ -144,6 +145,7 @@ require (
golang.org/x/mod v0.31.0 // indirect
golang.org/x/sys v0.40.0 // indirect
golang.org/x/text v0.33.0 // indirect
golang.org/x/tools v0.40.0 // indirect
google.golang.org/grpc v1.75.1 // indirect
google.golang.org/protobuf v1.36.10 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions backend/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
github.com/google/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN9oE=
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/wire v0.7.0 h1:JxUKI6+CVBgCO2WToKy/nQk0sS+amI9z9EjVmdaocj4=
Expand Down
63 changes: 38 additions & 25 deletions backend/internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,31 +38,32 @@ const (
)

type Config struct {
Server ServerConfig `mapstructure:"server"`
CORS CORSConfig `mapstructure:"cors"`
Security SecurityConfig `mapstructure:"security"`
Billing BillingConfig `mapstructure:"billing"`
Turnstile TurnstileConfig `mapstructure:"turnstile"`
Database DatabaseConfig `mapstructure:"database"`
Redis RedisConfig `mapstructure:"redis"`
Ops OpsConfig `mapstructure:"ops"`
JWT JWTConfig `mapstructure:"jwt"`
Totp TotpConfig `mapstructure:"totp"`
LinuxDo LinuxDoConnectConfig `mapstructure:"linuxdo_connect"`
Default DefaultConfig `mapstructure:"default"`
RateLimit RateLimitConfig `mapstructure:"rate_limit"`
Pricing PricingConfig `mapstructure:"pricing"`
Gateway GatewayConfig `mapstructure:"gateway"`
APIKeyAuth APIKeyAuthCacheConfig `mapstructure:"api_key_auth_cache"`
Dashboard DashboardCacheConfig `mapstructure:"dashboard_cache"`
DashboardAgg DashboardAggregationConfig `mapstructure:"dashboard_aggregation"`
UsageCleanup UsageCleanupConfig `mapstructure:"usage_cleanup"`
Concurrency ConcurrencyConfig `mapstructure:"concurrency"`
TokenRefresh TokenRefreshConfig `mapstructure:"token_refresh"`
RunMode string `mapstructure:"run_mode" yaml:"run_mode"`
Timezone string `mapstructure:"timezone"` // e.g. "Asia/Shanghai", "UTC"
Gemini GeminiConfig `mapstructure:"gemini"`
Update UpdateConfig `mapstructure:"update"`
Server ServerConfig `mapstructure:"server"`
CORS CORSConfig `mapstructure:"cors"`
Security SecurityConfig `mapstructure:"security"`
Billing BillingConfig `mapstructure:"billing"`
Turnstile TurnstileConfig `mapstructure:"turnstile"`
Database DatabaseConfig `mapstructure:"database"`
Redis RedisConfig `mapstructure:"redis"`
Ops OpsConfig `mapstructure:"ops"`
JWT JWTConfig `mapstructure:"jwt"`
Totp TotpConfig `mapstructure:"totp"`
LinuxDo LinuxDoConnectConfig `mapstructure:"linuxdo_connect"`
Default DefaultConfig `mapstructure:"default"`
RateLimit RateLimitConfig `mapstructure:"rate_limit"`
Pricing PricingConfig `mapstructure:"pricing"`
Gateway GatewayConfig `mapstructure:"gateway"`
APIKeyAuth APIKeyAuthCacheConfig `mapstructure:"api_key_auth_cache"`
Dashboard DashboardCacheConfig `mapstructure:"dashboard_cache"`
DashboardAgg DashboardAggregationConfig `mapstructure:"dashboard_aggregation"`
UsageCleanup UsageCleanupConfig `mapstructure:"usage_cleanup"`
Concurrency ConcurrencyConfig `mapstructure:"concurrency"`
TokenRefresh TokenRefreshConfig `mapstructure:"token_refresh"`
CopilotModelRefresh CopilotModelRefreshConfig `mapstructure:"copilot_model_refresh"`
RunMode string `mapstructure:"run_mode" yaml:"run_mode"`
Timezone string `mapstructure:"timezone"` // e.g. "Asia/Shanghai", "UTC"
Gemini GeminiConfig `mapstructure:"gemini"`
Update UpdateConfig `mapstructure:"update"`
}

type GeminiConfig struct {
Expand Down Expand Up @@ -128,6 +129,12 @@ type TokenRefreshConfig struct {
RetryBackoffSeconds int `mapstructure:"retry_backoff_seconds"`
}

type CopilotModelRefreshConfig struct {
Enabled bool `mapstructure:"enabled"`
CheckIntervalMinutes int `mapstructure:"check_interval_minutes"`
RequestTimeoutSeconds int `mapstructure:"request_timeout_seconds"`
}

type PricingConfig struct {
// 价格数据远程URL(默认使用LiteLLM镜像)
RemoteURL string `mapstructure:"remote_url"`
Expand Down Expand Up @@ -724,6 +731,8 @@ func setDefaults() {
viper.SetDefault("security.url_allowlist.upstream_hosts", []string{
"api.openai.com",
"api.anthropic.com",
"*.githubcopilot.com",
"api.github.com",
"api.kimi.com",
"open.bigmodel.cn",
"api.minimaxi.com",
Expand Down Expand Up @@ -923,6 +932,10 @@ func setDefaults() {
viper.SetDefault("token_refresh.max_retries", 3) // 最多重试3次
viper.SetDefault("token_refresh.retry_backoff_seconds", 2) // 重试退避基础2秒

viper.SetDefault("copilot_model_refresh.enabled", true)
viper.SetDefault("copilot_model_refresh.check_interval_minutes", 360)
viper.SetDefault("copilot_model_refresh.request_timeout_seconds", 30)

// Gemini OAuth - configure via environment variables or config file
// GEMINI_OAUTH_CLIENT_ID and GEMINI_OAUTH_CLIENT_SECRET
// Default: uses Gemini CLI public credentials (set via environment)
Expand Down
44 changes: 43 additions & 1 deletion backend/internal/domain/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,56 @@ const (
RoleUser = "user"
)

// Platform constants
// Platform constants (API protocol type)
const (
PlatformAnthropic = "anthropic"
PlatformOpenAI = "openai"
PlatformCopilot = "copilot"
PlatformAggregator = "aggregator"
PlatformGemini = "gemini"
PlatformAntigravity = "antigravity"
)

// Provider constants (actual service source)
// Provider identifies the upstream service provider for a model,
// enabling differentiation of context windows and pricing for the same model name.
const (
ProviderOpenAI = "openai" // OpenAI 官方 API
ProviderAzure = "azure" // Azure OpenAI
ProviderCopilot = "copilot" // GitHub Copilot
ProviderAnthropic = "anthropic" // Anthropic 官方 API
ProviderGemini = "gemini" // Google Gemini 官方 API
ProviderVertexAI = "vertex" // Google Vertex AI
ProviderAntigravity = "antigravity" // Antigravity 服务
ProviderBedrock = "bedrock" // AWS Bedrock
ProviderOpenRouter = "openrouter" // OpenRouter 聚合
ProviderAggregator = "aggregator" // 通用聚合器
)

// ProviderToPlatform maps provider to the API protocol (platform) it uses.
// This enables automatic platform inference from provider namespace.
var ProviderToPlatform = map[string]string{
ProviderOpenAI: PlatformOpenAI,
ProviderAzure: PlatformOpenAI,
ProviderCopilot: PlatformCopilot,
ProviderAnthropic: PlatformAnthropic,
ProviderGemini: PlatformGemini,
ProviderVertexAI: PlatformGemini,
ProviderAntigravity: PlatformAntigravity,
ProviderBedrock: PlatformOpenAI, // Bedrock uses OpenAI-compatible format
ProviderOpenRouter: PlatformOpenAI,
ProviderAggregator: PlatformAggregator,
}

// GetPlatformFromProvider returns the platform (API protocol) for a given provider.
// Returns empty string if provider is unknown.
func GetPlatformFromProvider(provider string) string {
if platform, ok := ProviderToPlatform[provider]; ok {
return platform
}
return ""
}

// Account type constants
const (
AccountTypeOAuth = "oauth" // OAuth类型账号(full scope: profile + inference)
Expand Down
2 changes: 2 additions & 0 deletions backend/internal/handler/admin/account_data_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ func setupAccountDataRouter() (*gin.Engine, *stubAdminService) {
nil,
nil,
nil,
nil,
nil,
)

router.GET("/api/v1/admin/accounts/data", h.ExportData)
Expand Down
Loading
Loading