Files
gpt-load/internal/router/router.go
2025-07-24 17:17:18 +08:00

192 lines
5.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package router
import (
"embed"
"gpt-load/internal/handler"
"gpt-load/internal/middleware"
"gpt-load/internal/proxy"
"gpt-load/internal/services"
"gpt-load/internal/types"
"io/fs"
"net/http"
"strings"
"time"
"github.com/gin-contrib/gzip"
"github.com/gin-contrib/static"
"github.com/gin-gonic/gin"
)
type embedFileSystem struct {
http.FileSystem
}
func (e embedFileSystem) Exists(prefix string, path string) bool {
_, err := e.Open(path)
return err == nil
}
func EmbedFolder(fsEmbed embed.FS, targetPath string) static.ServeFileSystem {
efs, err := fs.Sub(fsEmbed, targetPath)
if err != nil {
panic(err)
}
return embedFileSystem{
FileSystem: http.FS(efs),
}
}
func NewRouter(
serverHandler *handler.Server,
proxyServer *proxy.ProxyServer,
configManager types.ConfigManager,
groupManager *services.GroupManager,
buildFS embed.FS,
indexPage []byte,
) *gin.Engine {
gin.SetMode(gin.ReleaseMode)
router := gin.New()
// 注册全局中间件
router.Use(middleware.Recovery())
router.Use(middleware.ErrorHandler())
router.Use(middleware.Logger(configManager.GetLogConfig()))
router.Use(middleware.CORS(configManager.GetCORSConfig()))
router.Use(middleware.RateLimiter(configManager.GetPerformanceConfig()))
startTime := time.Now()
router.Use(func(c *gin.Context) {
c.Set("serverStartTime", startTime)
c.Next()
})
// 注册路由
registerSystemRoutes(router, serverHandler)
registerAPIRoutes(router, serverHandler, configManager)
registerProxyRoutes(router, proxyServer, groupManager)
registerFrontendRoutes(router, buildFS, indexPage)
return router
}
// registerSystemRoutes 注册系统级路由
func registerSystemRoutes(router *gin.Engine, serverHandler *handler.Server) {
router.GET("/health", serverHandler.Health)
}
// registerAPIRoutes 注册API路由
func registerAPIRoutes(
router *gin.Engine,
serverHandler *handler.Server,
configManager types.ConfigManager,
) {
api := router.Group("/api")
authConfig := configManager.GetAuthConfig()
// 公开
registerPublicAPIRoutes(api, serverHandler)
// 认证
protectedAPI := api.Group("")
protectedAPI.Use(middleware.Auth(authConfig))
registerProtectedAPIRoutes(protectedAPI, serverHandler)
}
// registerPublicAPIRoutes 公开API路由
func registerPublicAPIRoutes(api *gin.RouterGroup, serverHandler *handler.Server) {
api.POST("/auth/login", serverHandler.Login)
}
// registerProtectedAPIRoutes 认证API路由
func registerProtectedAPIRoutes(api *gin.RouterGroup, serverHandler *handler.Server) {
api.GET("/channel-types", serverHandler.CommonHandler.GetChannelTypes)
groups := api.Group("/groups")
{
groups.POST("", serverHandler.CreateGroup)
groups.GET("", serverHandler.ListGroups)
groups.GET("/list", serverHandler.List)
groups.GET("/config-options", serverHandler.GetGroupConfigOptions)
groups.PUT("/:id", serverHandler.UpdateGroup)
groups.DELETE("/:id", serverHandler.DeleteGroup)
groups.GET("/:id/stats", serverHandler.GetGroupStats)
}
// Key Management Routes
keys := api.Group("/keys")
{
keys.GET("", serverHandler.ListKeysInGroup)
keys.GET("/export", serverHandler.ExportKeys)
keys.POST("/add-multiple", serverHandler.AddMultipleKeys)
keys.POST("/add-async", serverHandler.AddMultipleKeysAsync)
keys.POST("/delete-multiple", serverHandler.DeleteMultipleKeys)
keys.POST("/restore-multiple", serverHandler.RestoreMultipleKeys)
keys.POST("/restore-all-invalid", serverHandler.RestoreAllInvalidKeys)
keys.POST("/clear-all-invalid", serverHandler.ClearAllInvalidKeys)
keys.POST("/validate-group", serverHandler.ValidateGroupKeys)
keys.POST("/test-multiple", serverHandler.TestMultipleKeys)
}
// Tasks
api.GET("/tasks/status", serverHandler.GetTaskStatus)
// 仪表板和日志
dashboard := api.Group("/dashboard")
{
dashboard.GET("/stats", serverHandler.Stats)
dashboard.GET("/chart", serverHandler.Chart)
}
// 日志
logs := api.Group("/logs")
{
logs.GET("", serverHandler.GetLogs)
logs.GET("/export", serverHandler.ExportLogs)
}
// 设置
settings := api.Group("/settings")
{
settings.GET("", serverHandler.GetSettings)
settings.PUT("", serverHandler.UpdateSettings)
}
}
// registerProxyRoutes 注册代理路由
func registerProxyRoutes(
router *gin.Engine,
proxyServer *proxy.ProxyServer,
groupManager *services.GroupManager,
) {
proxyGroup := router.Group("/proxy")
proxyGroup.Use(middleware.ProxyAuth(groupManager))
proxyGroup.Any("/:group_name/*path", proxyServer.HandleProxy)
}
// registerFrontendRoutes 注册前端路由
func registerFrontendRoutes(router *gin.Engine, buildFS embed.FS, indexPage []byte) {
router.Use(gzip.Gzip(gzip.DefaultCompression))
router.NoMethod(func(c *gin.Context) {
c.JSON(http.StatusMethodNotAllowed, gin.H{"error": "Method not allowed"})
})
// 使用静态资源缓存中间件
router.Use(middleware.StaticCache())
router.Use(static.Serve("/", EmbedFolder(buildFS, "web/dist")))
router.NoRoute(func(c *gin.Context) {
if strings.HasPrefix(c.Request.RequestURI, "/api") || strings.HasPrefix(c.Request.RequestURI, "/proxy") {
c.JSON(http.StatusNotFound, gin.H{"error": "Not Found"})
return
}
// HTML页面不缓存确保更新能及时生效
c.Header("Cache-Control", "no-cache, no-store, must-revalidate")
c.Header("Pragma", "no-cache")
c.Header("Expires", "0")
c.Data(http.StatusOK, "text/html; charset=utf-8", indexPage)
})
}