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
57fd1722
Commit
57fd1722
authored
Dec 26, 2025
by
Forest
Browse files
refactor: 调整 server 目录结构
parent
8d7a4975
Changes
27
Show whitespace changes
Inline
Side-by-side
backend/cmd/server/main.go
View file @
57fd1722
...
...
@@ -17,7 +17,7 @@ import (
"github.com/Wei-Shaw/sub2api/internal/config"
"github.com/Wei-Shaw/sub2api/internal/handler"
"github.com/Wei-Shaw/sub2api/internal/middleware"
"github.com/Wei-Shaw/sub2api/internal/
server/
middleware"
"github.com/Wei-Shaw/sub2api/internal/setup"
"github.com/Wei-Shaw/sub2api/internal/web"
...
...
backend/cmd/server/wire.go
View file @
57fd1722
...
...
@@ -4,18 +4,19 @@
package
main
import
(
"context"
"log"
"net/http"
"time"
"github.com/Wei-Shaw/sub2api/internal/config"
"github.com/Wei-Shaw/sub2api/internal/handler"
"github.com/Wei-Shaw/sub2api/internal/infrastructure"
"github.com/Wei-Shaw/sub2api/internal/repository"
"github.com/Wei-Shaw/sub2api/internal/server"
"github.com/Wei-Shaw/sub2api/internal/server/middleware"
"github.com/Wei-Shaw/sub2api/internal/service"
"context"
"log"
"net/http"
"time"
"github.com/google/wire"
"github.com/redis/go-redis/v9"
"gorm.io/gorm"
...
...
@@ -35,6 +36,7 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) {
// 业务层 ProviderSets
repository
.
ProviderSet
,
service
.
ProviderSet
,
middleware
.
ProviderSet
,
handler
.
ProviderSet
,
// 服务器层 ProviderSet
...
...
@@ -62,7 +64,11 @@ func provideServiceBuildInfo(buildInfo handler.BuildInfo) service.BuildInfo {
func
provideCleanup
(
db
*
gorm
.
DB
,
rdb
*
redis
.
Client
,
services
*
service
.
Services
,
tokenRefresh
*
service
.
TokenRefreshService
,
pricing
*
service
.
PricingService
,
emailQueue
*
service
.
EmailQueueService
,
oauth
*
service
.
OAuthService
,
openaiOAuth
*
service
.
OpenAIOAuthService
,
)
func
()
{
return
func
()
{
ctx
,
cancel
:=
context
.
WithTimeout
(
context
.
Background
(),
10
*
time
.
Second
)
...
...
@@ -74,23 +80,23 @@ func provideCleanup(
fn
func
()
error
}{
{
"TokenRefreshService"
,
func
()
error
{
services
.
T
okenRefresh
.
Stop
()
t
okenRefresh
.
Stop
()
return
nil
}},
{
"PricingService"
,
func
()
error
{
services
.
P
ricing
.
Stop
()
p
ricing
.
Stop
()
return
nil
}},
{
"EmailQueueService"
,
func
()
error
{
services
.
E
mailQueue
.
Stop
()
e
mailQueue
.
Stop
()
return
nil
}},
{
"OAuthService"
,
func
()
error
{
services
.
OA
uth
.
Stop
()
oa
uth
.
Stop
()
return
nil
}},
{
"OpenAIOAuthService"
,
func
()
error
{
services
.
O
pen
AI
OAuth
.
Stop
()
o
pen
ai
OAuth
.
Stop
()
return
nil
}},
{
"Redis"
,
func
()
error
{
...
...
backend/cmd/server/wire_gen.go
View file @
57fd1722
...
...
@@ -14,6 +14,7 @@ import (
"github.com/Wei-Shaw/sub2api/internal/infrastructure"
"github.com/Wei-Shaw/sub2api/internal/repository"
"github.com/Wei-Shaw/sub2api/internal/server"
"github.com/Wei-Shaw/sub2api/internal/server/middleware"
"github.com/Wei-Shaw/sub2api/internal/service"
"github.com/redis/go-redis/v9"
"gorm.io/gorm"
...
...
@@ -116,54 +117,13 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) {
openAIGatewayHandler
:=
handler
.
NewOpenAIGatewayHandler
(
openAIGatewayService
,
concurrencyService
,
billingCacheService
)
handlerSettingHandler
:=
handler
.
ProvideSettingHandler
(
settingService
,
buildInfo
)
handlers
:=
handler
.
ProvideHandlers
(
authHandler
,
userHandler
,
apiKeyHandler
,
usageHandler
,
redeemHandler
,
subscriptionHandler
,
adminHandlers
,
gatewayHandler
,
openAIGatewayHandler
,
handlerSettingHandler
)
groupService
:=
service
.
NewGroupService
(
groupRepository
)
accountService
:=
service
.
NewAccountService
(
accountRepository
,
groupRepository
)
proxyService
:=
service
.
NewProxyService
(
proxyRepository
)
tokenRefreshService
:=
service
.
ProvideTokenRefreshService
(
accountRepository
,
oAuthService
,
openAIOAuthService
,
configConfig
)
services
:=
&
service
.
Services
{
Auth
:
authService
,
User
:
userService
,
ApiKey
:
apiKeyService
,
Group
:
groupService
,
Account
:
accountService
,
Proxy
:
proxyService
,
Redeem
:
redeemService
,
Usage
:
usageService
,
Pricing
:
pricingService
,
Billing
:
billingService
,
BillingCache
:
billingCacheService
,
Admin
:
adminService
,
Gateway
:
gatewayService
,
OpenAIGateway
:
openAIGatewayService
,
OAuth
:
oAuthService
,
OpenAIOAuth
:
openAIOAuthService
,
RateLimit
:
rateLimitService
,
AccountUsage
:
accountUsageService
,
AccountTest
:
accountTestService
,
Setting
:
settingService
,
Email
:
emailService
,
EmailQueue
:
emailQueueService
,
Turnstile
:
turnstileService
,
Subscription
:
subscriptionService
,
Concurrency
:
concurrencyService
,
Identity
:
identityService
,
Update
:
updateService
,
TokenRefresh
:
tokenRefreshService
,
}
repositories
:=
&
repository
.
Repositories
{
User
:
userRepository
,
ApiKey
:
apiKeyRepository
,
Group
:
groupRepository
,
Account
:
accountRepository
,
Proxy
:
proxyRepository
,
RedeemCode
:
redeemCodeRepository
,
UsageLog
:
usageLogRepository
,
Setting
:
settingRepository
,
UserSubscription
:
userSubscriptionRepository
,
}
engine
:=
server
.
ProvideRouter
(
configConfig
,
handlers
,
services
,
repositories
)
jwtAuthMiddleware
:=
middleware
.
NewJWTAuthMiddleware
(
authService
,
userService
)
adminAuthMiddleware
:=
middleware
.
NewAdminAuthMiddleware
(
authService
,
userService
,
settingService
)
apiKeyAuthMiddleware
:=
middleware
.
NewApiKeyAuthMiddleware
(
apiKeyService
,
subscriptionService
)
engine
:=
server
.
ProvideRouter
(
configConfig
,
handlers
,
jwtAuthMiddleware
,
adminAuthMiddleware
,
apiKeyAuthMiddleware
)
httpServer
:=
server
.
ProvideHTTPServer
(
configConfig
,
engine
)
v
:=
provideCleanup
(
db
,
client
,
services
)
tokenRefreshService
:=
service
.
ProvideTokenRefreshService
(
accountRepository
,
oAuthService
,
openAIOAuthService
,
configConfig
)
v
:=
provideCleanup
(
db
,
client
,
tokenRefreshService
,
pricingService
,
emailQueueService
,
oAuthService
,
openAIOAuthService
)
application
:=
&
Application
{
Server
:
httpServer
,
Cleanup
:
v
,
...
...
@@ -188,7 +148,11 @@ func provideServiceBuildInfo(buildInfo handler.BuildInfo) service.BuildInfo {
func
provideCleanup
(
db
*
gorm
.
DB
,
rdb
*
redis
.
Client
,
services
*
service
.
Services
,
tokenRefresh
*
service
.
TokenRefreshService
,
pricing
*
service
.
PricingService
,
emailQueue
*
service
.
EmailQueueService
,
oauth
*
service
.
OAuthService
,
openaiOAuth
*
service
.
OpenAIOAuthService
,
)
func
()
{
return
func
()
{
ctx
,
cancel
:=
context
.
WithTimeout
(
context
.
Background
(),
10
*
time
.
Second
)
...
...
@@ -199,23 +163,23 @@ func provideCleanup(
fn
func
()
error
}{
{
"TokenRefreshService"
,
func
()
error
{
services
.
T
okenRefresh
.
Stop
()
t
okenRefresh
.
Stop
()
return
nil
}},
{
"PricingService"
,
func
()
error
{
services
.
P
ricing
.
Stop
()
p
ricing
.
Stop
()
return
nil
}},
{
"EmailQueueService"
,
func
()
error
{
services
.
E
mailQueue
.
Stop
()
e
mailQueue
.
Stop
()
return
nil
}},
{
"OAuthService"
,
func
()
error
{
services
.
OA
uth
.
Stop
()
oa
uth
.
Stop
()
return
nil
}},
{
"OpenAIOAuthService"
,
func
()
error
{
services
.
O
pen
AI
OAuth
.
Stop
()
o
pen
ai
OAuth
.
Stop
()
return
nil
}},
{
"Redis"
,
func
()
error
{
...
...
backend/internal/handler/gateway_handler.go
View file @
57fd1722
...
...
@@ -10,10 +10,10 @@ import (
"strings"
"time"
"github.com/Wei-Shaw/sub2api/internal/middleware"
"github.com/Wei-Shaw/sub2api/internal/model"
"github.com/Wei-Shaw/sub2api/internal/pkg/claude"
"github.com/Wei-Shaw/sub2api/internal/pkg/openai"
middleware2
"github.com/Wei-Shaw/sub2api/internal/server/middleware"
"github.com/Wei-Shaw/sub2api/internal/service"
"github.com/gin-gonic/gin"
...
...
@@ -41,13 +41,13 @@ func NewGatewayHandler(gatewayService *service.GatewayService, userService *serv
// POST /v1/messages
func
(
h
*
GatewayHandler
)
Messages
(
c
*
gin
.
Context
)
{
// 从context获取apiKey和user(ApiKeyAuth中间件已设置)
apiKey
,
ok
:=
middleware
.
GetApiKeyFromContext
(
c
)
apiKey
,
ok
:=
middleware
2
.
GetApiKeyFromContext
(
c
)
if
!
ok
{
h
.
errorResponse
(
c
,
http
.
StatusUnauthorized
,
"authentication_error"
,
"Invalid API key"
)
return
}
user
,
ok
:=
middleware
.
GetUserFromContext
(
c
)
user
,
ok
:=
middleware
2
.
GetUserFromContext
(
c
)
if
!
ok
{
h
.
errorResponse
(
c
,
http
.
StatusInternalServerError
,
"api_error"
,
"User context not found"
)
return
...
...
@@ -79,7 +79,7 @@ func (h *GatewayHandler) Messages(c *gin.Context) {
streamStarted
:=
false
// 获取订阅信息(可能为nil)- 提前获取用于后续检查
subscription
,
_
:=
middleware
.
GetSubscriptionFromContext
(
c
)
subscription
,
_
:=
middleware
2
.
GetSubscriptionFromContext
(
c
)
// 0. 检查wait队列是否已满
maxWait
:=
service
.
CalculateMaxWait
(
user
.
Concurrency
)
...
...
@@ -171,7 +171,7 @@ func (h *GatewayHandler) Messages(c *gin.Context) {
// GET /v1/models
// Returns different model lists based on the API key's group platform
func
(
h
*
GatewayHandler
)
Models
(
c
*
gin
.
Context
)
{
apiKey
,
_
:=
middleware
.
GetApiKeyFromContext
(
c
)
apiKey
,
_
:=
middleware
2
.
GetApiKeyFromContext
(
c
)
// Return OpenAI models for OpenAI platform groups
if
apiKey
!=
nil
&&
apiKey
.
Group
!=
nil
&&
apiKey
.
Group
.
Platform
==
"openai"
{
...
...
@@ -192,13 +192,13 @@ func (h *GatewayHandler) Models(c *gin.Context) {
// Usage handles getting account balance for CC Switch integration
// GET /v1/usage
func
(
h
*
GatewayHandler
)
Usage
(
c
*
gin
.
Context
)
{
apiKey
,
ok
:=
middleware
.
GetApiKeyFromContext
(
c
)
apiKey
,
ok
:=
middleware
2
.
GetApiKeyFromContext
(
c
)
if
!
ok
{
h
.
errorResponse
(
c
,
http
.
StatusUnauthorized
,
"authentication_error"
,
"Invalid API key"
)
return
}
user
,
ok
:=
middleware
.
GetUserFromContext
(
c
)
user
,
ok
:=
middleware
2
.
GetUserFromContext
(
c
)
if
!
ok
{
h
.
errorResponse
(
c
,
http
.
StatusUnauthorized
,
"authentication_error"
,
"Invalid API key"
)
return
...
...
@@ -206,7 +206,7 @@ func (h *GatewayHandler) Usage(c *gin.Context) {
// 订阅模式:返回订阅限额信息
if
apiKey
.
Group
!=
nil
&&
apiKey
.
Group
.
IsSubscriptionType
()
{
subscription
,
ok
:=
middleware
.
GetSubscriptionFromContext
(
c
)
subscription
,
ok
:=
middleware
2
.
GetSubscriptionFromContext
(
c
)
if
!
ok
{
h
.
errorResponse
(
c
,
http
.
StatusForbidden
,
"subscription_error"
,
"No active subscription"
)
return
...
...
@@ -328,13 +328,13 @@ func (h *GatewayHandler) errorResponse(c *gin.Context, status int, errType, mess
// 特点:校验订阅/余额,但不计算并发、不记录使用量
func
(
h
*
GatewayHandler
)
CountTokens
(
c
*
gin
.
Context
)
{
// 从context获取apiKey和user(ApiKeyAuth中间件已设置)
apiKey
,
ok
:=
middleware
.
GetApiKeyFromContext
(
c
)
apiKey
,
ok
:=
middleware
2
.
GetApiKeyFromContext
(
c
)
if
!
ok
{
h
.
errorResponse
(
c
,
http
.
StatusUnauthorized
,
"authentication_error"
,
"Invalid API key"
)
return
}
user
,
ok
:=
middleware
.
GetUserFromContext
(
c
)
user
,
ok
:=
middleware
2
.
GetUserFromContext
(
c
)
if
!
ok
{
h
.
errorResponse
(
c
,
http
.
StatusInternalServerError
,
"api_error"
,
"User context not found"
)
return
...
...
@@ -362,7 +362,7 @@ func (h *GatewayHandler) CountTokens(c *gin.Context) {
}
// 获取订阅信息(可能为nil)
subscription
,
_
:=
middleware
.
GetSubscriptionFromContext
(
c
)
subscription
,
_
:=
middleware
2
.
GetSubscriptionFromContext
(
c
)
// 校验 billing eligibility(订阅/余额)
// 【注意】不计算并发,但需要校验订阅/余额
...
...
backend/internal/handler/openai_gateway_handler.go
View file @
57fd1722
...
...
@@ -9,8 +9,8 @@ import (
"net/http"
"time"
"github.com/Wei-Shaw/sub2api/internal/middleware"
"github.com/Wei-Shaw/sub2api/internal/pkg/openai"
middleware2
"github.com/Wei-Shaw/sub2api/internal/server/middleware"
"github.com/Wei-Shaw/sub2api/internal/service"
"github.com/gin-gonic/gin"
...
...
@@ -40,13 +40,13 @@ func NewOpenAIGatewayHandler(
// POST /openai/v1/responses
func
(
h
*
OpenAIGatewayHandler
)
Responses
(
c
*
gin
.
Context
)
{
// Get apiKey and user from context (set by ApiKeyAuth middleware)
apiKey
,
ok
:=
middleware
.
GetApiKeyFromContext
(
c
)
apiKey
,
ok
:=
middleware
2
.
GetApiKeyFromContext
(
c
)
if
!
ok
{
h
.
errorResponse
(
c
,
http
.
StatusUnauthorized
,
"authentication_error"
,
"Invalid API key"
)
return
}
user
,
ok
:=
middleware
.
GetUserFromContext
(
c
)
user
,
ok
:=
middleware
2
.
GetUserFromContext
(
c
)
if
!
ok
{
h
.
errorResponse
(
c
,
http
.
StatusInternalServerError
,
"api_error"
,
"User context not found"
)
return
...
...
@@ -91,7 +91,7 @@ func (h *OpenAIGatewayHandler) Responses(c *gin.Context) {
streamStarted
:=
false
// Get subscription info (may be nil)
subscription
,
_
:=
middleware
.
GetSubscriptionFromContext
(
c
)
subscription
,
_
:=
middleware
2
.
GetSubscriptionFromContext
(
c
)
// 0. Check if wait queue is full
maxWait
:=
service
.
CalculateMaxWait
(
user
.
Concurrency
)
...
...
backend/internal/repository/repository.go
deleted
100644 → 0
View file @
8d7a4975
package
repository
import
"github.com/Wei-Shaw/sub2api/internal/service"
// Repositories 所有仓库的集合
type
Repositories
struct
{
User
service
.
UserRepository
ApiKey
service
.
ApiKeyRepository
Group
service
.
GroupRepository
Account
service
.
AccountRepository
Proxy
service
.
ProxyRepository
RedeemCode
service
.
RedeemCodeRepository
UsageLog
service
.
UsageLogRepository
Setting
service
.
SettingRepository
UserSubscription
service
.
UserSubscriptionRepository
}
backend/internal/repository/wire.go
View file @
57fd1722
...
...
@@ -15,7 +15,6 @@ var ProviderSet = wire.NewSet(
NewUsageLogRepository
,
NewSettingRepository
,
NewUserSubscriptionRepository
,
wire
.
Struct
(
new
(
Repositories
),
"*"
),
// Cache implementations
NewGatewayCache
,
...
...
backend/internal/server/http.go
View file @
57fd1722
package
server
import
(
"github.com/Wei-Shaw/sub2api/internal/config"
"github.com/Wei-Shaw/sub2api/internal/handler"
"github.com/Wei-Shaw/sub2api/internal/middleware"
"github.com/Wei-Shaw/sub2api/internal/repository"
"github.com/Wei-Shaw/sub2api/internal/service"
"net/http"
"time"
"github.com/Wei-Shaw/sub2api/internal/config"
"github.com/Wei-Shaw/sub2api/internal/handler"
middleware2
"github.com/Wei-Shaw/sub2api/internal/server/middleware"
"github.com/gin-gonic/gin"
"github.com/google/wire"
)
...
...
@@ -20,15 +19,21 @@ var ProviderSet = wire.NewSet(
)
// ProvideRouter 提供路由器
func
ProvideRouter
(
cfg
*
config
.
Config
,
handlers
*
handler
.
Handlers
,
services
*
service
.
Services
,
repos
*
repository
.
Repositories
)
*
gin
.
Engine
{
func
ProvideRouter
(
cfg
*
config
.
Config
,
handlers
*
handler
.
Handlers
,
jwtAuth
middleware2
.
JWTAuthMiddleware
,
adminAuth
middleware2
.
AdminAuthMiddleware
,
apiKeyAuth
middleware2
.
ApiKeyAuthMiddleware
,
)
*
gin
.
Engine
{
if
cfg
.
Server
.
Mode
==
"release"
{
gin
.
SetMode
(
gin
.
ReleaseMode
)
}
r
:=
gin
.
New
()
r
.
Use
(
middleware
.
Recovery
())
r
.
Use
(
middleware
2
.
Recovery
())
return
SetupRouter
(
r
,
cfg
,
handlers
,
services
,
repos
)
return
SetupRouter
(
r
,
handlers
,
jwtAuth
,
adminAuth
,
apiKeyAuth
)
}
// ProvideHTTPServer 提供 HTTP 服务器
...
...
backend/internal/middleware/admin_auth.go
→
backend/internal/
server/
middleware/admin_auth.go
View file @
57fd1722
package
middleware
import
(
"context"
"crypto/subtle"
"errors"
"strings"
...
...
@@ -12,23 +11,29 @@ import (
"github.com/gin-gonic/gin"
)
// AdminAuth 管理员认证中间件
// NewAdminAuthMiddleware 创建管理员认证中间件
func
NewAdminAuthMiddleware
(
authService
*
service
.
AuthService
,
userService
*
service
.
UserService
,
settingService
*
service
.
SettingService
,
)
AdminAuthMiddleware
{
return
AdminAuthMiddleware
(
adminAuth
(
authService
,
userService
,
settingService
))
}
// adminAuth 管理员认证中间件实现
// 支持两种认证方式(通过不同的 header 区分):
// 1. Admin API Key: x-api-key: <admin-api-key>
// 2. JWT Token: Authorization: Bearer <jwt-token> (需要管理员角色)
func
A
dminAuth
(
func
a
dminAuth
(
authService
*
service
.
AuthService
,
userRepo
interface
{
GetByID
(
ctx
context
.
Context
,
id
int64
)
(
*
model
.
User
,
error
)
GetFirstAdmin
(
ctx
context
.
Context
)
(
*
model
.
User
,
error
)
},
userService
*
service
.
UserService
,
settingService
*
service
.
SettingService
,
)
gin
.
HandlerFunc
{
return
func
(
c
*
gin
.
Context
)
{
// 检查 x-api-key header(Admin API Key 认证)
apiKey
:=
c
.
GetHeader
(
"x-api-key"
)
if
apiKey
!=
""
{
if
!
validateAdminApiKey
(
c
,
apiKey
,
settingService
,
user
Repo
)
{
if
!
validateAdminApiKey
(
c
,
apiKey
,
settingService
,
user
Service
)
{
return
}
c
.
Next
()
...
...
@@ -40,7 +45,7 @@ func AdminAuth(
if
authHeader
!=
""
{
parts
:=
strings
.
SplitN
(
authHeader
,
" "
,
2
)
if
len
(
parts
)
==
2
&&
parts
[
0
]
==
"Bearer"
{
if
!
validateJWTForAdmin
(
c
,
parts
[
1
],
authService
,
user
Repo
)
{
if
!
validateJWTForAdmin
(
c
,
parts
[
1
],
authService
,
user
Service
)
{
return
}
c
.
Next
()
...
...
@@ -58,9 +63,7 @@ func validateAdminApiKey(
c
*
gin
.
Context
,
key
string
,
settingService
*
service
.
SettingService
,
userRepo
interface
{
GetFirstAdmin
(
ctx
context
.
Context
)
(
*
model
.
User
,
error
)
},
userService
*
service
.
UserService
,
)
bool
{
storedKey
,
err
:=
settingService
.
GetAdminApiKey
(
c
.
Request
.
Context
())
if
err
!=
nil
{
...
...
@@ -75,7 +78,7 @@ func validateAdminApiKey(
}
// 获取真实的管理员用户
admin
,
err
:=
user
Repo
.
GetFirstAdmin
(
c
.
Request
.
Context
())
admin
,
err
:=
user
Service
.
GetFirstAdmin
(
c
.
Request
.
Context
())
if
err
!=
nil
{
AbortWithError
(
c
,
500
,
"INTERNAL_ERROR"
,
"No admin user found"
)
return
false
...
...
@@ -91,9 +94,7 @@ func validateJWTForAdmin(
c
*
gin
.
Context
,
token
string
,
authService
*
service
.
AuthService
,
userRepo
interface
{
GetByID
(
ctx
context
.
Context
,
id
int64
)
(
*
model
.
User
,
error
)
},
userService
*
service
.
UserService
,
)
bool
{
// 验证 JWT token
claims
,
err
:=
authService
.
ValidateToken
(
token
)
...
...
@@ -107,7 +108,7 @@ func validateJWTForAdmin(
}
// 从数据库获取用户
user
,
err
:=
user
Repo
.
GetByID
(
c
.
Request
.
Context
(),
claims
.
UserID
)
user
,
err
:=
user
Service
.
GetByID
(
c
.
Request
.
Context
(),
claims
.
UserID
)
if
err
!=
nil
{
AbortWithError
(
c
,
401
,
"USER_NOT_FOUND"
,
"User not found"
)
return
false
...
...
backend/internal/middleware/admin_only.go
→
backend/internal/
server/
middleware/admin_only.go
View file @
57fd1722
File moved
backend/internal/middleware/api_key_auth.go
→
backend/internal/
server/
middleware/api_key_auth.go
View file @
57fd1722
package
middleware
import
(
"context"
"errors"
"github.com/Wei-Shaw/sub2api/internal/model"
"log"
"strings"
"github.com/Wei-Shaw/sub2api/internal/model"
"github.com/Wei-Shaw/sub2api/internal/service"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
// ApiKeyAuthService 定义API Key认证服务需要的接口
type
ApiKeyAuthService
interface
{
GetByKey
(
ctx
context
.
Context
,
key
string
)
(
*
model
.
ApiKey
,
error
)
}
// SubscriptionAuthService 定义订阅认证服务需要的接口
type
SubscriptionAuthService
interface
{
GetActiveSubscription
(
ctx
context
.
Context
,
userID
,
groupID
int64
)
(
*
model
.
UserSubscription
,
error
)
ValidateSubscription
(
ctx
context
.
Context
,
sub
*
model
.
UserSubscription
)
error
CheckAndActivateWindow
(
ctx
context
.
Context
,
sub
*
model
.
UserSubscription
)
error
CheckAndResetWindows
(
ctx
context
.
Context
,
sub
*
model
.
UserSubscription
)
error
CheckUsageLimits
(
ctx
context
.
Context
,
sub
*
model
.
UserSubscription
,
group
*
model
.
Group
,
additionalCost
float64
)
error
}
// ApiKeyAuth API Key认证中间件
func
ApiKeyAuth
(
apiKeyRepo
ApiKeyAuthService
)
gin
.
HandlerFunc
{
return
ApiKeyAuthWithSubscription
(
apiKeyRepo
,
nil
)
// NewApiKeyAuthMiddleware 创建 API Key 认证中间件
func
NewApiKeyAuthMiddleware
(
apiKeyService
*
service
.
ApiKeyService
,
subscriptionService
*
service
.
SubscriptionService
)
ApiKeyAuthMiddleware
{
return
ApiKeyAuthMiddleware
(
apiKeyAuthWithSubscription
(
apiKeyService
,
subscriptionService
))
}
//
A
piKeyAuthWithSubscription API Key认证中间件(支持订阅验证)
func
A
piKeyAuthWithSubscription
(
apiKey
Repo
ApiKey
Auth
Service
,
subscriptionService
Subscription
Auth
Service
)
gin
.
HandlerFunc
{
//
a
piKeyAuthWithSubscription API Key认证中间件(支持订阅验证)
func
a
piKeyAuthWithSubscription
(
apiKey
Service
*
service
.
ApiKeyService
,
subscriptionService
*
service
.
SubscriptionService
)
gin
.
HandlerFunc
{
return
func
(
c
*
gin
.
Context
)
{
// 尝试从Authorization header中提取API key (Bearer scheme)
authHeader
:=
c
.
GetHeader
(
"Authorization"
)
...
...
@@ -57,7 +44,7 @@ func ApiKeyAuthWithSubscription(apiKeyRepo ApiKeyAuthService, subscriptionServic
}
// 从数据库验证API key
apiKey
,
err
:=
apiKey
Repo
.
GetByKey
(
c
.
Request
.
Context
(),
apiKeyString
)
apiKey
,
err
:=
apiKey
Service
.
GetByKey
(
c
.
Request
.
Context
(),
apiKeyString
)
if
err
!=
nil
{
if
errors
.
Is
(
err
,
gorm
.
ErrRecordNotFound
)
{
AbortWithError
(
c
,
401
,
"INVALID_API_KEY"
,
"Invalid API key"
)
...
...
backend/internal/middleware/cors.go
→
backend/internal/
server/
middleware/cors.go
View file @
57fd1722
File moved
backend/internal/middleware/jwt_auth.go
→
backend/internal/
server/
middleware/jwt_auth.go
View file @
57fd1722
package
middleware
import
(
"context"
"errors"
"strings"
...
...
@@ -11,10 +10,13 @@ import (
"github.com/gin-gonic/gin"
)
// JWTAuth JWT认证中间件
func
JWTAuth
(
authService
*
service
.
AuthService
,
userRepo
interface
{
GetByID
(
ctx
context
.
Context
,
id
int64
)
(
*
model
.
User
,
error
)
})
gin
.
HandlerFunc
{
// NewJWTAuthMiddleware 创建 JWT 认证中间件
func
NewJWTAuthMiddleware
(
authService
*
service
.
AuthService
,
userService
*
service
.
UserService
)
JWTAuthMiddleware
{
return
JWTAuthMiddleware
(
jwtAuth
(
authService
,
userService
))
}
// jwtAuth JWT认证中间件实现
func
jwtAuth
(
authService
*
service
.
AuthService
,
userService
*
service
.
UserService
)
gin
.
HandlerFunc
{
return
func
(
c
*
gin
.
Context
)
{
// 从Authorization header中提取token
authHeader
:=
c
.
GetHeader
(
"Authorization"
)
...
...
@@ -48,7 +50,7 @@ func JWTAuth(authService *service.AuthService, userRepo interface {
}
// 从数据库获取最新的用户信息
user
,
err
:=
user
Repo
.
GetByID
(
c
.
Request
.
Context
(),
claims
.
UserID
)
user
,
err
:=
user
Service
.
GetByID
(
c
.
Request
.
Context
(),
claims
.
UserID
)
if
err
!=
nil
{
AbortWithError
(
c
,
401
,
"USER_NOT_FOUND"
,
"User not found"
)
return
...
...
backend/internal/middleware/logger.go
→
backend/internal/
server/
middleware/logger.go
View file @
57fd1722
File moved
backend/internal/middleware/middleware.go
→
backend/internal/
server/
middleware/middleware.go
View file @
57fd1722
File moved
backend/internal/middleware/recovery.go
→
backend/internal/
server/
middleware/recovery.go
View file @
57fd1722
File moved
backend/internal/middleware/recovery_test.go
→
backend/internal/
server/
middleware/recovery_test.go
View file @
57fd1722
File moved
backend/internal/server/middleware/wire.go
0 → 100644
View file @
57fd1722
package
middleware
import
(
"github.com/gin-gonic/gin"
"github.com/google/wire"
)
// JWTAuthMiddleware JWT 认证中间件类型
type
JWTAuthMiddleware
gin
.
HandlerFunc
// AdminAuthMiddleware 管理员认证中间件类型
type
AdminAuthMiddleware
gin
.
HandlerFunc
// ApiKeyAuthMiddleware API Key 认证中间件类型
type
ApiKeyAuthMiddleware
gin
.
HandlerFunc
// ProviderSet 中间件层的依赖注入
var
ProviderSet
=
wire
.
NewSet
(
NewJWTAuthMiddleware
,
NewAdminAuthMiddleware
,
NewApiKeyAuthMiddleware
,
)
backend/internal/server/router.go
View file @
57fd1722
package
server
import
(
"github.com/Wei-Shaw/sub2api/internal/config"
"github.com/Wei-Shaw/sub2api/internal/handler"
"github.com/Wei-Shaw/sub2api/internal/middleware"
"github.com/Wei-Shaw/sub2api/internal/repository"
"github.com/Wei-Shaw/sub2api/internal/service"
middleware2
"github.com/Wei-Shaw/sub2api/internal/server/middleware"
"github.com/Wei-Shaw/sub2api/internal/server/routes"
"github.com/Wei-Shaw/sub2api/internal/web"
"net/http"
"github.com/gin-gonic/gin"
)
// SetupRouter 配置路由器中间件和路由
func
SetupRouter
(
r
*
gin
.
Engine
,
cfg
*
config
.
Config
,
handlers
*
handler
.
Handlers
,
services
*
service
.
Services
,
repos
*
repository
.
Repositories
)
*
gin
.
Engine
{
func
SetupRouter
(
r
*
gin
.
Engine
,
handlers
*
handler
.
Handlers
,
jwtAuth
middleware2
.
JWTAuthMiddleware
,
adminAuth
middleware2
.
AdminAuthMiddleware
,
apiKeyAuth
middleware2
.
ApiKeyAuthMiddleware
,
)
*
gin
.
Engine
{
// 应用中间件
r
.
Use
(
middleware
.
Logger
())
r
.
Use
(
middleware
.
CORS
())
// 注册路由
registerRoutes
(
r
,
handlers
,
services
,
repos
)
r
.
Use
(
middleware2
.
Logger
())
r
.
Use
(
middleware2
.
CORS
())
// Serve embedded frontend if available
if
web
.
HasEmbeddedFrontend
()
{
r
.
Use
(
web
.
ServeEmbeddedFrontend
())
}
// 注册路由
registerRoutes
(
r
,
handlers
,
jwtAuth
,
adminAuth
,
apiKeyAuth
)
return
r
}
// registerRoutes 注册所有 HTTP 路由
func
registerRoutes
(
r
*
gin
.
Engine
,
h
*
handler
.
Handlers
,
s
*
service
.
Services
,
repos
*
repository
.
Repositories
)
{
// 健康检查
r
.
GET
(
"/health"
,
func
(
c
*
gin
.
Context
)
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"status"
:
"ok"
})
})
// Claude Code 遥测日志(忽略,直接返回200)
r
.
POST
(
"/api/event_logging/batch"
,
func
(
c
*
gin
.
Context
)
{
c
.
Status
(
http
.
StatusOK
)
})
// Setup status endpoint (always returns needs_setup: false in normal mode)
// This is used by the frontend to detect when the service has restarted after setup
r
.
GET
(
"/setup/status"
,
func
(
c
*
gin
.
Context
)
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"code"
:
0
,
"data"
:
gin
.
H
{
"needs_setup"
:
false
,
"step"
:
"completed"
,
},
})
})
func
registerRoutes
(
r
*
gin
.
Engine
,
h
*
handler
.
Handlers
,
jwtAuth
middleware2
.
JWTAuthMiddleware
,
adminAuth
middleware2
.
AdminAuthMiddleware
,
apiKeyAuth
middleware2
.
ApiKeyAuthMiddleware
,
)
{
// 通用路由(健康检查、状态等)
routes
.
RegisterCommonRoutes
(
r
)
// API v1
v1
:=
r
.
Group
(
"/api/v1"
)
{
// 公开接口
auth
:=
v1
.
Group
(
"/auth"
)
{
auth
.
POST
(
"/register"
,
h
.
Auth
.
Register
)
auth
.
POST
(
"/login"
,
h
.
Auth
.
Login
)
auth
.
POST
(
"/send-verify-code"
,
h
.
Auth
.
SendVerifyCode
)
}
// 公开设置(无需认证)
settings
:=
v1
.
Group
(
"/settings"
)
{
settings
.
GET
(
"/public"
,
h
.
Setting
.
GetPublicSettings
)
}
// 需要认证的接口
authenticated
:=
v1
.
Group
(
""
)
authenticated
.
Use
(
middleware
.
JWTAuth
(
s
.
Auth
,
repos
.
User
))
{
// 当前用户信息
authenticated
.
GET
(
"/auth/me"
,
h
.
Auth
.
GetCurrentUser
)
// 用户接口
user
:=
authenticated
.
Group
(
"/user"
)
{
user
.
GET
(
"/profile"
,
h
.
User
.
GetProfile
)
user
.
PUT
(
"/password"
,
h
.
User
.
ChangePassword
)
user
.
PUT
(
""
,
h
.
User
.
UpdateProfile
)
}
// API Key管理
keys
:=
authenticated
.
Group
(
"/keys"
)
{
keys
.
GET
(
""
,
h
.
APIKey
.
List
)
keys
.
GET
(
"/:id"
,
h
.
APIKey
.
GetByID
)
keys
.
POST
(
""
,
h
.
APIKey
.
Create
)
keys
.
PUT
(
"/:id"
,
h
.
APIKey
.
Update
)
keys
.
DELETE
(
"/:id"
,
h
.
APIKey
.
Delete
)
}
// 用户可用分组(非管理员接口)
groups
:=
authenticated
.
Group
(
"/groups"
)
{
groups
.
GET
(
"/available"
,
h
.
APIKey
.
GetAvailableGroups
)
}
// 使用记录
usage
:=
authenticated
.
Group
(
"/usage"
)
{
usage
.
GET
(
""
,
h
.
Usage
.
List
)
usage
.
GET
(
"/:id"
,
h
.
Usage
.
GetByID
)
usage
.
GET
(
"/stats"
,
h
.
Usage
.
Stats
)
// User dashboard endpoints
usage
.
GET
(
"/dashboard/stats"
,
h
.
Usage
.
DashboardStats
)
usage
.
GET
(
"/dashboard/trend"
,
h
.
Usage
.
DashboardTrend
)
usage
.
GET
(
"/dashboard/models"
,
h
.
Usage
.
DashboardModels
)
usage
.
POST
(
"/dashboard/api-keys-usage"
,
h
.
Usage
.
DashboardApiKeysUsage
)
}
// 卡密兑换
redeem
:=
authenticated
.
Group
(
"/redeem"
)
{
redeem
.
POST
(
""
,
h
.
Redeem
.
Redeem
)
redeem
.
GET
(
"/history"
,
h
.
Redeem
.
GetHistory
)
}
// 用户订阅
subscriptions
:=
authenticated
.
Group
(
"/subscriptions"
)
{
subscriptions
.
GET
(
""
,
h
.
Subscription
.
List
)
subscriptions
.
GET
(
"/active"
,
h
.
Subscription
.
GetActive
)
subscriptions
.
GET
(
"/progress"
,
h
.
Subscription
.
GetProgress
)
subscriptions
.
GET
(
"/summary"
,
h
.
Subscription
.
GetSummary
)
}
}
// 管理员接口
admin
:=
v1
.
Group
(
"/admin"
)
admin
.
Use
(
middleware
.
AdminAuth
(
s
.
Auth
,
repos
.
User
,
s
.
Setting
))
{
// 仪表盘
dashboard
:=
admin
.
Group
(
"/dashboard"
)
{
dashboard
.
GET
(
"/stats"
,
h
.
Admin
.
Dashboard
.
GetStats
)
dashboard
.
GET
(
"/realtime"
,
h
.
Admin
.
Dashboard
.
GetRealtimeMetrics
)
dashboard
.
GET
(
"/trend"
,
h
.
Admin
.
Dashboard
.
GetUsageTrend
)
dashboard
.
GET
(
"/models"
,
h
.
Admin
.
Dashboard
.
GetModelStats
)
dashboard
.
GET
(
"/api-keys-trend"
,
h
.
Admin
.
Dashboard
.
GetApiKeyUsageTrend
)
dashboard
.
GET
(
"/users-trend"
,
h
.
Admin
.
Dashboard
.
GetUserUsageTrend
)
dashboard
.
POST
(
"/users-usage"
,
h
.
Admin
.
Dashboard
.
GetBatchUsersUsage
)
dashboard
.
POST
(
"/api-keys-usage"
,
h
.
Admin
.
Dashboard
.
GetBatchApiKeysUsage
)
}
// 用户管理
users
:=
admin
.
Group
(
"/users"
)
{
users
.
GET
(
""
,
h
.
Admin
.
User
.
List
)
users
.
GET
(
"/:id"
,
h
.
Admin
.
User
.
GetByID
)
users
.
POST
(
""
,
h
.
Admin
.
User
.
Create
)
users
.
PUT
(
"/:id"
,
h
.
Admin
.
User
.
Update
)
users
.
DELETE
(
"/:id"
,
h
.
Admin
.
User
.
Delete
)
users
.
POST
(
"/:id/balance"
,
h
.
Admin
.
User
.
UpdateBalance
)
users
.
GET
(
"/:id/api-keys"
,
h
.
Admin
.
User
.
GetUserAPIKeys
)
users
.
GET
(
"/:id/usage"
,
h
.
Admin
.
User
.
GetUserUsage
)
}
// 分组管理
groups
:=
admin
.
Group
(
"/groups"
)
{
groups
.
GET
(
""
,
h
.
Admin
.
Group
.
List
)
groups
.
GET
(
"/all"
,
h
.
Admin
.
Group
.
GetAll
)
groups
.
GET
(
"/:id"
,
h
.
Admin
.
Group
.
GetByID
)
groups
.
POST
(
""
,
h
.
Admin
.
Group
.
Create
)
groups
.
PUT
(
"/:id"
,
h
.
Admin
.
Group
.
Update
)
groups
.
DELETE
(
"/:id"
,
h
.
Admin
.
Group
.
Delete
)
groups
.
GET
(
"/:id/stats"
,
h
.
Admin
.
Group
.
GetStats
)
groups
.
GET
(
"/:id/api-keys"
,
h
.
Admin
.
Group
.
GetGroupAPIKeys
)
}
// 账号管理
accounts
:=
admin
.
Group
(
"/accounts"
)
{
accounts
.
GET
(
""
,
h
.
Admin
.
Account
.
List
)
accounts
.
GET
(
"/:id"
,
h
.
Admin
.
Account
.
GetByID
)
accounts
.
POST
(
""
,
h
.
Admin
.
Account
.
Create
)
accounts
.
POST
(
"/sync/crs"
,
h
.
Admin
.
Account
.
SyncFromCRS
)
accounts
.
PUT
(
"/:id"
,
h
.
Admin
.
Account
.
Update
)
accounts
.
DELETE
(
"/:id"
,
h
.
Admin
.
Account
.
Delete
)
accounts
.
POST
(
"/:id/test"
,
h
.
Admin
.
Account
.
Test
)
accounts
.
POST
(
"/:id/refresh"
,
h
.
Admin
.
Account
.
Refresh
)
accounts
.
GET
(
"/:id/stats"
,
h
.
Admin
.
Account
.
GetStats
)
accounts
.
POST
(
"/:id/clear-error"
,
h
.
Admin
.
Account
.
ClearError
)
accounts
.
GET
(
"/:id/usage"
,
h
.
Admin
.
Account
.
GetUsage
)
accounts
.
GET
(
"/:id/today-stats"
,
h
.
Admin
.
Account
.
GetTodayStats
)
accounts
.
POST
(
"/:id/clear-rate-limit"
,
h
.
Admin
.
Account
.
ClearRateLimit
)
accounts
.
POST
(
"/:id/schedulable"
,
h
.
Admin
.
Account
.
SetSchedulable
)
accounts
.
GET
(
"/:id/models"
,
h
.
Admin
.
Account
.
GetAvailableModels
)
accounts
.
POST
(
"/batch"
,
h
.
Admin
.
Account
.
BatchCreate
)
accounts
.
POST
(
"/batch-update-credentials"
,
h
.
Admin
.
Account
.
BatchUpdateCredentials
)
accounts
.
POST
(
"/bulk-update"
,
h
.
Admin
.
Account
.
BulkUpdate
)
// Claude OAuth routes
accounts
.
POST
(
"/generate-auth-url"
,
h
.
Admin
.
OAuth
.
GenerateAuthURL
)
accounts
.
POST
(
"/generate-setup-token-url"
,
h
.
Admin
.
OAuth
.
GenerateSetupTokenURL
)
accounts
.
POST
(
"/exchange-code"
,
h
.
Admin
.
OAuth
.
ExchangeCode
)
accounts
.
POST
(
"/exchange-setup-token-code"
,
h
.
Admin
.
OAuth
.
ExchangeSetupTokenCode
)
accounts
.
POST
(
"/cookie-auth"
,
h
.
Admin
.
OAuth
.
CookieAuth
)
accounts
.
POST
(
"/setup-token-cookie-auth"
,
h
.
Admin
.
OAuth
.
SetupTokenCookieAuth
)
}
// OpenAI OAuth routes
openai
:=
admin
.
Group
(
"/openai"
)
{
openai
.
POST
(
"/generate-auth-url"
,
h
.
Admin
.
OpenAIOAuth
.
GenerateAuthURL
)
openai
.
POST
(
"/exchange-code"
,
h
.
Admin
.
OpenAIOAuth
.
ExchangeCode
)
openai
.
POST
(
"/refresh-token"
,
h
.
Admin
.
OpenAIOAuth
.
RefreshToken
)
openai
.
POST
(
"/accounts/:id/refresh"
,
h
.
Admin
.
OpenAIOAuth
.
RefreshAccountToken
)
openai
.
POST
(
"/create-from-oauth"
,
h
.
Admin
.
OpenAIOAuth
.
CreateAccountFromOAuth
)
}
// 代理管理
proxies
:=
admin
.
Group
(
"/proxies"
)
{
proxies
.
GET
(
""
,
h
.
Admin
.
Proxy
.
List
)
proxies
.
GET
(
"/all"
,
h
.
Admin
.
Proxy
.
GetAll
)
proxies
.
GET
(
"/:id"
,
h
.
Admin
.
Proxy
.
GetByID
)
proxies
.
POST
(
""
,
h
.
Admin
.
Proxy
.
Create
)
proxies
.
PUT
(
"/:id"
,
h
.
Admin
.
Proxy
.
Update
)
proxies
.
DELETE
(
"/:id"
,
h
.
Admin
.
Proxy
.
Delete
)
proxies
.
POST
(
"/:id/test"
,
h
.
Admin
.
Proxy
.
Test
)
proxies
.
GET
(
"/:id/stats"
,
h
.
Admin
.
Proxy
.
GetStats
)
proxies
.
GET
(
"/:id/accounts"
,
h
.
Admin
.
Proxy
.
GetProxyAccounts
)
proxies
.
POST
(
"/batch"
,
h
.
Admin
.
Proxy
.
BatchCreate
)
}
// 卡密管理
codes
:=
admin
.
Group
(
"/redeem-codes"
)
{
codes
.
GET
(
""
,
h
.
Admin
.
Redeem
.
List
)
codes
.
GET
(
"/stats"
,
h
.
Admin
.
Redeem
.
GetStats
)
codes
.
GET
(
"/export"
,
h
.
Admin
.
Redeem
.
Export
)
codes
.
GET
(
"/:id"
,
h
.
Admin
.
Redeem
.
GetByID
)
codes
.
POST
(
"/generate"
,
h
.
Admin
.
Redeem
.
Generate
)
codes
.
DELETE
(
"/:id"
,
h
.
Admin
.
Redeem
.
Delete
)
codes
.
POST
(
"/batch-delete"
,
h
.
Admin
.
Redeem
.
BatchDelete
)
codes
.
POST
(
"/:id/expire"
,
h
.
Admin
.
Redeem
.
Expire
)
}
// 系统设置
adminSettings
:=
admin
.
Group
(
"/settings"
)
{
adminSettings
.
GET
(
""
,
h
.
Admin
.
Setting
.
GetSettings
)
adminSettings
.
PUT
(
""
,
h
.
Admin
.
Setting
.
UpdateSettings
)
adminSettings
.
POST
(
"/test-smtp"
,
h
.
Admin
.
Setting
.
TestSmtpConnection
)
adminSettings
.
POST
(
"/send-test-email"
,
h
.
Admin
.
Setting
.
SendTestEmail
)
// Admin API Key 管理
adminSettings
.
GET
(
"/admin-api-key"
,
h
.
Admin
.
Setting
.
GetAdminApiKey
)
adminSettings
.
POST
(
"/admin-api-key/regenerate"
,
h
.
Admin
.
Setting
.
RegenerateAdminApiKey
)
adminSettings
.
DELETE
(
"/admin-api-key"
,
h
.
Admin
.
Setting
.
DeleteAdminApiKey
)
}
// 系统管理
system
:=
admin
.
Group
(
"/system"
)
{
system
.
GET
(
"/version"
,
h
.
Admin
.
System
.
GetVersion
)
system
.
GET
(
"/check-updates"
,
h
.
Admin
.
System
.
CheckUpdates
)
system
.
POST
(
"/update"
,
h
.
Admin
.
System
.
PerformUpdate
)
system
.
POST
(
"/rollback"
,
h
.
Admin
.
System
.
Rollback
)
system
.
POST
(
"/restart"
,
h
.
Admin
.
System
.
RestartService
)
}
// 订阅管理
subscriptions
:=
admin
.
Group
(
"/subscriptions"
)
{
subscriptions
.
GET
(
""
,
h
.
Admin
.
Subscription
.
List
)
subscriptions
.
GET
(
"/:id"
,
h
.
Admin
.
Subscription
.
GetByID
)
subscriptions
.
GET
(
"/:id/progress"
,
h
.
Admin
.
Subscription
.
GetProgress
)
subscriptions
.
POST
(
"/assign"
,
h
.
Admin
.
Subscription
.
Assign
)
subscriptions
.
POST
(
"/bulk-assign"
,
h
.
Admin
.
Subscription
.
BulkAssign
)
subscriptions
.
POST
(
"/:id/extend"
,
h
.
Admin
.
Subscription
.
Extend
)
subscriptions
.
DELETE
(
"/:id"
,
h
.
Admin
.
Subscription
.
Revoke
)
}
// 分组下的订阅列表
admin
.
GET
(
"/groups/:id/subscriptions"
,
h
.
Admin
.
Subscription
.
ListByGroup
)
// 用户下的订阅列表
admin
.
GET
(
"/users/:id/subscriptions"
,
h
.
Admin
.
Subscription
.
ListByUser
)
// 使用记录管理
usage
:=
admin
.
Group
(
"/usage"
)
{
usage
.
GET
(
""
,
h
.
Admin
.
Usage
.
List
)
usage
.
GET
(
"/stats"
,
h
.
Admin
.
Usage
.
Stats
)
usage
.
GET
(
"/search-users"
,
h
.
Admin
.
Usage
.
SearchUsers
)
usage
.
GET
(
"/search-api-keys"
,
h
.
Admin
.
Usage
.
SearchApiKeys
)
}
}
}
// API网关(Claude API兼容)
gateway
:=
r
.
Group
(
"/v1"
)
gateway
.
Use
(
middleware
.
ApiKeyAuthWithSubscription
(
s
.
ApiKey
,
s
.
Subscription
))
{
gateway
.
POST
(
"/messages"
,
h
.
Gateway
.
Messages
)
gateway
.
POST
(
"/messages/count_tokens"
,
h
.
Gateway
.
CountTokens
)
gateway
.
GET
(
"/models"
,
h
.
Gateway
.
Models
)
gateway
.
GET
(
"/usage"
,
h
.
Gateway
.
Usage
)
// OpenAI Responses API
gateway
.
POST
(
"/responses"
,
h
.
OpenAIGateway
.
Responses
)
}
// OpenAI Responses API(不带v1前缀的别名)
r
.
POST
(
"/responses"
,
middleware
.
ApiKeyAuthWithSubscription
(
s
.
ApiKey
,
s
.
Subscription
),
h
.
OpenAIGateway
.
Responses
)
// 注册各模块路由
routes
.
RegisterAuthRoutes
(
v1
,
h
,
jwtAuth
)
routes
.
RegisterUserRoutes
(
v1
,
h
,
jwtAuth
)
routes
.
RegisterAdminRoutes
(
v1
,
h
,
adminAuth
)
routes
.
RegisterGatewayRoutes
(
r
,
h
,
apiKeyAuth
)
}
backend/internal/server/routes/admin.go
0 → 100644
View file @
57fd1722
package
routes
import
(
"github.com/Wei-Shaw/sub2api/internal/handler"
"github.com/Wei-Shaw/sub2api/internal/server/middleware"
"github.com/gin-gonic/gin"
)
// RegisterAdminRoutes 注册管理员路由
func
RegisterAdminRoutes
(
v1
*
gin
.
RouterGroup
,
h
*
handler
.
Handlers
,
adminAuth
middleware
.
AdminAuthMiddleware
,
)
{
admin
:=
v1
.
Group
(
"/admin"
)
admin
.
Use
(
gin
.
HandlerFunc
(
adminAuth
))
{
// 仪表盘
registerDashboardRoutes
(
admin
,
h
)
// 用户管理
registerUserManagementRoutes
(
admin
,
h
)
// 分组管理
registerGroupRoutes
(
admin
,
h
)
// 账号管理
registerAccountRoutes
(
admin
,
h
)
// OpenAI OAuth
registerOpenAIOAuthRoutes
(
admin
,
h
)
// 代理管理
registerProxyRoutes
(
admin
,
h
)
// 卡密管理
registerRedeemCodeRoutes
(
admin
,
h
)
// 系统设置
registerSettingsRoutes
(
admin
,
h
)
// 系统管理
registerSystemRoutes
(
admin
,
h
)
// 订阅管理
registerSubscriptionRoutes
(
admin
,
h
)
// 使用记录管理
registerUsageRoutes
(
admin
,
h
)
}
}
func
registerDashboardRoutes
(
admin
*
gin
.
RouterGroup
,
h
*
handler
.
Handlers
)
{
dashboard
:=
admin
.
Group
(
"/dashboard"
)
{
dashboard
.
GET
(
"/stats"
,
h
.
Admin
.
Dashboard
.
GetStats
)
dashboard
.
GET
(
"/realtime"
,
h
.
Admin
.
Dashboard
.
GetRealtimeMetrics
)
dashboard
.
GET
(
"/trend"
,
h
.
Admin
.
Dashboard
.
GetUsageTrend
)
dashboard
.
GET
(
"/models"
,
h
.
Admin
.
Dashboard
.
GetModelStats
)
dashboard
.
GET
(
"/api-keys-trend"
,
h
.
Admin
.
Dashboard
.
GetApiKeyUsageTrend
)
dashboard
.
GET
(
"/users-trend"
,
h
.
Admin
.
Dashboard
.
GetUserUsageTrend
)
dashboard
.
POST
(
"/users-usage"
,
h
.
Admin
.
Dashboard
.
GetBatchUsersUsage
)
dashboard
.
POST
(
"/api-keys-usage"
,
h
.
Admin
.
Dashboard
.
GetBatchApiKeysUsage
)
}
}
func
registerUserManagementRoutes
(
admin
*
gin
.
RouterGroup
,
h
*
handler
.
Handlers
)
{
users
:=
admin
.
Group
(
"/users"
)
{
users
.
GET
(
""
,
h
.
Admin
.
User
.
List
)
users
.
GET
(
"/:id"
,
h
.
Admin
.
User
.
GetByID
)
users
.
POST
(
""
,
h
.
Admin
.
User
.
Create
)
users
.
PUT
(
"/:id"
,
h
.
Admin
.
User
.
Update
)
users
.
DELETE
(
"/:id"
,
h
.
Admin
.
User
.
Delete
)
users
.
POST
(
"/:id/balance"
,
h
.
Admin
.
User
.
UpdateBalance
)
users
.
GET
(
"/:id/api-keys"
,
h
.
Admin
.
User
.
GetUserAPIKeys
)
users
.
GET
(
"/:id/usage"
,
h
.
Admin
.
User
.
GetUserUsage
)
}
}
func
registerGroupRoutes
(
admin
*
gin
.
RouterGroup
,
h
*
handler
.
Handlers
)
{
groups
:=
admin
.
Group
(
"/groups"
)
{
groups
.
GET
(
""
,
h
.
Admin
.
Group
.
List
)
groups
.
GET
(
"/all"
,
h
.
Admin
.
Group
.
GetAll
)
groups
.
GET
(
"/:id"
,
h
.
Admin
.
Group
.
GetByID
)
groups
.
POST
(
""
,
h
.
Admin
.
Group
.
Create
)
groups
.
PUT
(
"/:id"
,
h
.
Admin
.
Group
.
Update
)
groups
.
DELETE
(
"/:id"
,
h
.
Admin
.
Group
.
Delete
)
groups
.
GET
(
"/:id/stats"
,
h
.
Admin
.
Group
.
GetStats
)
groups
.
GET
(
"/:id/api-keys"
,
h
.
Admin
.
Group
.
GetGroupAPIKeys
)
}
}
func
registerAccountRoutes
(
admin
*
gin
.
RouterGroup
,
h
*
handler
.
Handlers
)
{
accounts
:=
admin
.
Group
(
"/accounts"
)
{
accounts
.
GET
(
""
,
h
.
Admin
.
Account
.
List
)
accounts
.
GET
(
"/:id"
,
h
.
Admin
.
Account
.
GetByID
)
accounts
.
POST
(
""
,
h
.
Admin
.
Account
.
Create
)
accounts
.
POST
(
"/sync/crs"
,
h
.
Admin
.
Account
.
SyncFromCRS
)
accounts
.
PUT
(
"/:id"
,
h
.
Admin
.
Account
.
Update
)
accounts
.
DELETE
(
"/:id"
,
h
.
Admin
.
Account
.
Delete
)
accounts
.
POST
(
"/:id/test"
,
h
.
Admin
.
Account
.
Test
)
accounts
.
POST
(
"/:id/refresh"
,
h
.
Admin
.
Account
.
Refresh
)
accounts
.
GET
(
"/:id/stats"
,
h
.
Admin
.
Account
.
GetStats
)
accounts
.
POST
(
"/:id/clear-error"
,
h
.
Admin
.
Account
.
ClearError
)
accounts
.
GET
(
"/:id/usage"
,
h
.
Admin
.
Account
.
GetUsage
)
accounts
.
GET
(
"/:id/today-stats"
,
h
.
Admin
.
Account
.
GetTodayStats
)
accounts
.
POST
(
"/:id/clear-rate-limit"
,
h
.
Admin
.
Account
.
ClearRateLimit
)
accounts
.
POST
(
"/:id/schedulable"
,
h
.
Admin
.
Account
.
SetSchedulable
)
accounts
.
GET
(
"/:id/models"
,
h
.
Admin
.
Account
.
GetAvailableModels
)
accounts
.
POST
(
"/batch"
,
h
.
Admin
.
Account
.
BatchCreate
)
accounts
.
POST
(
"/batch-update-credentials"
,
h
.
Admin
.
Account
.
BatchUpdateCredentials
)
accounts
.
POST
(
"/bulk-update"
,
h
.
Admin
.
Account
.
BulkUpdate
)
// Claude OAuth routes
accounts
.
POST
(
"/generate-auth-url"
,
h
.
Admin
.
OAuth
.
GenerateAuthURL
)
accounts
.
POST
(
"/generate-setup-token-url"
,
h
.
Admin
.
OAuth
.
GenerateSetupTokenURL
)
accounts
.
POST
(
"/exchange-code"
,
h
.
Admin
.
OAuth
.
ExchangeCode
)
accounts
.
POST
(
"/exchange-setup-token-code"
,
h
.
Admin
.
OAuth
.
ExchangeSetupTokenCode
)
accounts
.
POST
(
"/cookie-auth"
,
h
.
Admin
.
OAuth
.
CookieAuth
)
accounts
.
POST
(
"/setup-token-cookie-auth"
,
h
.
Admin
.
OAuth
.
SetupTokenCookieAuth
)
}
}
func
registerOpenAIOAuthRoutes
(
admin
*
gin
.
RouterGroup
,
h
*
handler
.
Handlers
)
{
openai
:=
admin
.
Group
(
"/openai"
)
{
openai
.
POST
(
"/generate-auth-url"
,
h
.
Admin
.
OpenAIOAuth
.
GenerateAuthURL
)
openai
.
POST
(
"/exchange-code"
,
h
.
Admin
.
OpenAIOAuth
.
ExchangeCode
)
openai
.
POST
(
"/refresh-token"
,
h
.
Admin
.
OpenAIOAuth
.
RefreshToken
)
openai
.
POST
(
"/accounts/:id/refresh"
,
h
.
Admin
.
OpenAIOAuth
.
RefreshAccountToken
)
openai
.
POST
(
"/create-from-oauth"
,
h
.
Admin
.
OpenAIOAuth
.
CreateAccountFromOAuth
)
}
}
func
registerProxyRoutes
(
admin
*
gin
.
RouterGroup
,
h
*
handler
.
Handlers
)
{
proxies
:=
admin
.
Group
(
"/proxies"
)
{
proxies
.
GET
(
""
,
h
.
Admin
.
Proxy
.
List
)
proxies
.
GET
(
"/all"
,
h
.
Admin
.
Proxy
.
GetAll
)
proxies
.
GET
(
"/:id"
,
h
.
Admin
.
Proxy
.
GetByID
)
proxies
.
POST
(
""
,
h
.
Admin
.
Proxy
.
Create
)
proxies
.
PUT
(
"/:id"
,
h
.
Admin
.
Proxy
.
Update
)
proxies
.
DELETE
(
"/:id"
,
h
.
Admin
.
Proxy
.
Delete
)
proxies
.
POST
(
"/:id/test"
,
h
.
Admin
.
Proxy
.
Test
)
proxies
.
GET
(
"/:id/stats"
,
h
.
Admin
.
Proxy
.
GetStats
)
proxies
.
GET
(
"/:id/accounts"
,
h
.
Admin
.
Proxy
.
GetProxyAccounts
)
proxies
.
POST
(
"/batch"
,
h
.
Admin
.
Proxy
.
BatchCreate
)
}
}
func
registerRedeemCodeRoutes
(
admin
*
gin
.
RouterGroup
,
h
*
handler
.
Handlers
)
{
codes
:=
admin
.
Group
(
"/redeem-codes"
)
{
codes
.
GET
(
""
,
h
.
Admin
.
Redeem
.
List
)
codes
.
GET
(
"/stats"
,
h
.
Admin
.
Redeem
.
GetStats
)
codes
.
GET
(
"/export"
,
h
.
Admin
.
Redeem
.
Export
)
codes
.
GET
(
"/:id"
,
h
.
Admin
.
Redeem
.
GetByID
)
codes
.
POST
(
"/generate"
,
h
.
Admin
.
Redeem
.
Generate
)
codes
.
DELETE
(
"/:id"
,
h
.
Admin
.
Redeem
.
Delete
)
codes
.
POST
(
"/batch-delete"
,
h
.
Admin
.
Redeem
.
BatchDelete
)
codes
.
POST
(
"/:id/expire"
,
h
.
Admin
.
Redeem
.
Expire
)
}
}
func
registerSettingsRoutes
(
admin
*
gin
.
RouterGroup
,
h
*
handler
.
Handlers
)
{
adminSettings
:=
admin
.
Group
(
"/settings"
)
{
adminSettings
.
GET
(
""
,
h
.
Admin
.
Setting
.
GetSettings
)
adminSettings
.
PUT
(
""
,
h
.
Admin
.
Setting
.
UpdateSettings
)
adminSettings
.
POST
(
"/test-smtp"
,
h
.
Admin
.
Setting
.
TestSmtpConnection
)
adminSettings
.
POST
(
"/send-test-email"
,
h
.
Admin
.
Setting
.
SendTestEmail
)
// Admin API Key 管理
adminSettings
.
GET
(
"/admin-api-key"
,
h
.
Admin
.
Setting
.
GetAdminApiKey
)
adminSettings
.
POST
(
"/admin-api-key/regenerate"
,
h
.
Admin
.
Setting
.
RegenerateAdminApiKey
)
adminSettings
.
DELETE
(
"/admin-api-key"
,
h
.
Admin
.
Setting
.
DeleteAdminApiKey
)
}
}
func
registerSystemRoutes
(
admin
*
gin
.
RouterGroup
,
h
*
handler
.
Handlers
)
{
system
:=
admin
.
Group
(
"/system"
)
{
system
.
GET
(
"/version"
,
h
.
Admin
.
System
.
GetVersion
)
system
.
GET
(
"/check-updates"
,
h
.
Admin
.
System
.
CheckUpdates
)
system
.
POST
(
"/update"
,
h
.
Admin
.
System
.
PerformUpdate
)
system
.
POST
(
"/rollback"
,
h
.
Admin
.
System
.
Rollback
)
system
.
POST
(
"/restart"
,
h
.
Admin
.
System
.
RestartService
)
}
}
func
registerSubscriptionRoutes
(
admin
*
gin
.
RouterGroup
,
h
*
handler
.
Handlers
)
{
subscriptions
:=
admin
.
Group
(
"/subscriptions"
)
{
subscriptions
.
GET
(
""
,
h
.
Admin
.
Subscription
.
List
)
subscriptions
.
GET
(
"/:id"
,
h
.
Admin
.
Subscription
.
GetByID
)
subscriptions
.
GET
(
"/:id/progress"
,
h
.
Admin
.
Subscription
.
GetProgress
)
subscriptions
.
POST
(
"/assign"
,
h
.
Admin
.
Subscription
.
Assign
)
subscriptions
.
POST
(
"/bulk-assign"
,
h
.
Admin
.
Subscription
.
BulkAssign
)
subscriptions
.
POST
(
"/:id/extend"
,
h
.
Admin
.
Subscription
.
Extend
)
subscriptions
.
DELETE
(
"/:id"
,
h
.
Admin
.
Subscription
.
Revoke
)
}
// 分组下的订阅列表
admin
.
GET
(
"/groups/:id/subscriptions"
,
h
.
Admin
.
Subscription
.
ListByGroup
)
// 用户下的订阅列表
admin
.
GET
(
"/users/:id/subscriptions"
,
h
.
Admin
.
Subscription
.
ListByUser
)
}
func
registerUsageRoutes
(
admin
*
gin
.
RouterGroup
,
h
*
handler
.
Handlers
)
{
usage
:=
admin
.
Group
(
"/usage"
)
{
usage
.
GET
(
""
,
h
.
Admin
.
Usage
.
List
)
usage
.
GET
(
"/stats"
,
h
.
Admin
.
Usage
.
Stats
)
usage
.
GET
(
"/search-users"
,
h
.
Admin
.
Usage
.
SearchUsers
)
usage
.
GET
(
"/search-api-keys"
,
h
.
Admin
.
Usage
.
SearchApiKeys
)
}
}
Prev
1
2
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment