mirror of
https://github.com/woodchen-ink/random-api-go.git
synced 2025-07-18 05:42:01 +08:00
257 lines
8.3 KiB
Go
257 lines
8.3 KiB
Go
package router
|
||
|
||
import (
|
||
"net/http"
|
||
"random-api-go/middleware"
|
||
"strings"
|
||
)
|
||
|
||
type Router struct {
|
||
mux *http.ServeMux
|
||
staticHandler StaticHandler
|
||
authMiddleware *middleware.AuthMiddleware
|
||
middlewares []func(http.Handler) http.Handler
|
||
}
|
||
|
||
// Handler 接口定义处理器需要的方法
|
||
type Handler interface {
|
||
// API请求处理
|
||
HandleAPIRequest(w http.ResponseWriter, r *http.Request)
|
||
// 统计相关
|
||
HandleStats(w http.ResponseWriter, r *http.Request)
|
||
HandleURLStats(w http.ResponseWriter, r *http.Request)
|
||
HandleMetrics(w http.ResponseWriter, r *http.Request)
|
||
// 健康检查
|
||
HandleHealth(w http.ResponseWriter, r *http.Request)
|
||
// 公开端点
|
||
HandlePublicEndpoints(w http.ResponseWriter, r *http.Request)
|
||
// 公开首页配置
|
||
HandlePublicHomeConfig(w http.ResponseWriter, r *http.Request)
|
||
}
|
||
|
||
// StaticHandler 接口定义静态文件处理器需要的方法
|
||
type StaticHandler interface {
|
||
ServeStatic(w http.ResponseWriter, r *http.Request)
|
||
}
|
||
|
||
// AdminHandler 接口定义管理后台处理器需要的方法
|
||
type AdminHandler interface {
|
||
// OAuth相关
|
||
GetOAuthConfig(w http.ResponseWriter, r *http.Request)
|
||
VerifyOAuthToken(w http.ResponseWriter, r *http.Request)
|
||
HandleOAuthCallback(w http.ResponseWriter, r *http.Request)
|
||
|
||
// 端点管理
|
||
HandleEndpoints(w http.ResponseWriter, r *http.Request)
|
||
HandleEndpointByID(w http.ResponseWriter, r *http.Request)
|
||
HandleEndpointDataSources(w http.ResponseWriter, r *http.Request)
|
||
UpdateEndpointSortOrder(w http.ResponseWriter, r *http.Request)
|
||
|
||
// 数据源管理
|
||
CreateDataSource(w http.ResponseWriter, r *http.Request)
|
||
HandleDataSourceByID(w http.ResponseWriter, r *http.Request)
|
||
SyncDataSource(w http.ResponseWriter, r *http.Request)
|
||
|
||
// URL替换规则
|
||
ListURLReplaceRules(w http.ResponseWriter, r *http.Request)
|
||
CreateURLReplaceRule(w http.ResponseWriter, r *http.Request)
|
||
HandleURLReplaceRuleByID(w http.ResponseWriter, r *http.Request)
|
||
|
||
// 首页配置
|
||
GetHomePageConfig(w http.ResponseWriter, r *http.Request)
|
||
UpdateHomePageConfig(w http.ResponseWriter, r *http.Request)
|
||
|
||
// 通用配置管理
|
||
ListConfigs(w http.ResponseWriter, r *http.Request)
|
||
CreateOrUpdateConfig(w http.ResponseWriter, r *http.Request)
|
||
DeleteConfigByKey(w http.ResponseWriter, r *http.Request)
|
||
|
||
// 域名统计
|
||
GetDomainStats(w http.ResponseWriter, r *http.Request)
|
||
}
|
||
|
||
func New() *Router {
|
||
return &Router{
|
||
mux: http.NewServeMux(),
|
||
authMiddleware: middleware.NewAuthMiddleware(),
|
||
middlewares: []func(http.Handler) http.Handler{
|
||
middleware.MetricsMiddleware,
|
||
middleware.RateLimiter,
|
||
},
|
||
}
|
||
}
|
||
|
||
// SetupAllRoutes 统一设置所有路由
|
||
func (r *Router) SetupAllRoutes(handler Handler, adminHandler AdminHandler, staticHandler StaticHandler) {
|
||
// 设置公开API路由
|
||
r.HandleFunc("/", handler.HandleAPIRequest)
|
||
r.HandleFunc("/api/stats", handler.HandleStats)
|
||
r.HandleFunc("/api/urlstats", handler.HandleURLStats)
|
||
r.HandleFunc("/api/metrics", handler.HandleMetrics)
|
||
r.HandleFunc("/api/health", handler.HandleHealth)
|
||
r.HandleFunc("/api/endpoints", handler.HandlePublicEndpoints)
|
||
r.HandleFunc("/api/home-config", handler.HandlePublicHomeConfig)
|
||
|
||
// 设置公开的OAuth配置路由
|
||
r.HandleFunc("/api/oauth-config", adminHandler.GetOAuthConfig)
|
||
|
||
// 设置管理后台路由
|
||
r.setupAdminRoutes(adminHandler)
|
||
|
||
// 设置静态文件路由
|
||
if staticHandler != nil {
|
||
r.staticHandler = staticHandler
|
||
}
|
||
}
|
||
|
||
// setupAdminRoutes 设置管理后台路由(私有方法)
|
||
func (r *Router) setupAdminRoutes(adminHandler AdminHandler) {
|
||
// OAuth令牌验证API(保留,以防需要)- 不需要认证
|
||
r.HandleFunc("/api/admin/oauth-verify", adminHandler.VerifyOAuthToken)
|
||
// OAuth回调处理(使用API前缀以便区分前后端)- 不需要认证
|
||
r.HandleFunc("/api/admin/oauth/callback", adminHandler.HandleOAuthCallback)
|
||
|
||
// 管理后台API路由 - 需要认证
|
||
r.HandleFunc("/api/admin/endpoints", r.authMiddleware.RequireAuth(adminHandler.HandleEndpoints))
|
||
|
||
// 端点排序路由 - 需要认证
|
||
r.HandleFunc("/api/admin/endpoints/sort-order", r.authMiddleware.RequireAuth(adminHandler.UpdateEndpointSortOrder))
|
||
|
||
// 数据源路由 - 需要认证
|
||
r.HandleFunc("/api/admin/data-sources", r.authMiddleware.RequireAuth(adminHandler.CreateDataSource))
|
||
|
||
// 端点相关路由 - 需要认证
|
||
r.HandleFunc("/api/admin/endpoints/", r.authMiddleware.RequireAuth(func(w http.ResponseWriter, r *http.Request) {
|
||
path := r.URL.Path
|
||
if strings.Contains(path, "/data-sources") {
|
||
adminHandler.HandleEndpointDataSources(w, r)
|
||
} else {
|
||
adminHandler.HandleEndpointByID(w, r)
|
||
}
|
||
}))
|
||
|
||
// 数据源操作路由 - 需要认证
|
||
r.HandleFunc("/api/admin/data-sources/", r.authMiddleware.RequireAuth(func(w http.ResponseWriter, r *http.Request) {
|
||
path := r.URL.Path
|
||
if strings.Contains(path, "/sync") {
|
||
adminHandler.SyncDataSource(w, r)
|
||
} else {
|
||
adminHandler.HandleDataSourceByID(w, r)
|
||
}
|
||
}))
|
||
|
||
// URL替换规则路由 - 需要认证
|
||
r.HandleFunc("/api/admin/url-replace-rules", r.authMiddleware.RequireAuth(func(w http.ResponseWriter, r *http.Request) {
|
||
if r.Method == http.MethodGet {
|
||
adminHandler.ListURLReplaceRules(w, r)
|
||
} else if r.Method == http.MethodPost {
|
||
adminHandler.CreateURLReplaceRule(w, r)
|
||
} else {
|
||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||
}
|
||
}))
|
||
r.HandleFunc("/api/admin/url-replace-rules/", r.authMiddleware.RequireAuth(adminHandler.HandleURLReplaceRuleByID))
|
||
|
||
// 首页配置路由 - 需要认证
|
||
r.HandleFunc("/api/admin/home-config", r.authMiddleware.RequireAuth(func(w http.ResponseWriter, r *http.Request) {
|
||
if r.Method == http.MethodGet {
|
||
adminHandler.GetHomePageConfig(w, r)
|
||
} else {
|
||
adminHandler.UpdateHomePageConfig(w, r)
|
||
}
|
||
}))
|
||
|
||
// 通用配置管理路由 - 需要认证
|
||
r.HandleFunc("/api/admin/configs", r.authMiddleware.RequireAuth(adminHandler.ListConfigs))
|
||
r.HandleFunc("/api/admin/configs/", r.authMiddleware.RequireAuth(func(w http.ResponseWriter, r *http.Request) {
|
||
if r.Method == http.MethodDelete {
|
||
adminHandler.DeleteConfigByKey(w, r)
|
||
} else {
|
||
adminHandler.CreateOrUpdateConfig(w, r)
|
||
}
|
||
}))
|
||
|
||
// 域名统计路由 - 需要认证
|
||
r.HandleFunc("/api/admin/domain-stats", r.authMiddleware.RequireAuth(adminHandler.GetDomainStats))
|
||
}
|
||
|
||
func (r *Router) HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request)) {
|
||
r.mux.HandleFunc(pattern, handler)
|
||
}
|
||
|
||
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||
// 首先检查是否是静态文件请求或前端路由
|
||
if r.staticHandler != nil && r.shouldServeStatic(req.URL.Path) {
|
||
r.staticHandler.ServeStatic(w, req)
|
||
return
|
||
}
|
||
|
||
// 应用中间件链,然后使用路由处理
|
||
handler := http.Handler(r.mux)
|
||
|
||
// 反向应用中间件(因为要从最外层开始包装)
|
||
for i := len(r.middlewares) - 1; i >= 0; i-- {
|
||
handler = r.middlewares[i](handler)
|
||
}
|
||
|
||
handler.ServeHTTP(w, req)
|
||
}
|
||
|
||
// shouldServeStatic 判断是否应该由静态文件处理器处理
|
||
func (r *Router) shouldServeStatic(path string) bool {
|
||
// API 路径不由静态文件处理器处理
|
||
if strings.HasPrefix(path, "/api/") {
|
||
return false
|
||
}
|
||
|
||
// 根路径由静态文件处理器处理(返回首页)
|
||
if path == "/" {
|
||
return true
|
||
}
|
||
|
||
// 前端路由(以 /admin 开头)由静态文件处理器处理
|
||
if strings.HasPrefix(path, "/admin") {
|
||
return true
|
||
}
|
||
|
||
// 静态资源文件(包含文件扩展名或特定前缀)
|
||
if strings.HasPrefix(path, "/_next/") ||
|
||
strings.HasPrefix(path, "/static/") ||
|
||
strings.HasPrefix(path, "/favicon.ico") ||
|
||
r.hasFileExtension(path) {
|
||
return true
|
||
}
|
||
|
||
// 其他路径可能是动态API端点,不由静态文件处理器处理
|
||
return false
|
||
}
|
||
|
||
// hasFileExtension 检查路径是否包含文件扩展名
|
||
func (r *Router) hasFileExtension(path string) bool {
|
||
// 获取路径的最后一部分
|
||
parts := strings.Split(path, "/")
|
||
if len(parts) == 0 {
|
||
return false
|
||
}
|
||
|
||
lastPart := parts[len(parts)-1]
|
||
|
||
// 检查是否包含点号且不是隐藏文件
|
||
if strings.Contains(lastPart, ".") && !strings.HasPrefix(lastPart, ".") {
|
||
// 常见的文件扩展名
|
||
commonExts := []string{
|
||
".html", ".css", ".js", ".json", ".png", ".jpg", ".jpeg",
|
||
".gif", ".svg", ".ico", ".woff", ".woff2", ".ttf", ".eot",
|
||
".txt", ".xml", ".pdf", ".zip", ".mp4", ".mp3",
|
||
}
|
||
|
||
for _, ext := range commonExts {
|
||
if strings.HasSuffix(strings.ToLower(lastPart), ext) {
|
||
return true
|
||
}
|
||
}
|
||
}
|
||
|
||
return false
|
||
}
|