Files
AskaEth 71f0a1abdb feat: Go模块路径迁移 + Docker生产部署适配 + ethend Docker兼容
- 所有Go模块路径从 github.com/yourname/cyrene-ai 迁移到 git.yeij.top/AskaEth/Cyrene
- 5个Go Dockerfile添加 GOPROXY=https://goproxy.cn,direct 解决国内构建问题
- ai-core go.mod 添加 pkg/plugins replace 指令
- Caddyfile 简化为 http:// 通配 + handle 保留 /api 前缀
- ethend Dockerfile 适配 (npm install + 仅 COPY package.json)
- ethend 新增 RUNNING_IN_DOCKER 环境变量,健康检查改用Docker服务名
- ethend 数据库状态检查支持Docker hostname (postgres/redis/qdrant/minio)
- process-manager 新增 CONTAINER_SVC_MAP + Docker模式自动检测
- 统一 docker-compose.dev.db.yml 卷名 (pg_data/redis_data/qdrant_data/minio_data)
- docker-compose.yml ethend服务挂载docker.sock + 端口变量化
- 清理 .env 统一后的残留文件与提示信息

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-30 13:43:22 +08:00

276 lines
11 KiB
Go

package router
import (
"database/sql"
"net/http"
"github.com/gin-gonic/gin"
"git.yeij.top/AskaEth/Cyrene/gateway/internal/config"
"git.yeij.top/AskaEth/Cyrene/gateway/internal/engine"
"git.yeij.top/AskaEth/Cyrene/gateway/internal/handler"
"git.yeij.top/AskaEth/Cyrene/gateway/internal/middleware"
"git.yeij.top/AskaEth/Cyrene/gateway/internal/store"
"git.yeij.top/AskaEth/Cyrene/gateway/internal/ws"
)
// Setup 注册所有路由
func Setup(r *gin.Engine, hub *ws.Hub, cfg *config.Config, sessionStore *store.SessionStore, reminderStore *store.ReminderStore, automationStore *store.AutomationStore, fileStore *store.FileStore, ruleEngine *engine.RuleEngine, knowledgeStore *store.KnowledgeStore, imageHandler *handler.ImageHandler, db interface{}, modelConfigStore *config.ModelsConfigStore, thinkingScheduleStore *config.ThinkingScheduleStore) {
// 限流器
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, sessionStore, fileStore)
webhookHandler := handler.NewWebhookHandler(cfg, hub)
notificationHandler := handler.NewNotificationHandler(cfg, hub)
reminderHandler := handler.NewReminderHandler(reminderStore, hub)
voiceHandler := handler.NewVoiceHandler(cfg.VoiceServiceURL)
fileHandler := handler.NewFileHandler(fileStore)
automationHandler := handler.NewAutomationHandler(automationStore, ruleEngine)
knowledgeHandler := handler.NewKnowledgeHandler(knowledgeStore, fileStore)
modelConfigHandler := handler.NewModelConfigHandler(modelConfigStore)
thinkingScheduleHandler := handler.NewThinkingScheduleHandler(thinkingScheduleStore)
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(),
})
})
// 认证路由专用限流器:每分钟每个IP每个端点最多5次请求(防暴力破解)
authRateLimiter := middleware.NewRateLimiter(0.083, 5) // ~5 per minute per IP+endpoint
// 认证 (无需JWT)
auth := api.Group("/auth")
{
auth.POST("/register", authRateLimiter.HandlerWithKey(middleware.AuthIPKey("register")), authHandler.Register)
auth.POST("/login", authRateLimiter.HandlerWithKey(middleware.AuthIPKey("login")), authHandler.Login)
}
// ========== 需要认证的路由 ==========
protected := api.Group("")
protected.Use(middleware.JWTAuth(cfg))
protected.Use(rateLimiter.Handler())
{
// Token刷新
protected.POST("/auth/refresh", authHandler.RefreshToken)
// 当前用户信息
protected.GET("/profile", authHandler.GetProfile)
// 会话管理
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
}
// 语音识别 + 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)
// 多端客户端管理
admin.GET("/clients", chatHandler.HandleListClients)
admin.PUT("/clients/:id/note", chatHandler.HandleUpdateClientNote)
// 模型配置管理
models := admin.Group("/models")
{
models.GET("/providers", modelConfigHandler.ListProviders)
models.GET("/providers/:name", modelConfigHandler.GetProvider)
models.POST("/providers/:name", modelConfigHandler.SetProvider)
models.DELETE("/providers/:name", modelConfigHandler.DeleteProvider)
models.GET("/models", modelConfigHandler.ListModels)
models.GET("/models/:id", modelConfigHandler.GetModel)
models.POST("/models/:id", modelConfigHandler.SetModel)
models.DELETE("/models/:id", modelConfigHandler.DeleteModel)
models.GET("/routing", modelConfigHandler.ListRouting)
models.GET("/routing/:purpose", modelConfigHandler.GetRouting)
models.POST("/routing/:purpose", modelConfigHandler.SetRouting)
models.DELETE("/routing/:purpose", modelConfigHandler.DeleteRouting)
models.POST("/health-check", modelConfigHandler.TestProvider)
models.GET("/fetch-models/:name", modelConfigHandler.ProxyListModels)
}
// 思考调度配置
thinkingSchedule := admin.Group("/thinking-schedule")
{
thinkingSchedule.GET("", thinkingScheduleHandler.GetSchedule)
thinkingSchedule.PUT("", thinkingScheduleHandler.SetSchedule)
}
}
}
// ========== 内部服务路由 (使用 Internal Service Token 认证) ==========
internal := r.Group("/api/v1/internal")
internal.Use(notificationHandler.InternalNotifyAuth())
{
internal.POST("/notify", notificationHandler.InternalNotify)
internal.POST("/proactive-message", chatHandler.HandleProactiveMessage)
}
// ========== 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()
}
}