4b35736f73
P0 (5): crypto/rand session ID, TTS fallback可达性, goroutine defer recover, adminAuth前缀修正 P1 (5): 普通用户密码验证, context传递, priority clamp, 超时重试, 自主思考速率限制 P2 (4): Briefing AI降级, 前端消息类型渲染, Docker Compose补全, PWA 192图标 P3 (5): goroutine错误处理, .gitignore完善, reminder created_at, voice Dockerfile, Go版本更新
246 lines
9.8 KiB
Go
246 lines
9.8 KiB
Go
package router
|
|
|
|
import (
|
|
"database/sql"
|
|
"net/http"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"github.com/yourname/cyrene-ai/gateway/internal/config"
|
|
"github.com/yourname/cyrene-ai/gateway/internal/engine"
|
|
"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, reminderStore *store.ReminderStore, briefingStore *store.BriefingStore, automationStore *store.AutomationStore, fileStore *store.FileStore, ruleEngine *engine.RuleEngine, knowledgeStore *store.KnowledgeStore, imageHandler *handler.ImageHandler, db interface{}) {
|
|
// 限流器
|
|
rateLimiter := middleware.NewRateLimiter(10, 20) // 每秒10个请求,突发20
|
|
|
|
// 初始化处理器
|
|
var authDB *sql.DB
|
|
if db != nil {
|
|
authDB = db.(*sql.DB)
|
|
}
|
|
authHandler := handler.NewAuthHandler(cfg, authDB)
|
|
sessionHandler := handler.NewSessionHandler(hub, sessionStore)
|
|
memoryHandler := handler.NewMemoryHandler(cfg.MemoryServiceURL)
|
|
chatHandler := handler.NewChatHandler(cfg, hub)
|
|
webhookHandler := handler.NewWebhookHandler(cfg, hub)
|
|
notificationHandler := handler.NewNotificationHandler(cfg, hub)
|
|
reminderHandler := handler.NewReminderHandler(reminderStore, hub)
|
|
briefingHandler := handler.NewBriefingHandler(cfg, hub, briefingStore, reminderStore)
|
|
voiceHandler := handler.NewVoiceHandler(cfg.VoiceServiceURL)
|
|
fileHandler := handler.NewFileHandler(fileStore)
|
|
automationHandler := handler.NewAutomationHandler(automationStore, ruleEngine)
|
|
knowledgeHandler := handler.NewKnowledgeHandler(knowledgeStore, fileStore)
|
|
if imageHandler == nil {
|
|
imageHandler = handler.NewImageHandler(cfg, fileStore)
|
|
}
|
|
|
|
// ========== 公开路由 ==========
|
|
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
|
|
sessions.GET("/:id/export", sessionHandler.ExportSession) // GET /api/v1/sessions/:id/export?format=json|markdown|txt
|
|
}
|
|
|
|
// 消息搜索
|
|
protected.GET("/messages/search", sessionHandler.SearchMessages) // GET /api/v1/messages/search?q=xxx&user_id=xxx&limit=50&offset=0
|
|
|
|
// 记忆管理
|
|
memory := protected.Group("/memory")
|
|
{
|
|
memory.GET("/search", memoryHandler.Query)
|
|
memory.GET("", memoryHandler.List)
|
|
memory.POST("", memoryHandler.Add)
|
|
memory.DELETE("", memoryHandler.Delete)
|
|
}
|
|
|
|
// 通知推送 (需要认证)
|
|
notifications := protected.Group("/notifications")
|
|
{
|
|
notifications.POST("/push", notificationHandler.Push)
|
|
}
|
|
|
|
// 提醒管理 (需要认证)
|
|
reminders := protected.Group("/reminders")
|
|
{
|
|
reminders.GET("", reminderHandler.List) // GET /api/v1/reminders?user_id=xxx&status=pending&limit=50
|
|
reminders.POST("", reminderHandler.Create) // POST /api/v1/reminders
|
|
reminders.PUT("/:id", reminderHandler.Update) // PUT /api/v1/reminders/:id
|
|
reminders.DELETE("/:id", reminderHandler.Delete) // DELETE /api/v1/reminders/:id
|
|
}
|
|
|
|
// 每日简报 (需要认证)
|
|
briefings := protected.Group("/briefings")
|
|
{
|
|
briefings.GET("", briefingHandler.GetBriefing) // GET /api/v1/briefings?user_id=xxx&date=2024-01-01
|
|
briefings.GET("/latest", briefingHandler.GetLatestBriefings) // GET /api/v1/briefings/latest?user_id=xxx&limit=7
|
|
briefings.POST("/generate", briefingHandler.Generate) // POST /api/v1/briefings/generate
|
|
}
|
|
|
|
// 语音识别 + TTS (需要认证)
|
|
voice := protected.Group("/voice")
|
|
{
|
|
voice.POST("/transcribe", voiceHandler.Transcribe)
|
|
voice.POST("/tts", voiceHandler.TTSSynthesize)
|
|
voice.GET("/tts/voices", voiceHandler.TTSVoices)
|
|
voice.GET("/tts/status", voiceHandler.TTSStatus)
|
|
voice.GET("/status", voiceHandler.VoiceStatus)
|
|
}
|
|
|
|
// 文件管理 (需要认证)
|
|
files := protected.Group("/files")
|
|
{
|
|
files.POST("/upload", fileHandler.Upload)
|
|
files.GET("", fileHandler.List)
|
|
files.GET("/:id", fileHandler.Get)
|
|
files.GET("/:id/download", fileHandler.Download)
|
|
files.GET("/:id/thumbnail", fileHandler.Thumbnail)
|
|
files.DELETE("/:id", fileHandler.Delete)
|
|
}
|
|
|
|
// 自动化 (需要认证)
|
|
automation := protected.Group("/automation")
|
|
{
|
|
// 规则
|
|
rules := automation.Group("/rules")
|
|
{
|
|
rules.GET("", automationHandler.ListRules) // GET /api/v1/automation/rules
|
|
rules.POST("", automationHandler.CreateRule) // POST /api/v1/automation/rules
|
|
rules.GET("/:id", automationHandler.GetRule) // GET /api/v1/automation/rules/:id
|
|
rules.PUT("/:id", automationHandler.UpdateRule) // PUT /api/v1/automation/rules/:id
|
|
rules.DELETE("/:id", automationHandler.DeleteRule) // DELETE /api/v1/automation/rules/:id
|
|
rules.POST("/:id/trigger", automationHandler.TriggerRule) // POST /api/v1/automation/rules/:id/trigger
|
|
}
|
|
|
|
// 场景
|
|
scenes := automation.Group("/scenes")
|
|
{
|
|
scenes.GET("", automationHandler.ListScenes) // GET /api/v1/automation/scenes
|
|
scenes.POST("", automationHandler.CreateScene) // POST /api/v1/automation/scenes
|
|
scenes.GET("/:id", automationHandler.GetScene) // GET /api/v1/automation/scenes/:id
|
|
scenes.PUT("/:id", automationHandler.UpdateScene) // PUT /api/v1/automation/scenes/:id
|
|
scenes.DELETE("/:id", automationHandler.DeleteScene) // DELETE /api/v1/automation/scenes/:id
|
|
scenes.POST("/:id/execute", automationHandler.ExecuteScene) // POST /api/v1/automation/scenes/:id/execute
|
|
}
|
|
}
|
|
|
|
// 知识库管理 (需要认证)
|
|
knowledge := protected.Group("/knowledge")
|
|
{
|
|
// 知识库 CRUD
|
|
knowledge.POST("/bases", knowledgeHandler.CreateKB) // POST /api/v1/knowledge/bases
|
|
knowledge.GET("/bases", knowledgeHandler.ListKBs) // GET /api/v1/knowledge/bases
|
|
knowledge.GET("/bases/:id", knowledgeHandler.GetKB) // GET /api/v1/knowledge/bases/:id
|
|
knowledge.PUT("/bases/:id", knowledgeHandler.UpdateKB) // PUT /api/v1/knowledge/bases/:id
|
|
knowledge.DELETE("/bases/:id", knowledgeHandler.DeleteKB) // DELETE /api/v1/knowledge/bases/:id
|
|
|
|
// 文档管理
|
|
knowledge.POST("/bases/:id/documents", knowledgeHandler.AddDocument) // POST /api/v1/knowledge/bases/:id/documents
|
|
knowledge.GET("/bases/:id/documents", knowledgeHandler.ListDocuments) // GET /api/v1/knowledge/bases/:id/documents
|
|
knowledge.GET("/documents/:id", knowledgeHandler.GetDocument) // GET /api/v1/knowledge/documents/:id
|
|
knowledge.DELETE("/documents/:id", knowledgeHandler.DeleteDocument) // DELETE /api/v1/knowledge/documents/:id
|
|
|
|
// 搜索
|
|
knowledge.POST("/search", knowledgeHandler.Search) // POST /api/v1/knowledge/search
|
|
}
|
|
|
|
// 图片分析 (需要认证)
|
|
images := protected.Group("/images")
|
|
{
|
|
images.POST("/analyze", imageHandler.Analyze) // POST /api/v1/images/analyze
|
|
images.GET("/analyze/:file_id", imageHandler.AnalyzeByID) // GET /api/v1/images/analyze/:file_id
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
}
|
|
|
|
// ========== 内部服务路由 (使用 Internal Service Token 认证) ==========
|
|
internal := r.Group("/api/v1/internal")
|
|
internal.Use(notificationHandler.InternalNotifyAuth())
|
|
{
|
|
internal.POST("/notify", notificationHandler.InternalNotify)
|
|
}
|
|
|
|
// ========== 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 管理员权限中间件 (检查认证中间件设置的 is_admin 标记)
|
|
func adminAuth() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
isAdmin, _ := c.Get(middleware.IsAdminKey)
|
|
if isAdmin == nil || !isAdmin.(bool) {
|
|
c.JSON(http.StatusForbidden, gin.H{"error": "需要管理员权限"})
|
|
c.Abort()
|
|
return
|
|
}
|
|
c.Next()
|
|
}
|
|
}
|