e7b7eff0d8
1. DevTools 启动前检查数据库状态,失败时自动尝试启动 2. ai-core 添加数据库断线重连机制 (30秒间隔) 3. Dashboard 添加数据库状态卡片 (启动/停止/重启) 4. Gateway 会话空闲超时管理 (30分钟标记空闲) 5. 会话/消息 PostgreSQL 持久化 (SessionStore + REST API) 6. 前端服务端会话持久化 + URL hash 路由 + 侧边栏管理 7. 管理员回到主对话按钮 8. IoT 设备卡片固定排序 9. 更新相关文档
123 lines
3.8 KiB
Go
123 lines
3.8 KiB
Go
package router
|
|
|
|
import (
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"github.com/yourname/cyrene-ai/gateway/internal/config"
|
|
"github.com/yourname/cyrene-ai/gateway/internal/handler"
|
|
"github.com/yourname/cyrene-ai/gateway/internal/middleware"
|
|
"github.com/yourname/cyrene-ai/gateway/internal/store"
|
|
"github.com/yourname/cyrene-ai/gateway/internal/ws"
|
|
)
|
|
|
|
// Setup 注册所有路由
|
|
func Setup(r *gin.Engine, hub *ws.Hub, cfg *config.Config, sessionStore *store.SessionStore) {
|
|
// 限流器
|
|
rateLimiter := middleware.NewRateLimiter(10, 20) // 每秒10个请求,突发20
|
|
|
|
// 初始化处理器
|
|
authHandler := handler.NewAuthHandler(cfg)
|
|
sessionHandler := handler.NewSessionHandler(hub, sessionStore)
|
|
memoryHandler := handler.NewMemoryHandler(cfg.AICoreURL)
|
|
chatHandler := handler.NewChatHandler(cfg, hub)
|
|
webhookHandler := handler.NewWebhookHandler(cfg, hub)
|
|
|
|
// ========== 公开路由 ==========
|
|
api := r.Group("/api/v1")
|
|
|
|
// 健康检查
|
|
api.GET("/health", func(c *gin.Context) {
|
|
c.JSON(200, gin.H{
|
|
"status": "ok",
|
|
"service": "cyrene-gateway",
|
|
"ws_connections": hub.ClientCount(),
|
|
})
|
|
})
|
|
|
|
// 认证 (无需JWT)
|
|
auth := api.Group("/auth")
|
|
{
|
|
auth.POST("/register", authHandler.Register)
|
|
auth.POST("/login", authHandler.Login)
|
|
}
|
|
|
|
// ========== 需要认证的路由 ==========
|
|
protected := api.Group("")
|
|
protected.Use(middleware.JWTAuth(cfg))
|
|
protected.Use(rateLimiter.Handler())
|
|
{
|
|
// Token刷新
|
|
protected.POST("/auth/refresh", authHandler.RefreshToken)
|
|
|
|
// 会话管理
|
|
sessions := protected.Group("/sessions")
|
|
{
|
|
sessions.POST("", sessionHandler.Create) // POST /api/v1/sessions
|
|
sessions.GET("", sessionHandler.List) // GET /api/v1/sessions?user_id=xxx
|
|
sessions.DELETE("", sessionHandler.DeleteAll) // DELETE /api/v1/sessions?user_id=xxx
|
|
sessions.GET("/:id", sessionHandler.Get) // GET /api/v1/sessions/:id
|
|
sessions.DELETE("/:id", sessionHandler.Delete) // DELETE /api/v1/sessions/:id
|
|
sessions.GET("/:id/messages", sessionHandler.GetMessages) // GET /api/v1/sessions/:id/messages?limit=50
|
|
sessions.DELETE("/:id/messages", sessionHandler.ClearMessages) // DELETE /api/v1/sessions/:id/messages
|
|
}
|
|
|
|
// 记忆管理
|
|
memory := protected.Group("/memory")
|
|
{
|
|
memory.GET("/search", memoryHandler.Query)
|
|
memory.GET("", memoryHandler.List)
|
|
memory.POST("", memoryHandler.Add)
|
|
memory.DELETE("", memoryHandler.Delete)
|
|
}
|
|
|
|
// Admin 路由 (需要管理员权限)
|
|
admin := protected.Group("/admin")
|
|
admin.Use(adminAuth())
|
|
{
|
|
admin.GET("/sessions", sessionHandler.ListActiveSessions)
|
|
admin.GET("/sessions/active", sessionHandler.GetActiveSessions)
|
|
admin.GET("/sessions/:id", sessionHandler.GetSession)
|
|
}
|
|
}
|
|
|
|
// ========== WebSocket路由 ==========
|
|
// WebSocket升级在HTTP层,token通过query参数或Header传递
|
|
wsGroup := r.Group("/ws")
|
|
{
|
|
wsGroup.GET("/chat", chatHandler.HandleWebSocket)
|
|
}
|
|
|
|
// ========== Webhook 路由(第三方平台接入) ==========
|
|
webhook := r.Group("/api/v1/webhook")
|
|
webhook.Use(webhookHandler.WebhookAuth())
|
|
{
|
|
webhook.POST("/generic", webhookHandler.HandleGenericWebhook)
|
|
webhook.POST("/discord", webhookHandler.HandleDiscordWebhook)
|
|
}
|
|
|
|
// ========== 静态文件服务 (生产环境) ==========
|
|
if cfg.Env == "production" {
|
|
r.Static("/assets", "./public/assets")
|
|
r.StaticFile("/", "./public/index.html")
|
|
r.NoRoute(func(c *gin.Context) {
|
|
c.File("./public/index.html")
|
|
})
|
|
}
|
|
}
|
|
|
|
// adminAuth 管理员权限中间件 (检查 userID 是否以 "admin_" 开头)
|
|
func adminAuth() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
userID := middleware.GetUserID(c)
|
|
if userID == "" || !strings.HasPrefix(userID, "admin_") {
|
|
c.JSON(http.StatusForbidden, gin.H{"error": "需要管理员权限"})
|
|
c.Abort()
|
|
return
|
|
}
|
|
c.Next()
|
|
}
|
|
}
|