random-api-go/router/router.go

213 lines
6.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 (
"net/http"
"strings"
)
type Router struct {
mux *http.ServeMux
staticHandler StaticHandler
}
type Handler interface {
Setup(r *Router)
}
// 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)
}
func New() *Router {
return &Router{
mux: http.NewServeMux(),
}
}
func (r *Router) Setup(h Handler) {
// 设置API路由
h.Setup(r)
}
// SetupStaticRoutes 设置静态文件路由
func (r *Router) SetupStaticRoutes(staticHandler StaticHandler) {
r.staticHandler = staticHandler
}
// SetupAdminRoutes 设置管理后台路由
func (r *Router) SetupAdminRoutes(adminHandler AdminHandler) {
// OAuth配置API前端需要获取client_id等信息
r.HandleFunc("/api/admin/oauth-config", adminHandler.GetOAuthConfig)
// 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", adminHandler.HandleEndpoints)
// 端点排序路由
r.HandleFunc("/api/admin/endpoints/sort-order", adminHandler.UpdateEndpointSortOrder)
// 数据源路由 - 需要在端点路由之前注册,因为路径更具体
r.HandleFunc("/api/admin/data-sources", adminHandler.CreateDataSource)
// 端点相关路由 - 使用通配符处理所有端点相关请求
r.HandleFunc("/api/admin/endpoints/", 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/", 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", 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/", adminHandler.HandleURLReplaceRuleByID)
// 首页配置路由
r.HandleFunc("/api/admin/home-config", 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", adminHandler.ListConfigs)
r.HandleFunc("/api/admin/configs/", func(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodDelete {
adminHandler.DeleteConfigByKey(w, r)
} else {
adminHandler.CreateOrUpdateConfig(w, r)
}
})
}
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
}
// 否则使用默认的路由处理
r.mux.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
}