2026-03-06 16:39:42 +08:00
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
|
|
|
|
"io"
|
|
|
|
|
"log"
|
|
|
|
|
"os"
|
|
|
|
|
"path/filepath"
|
|
|
|
|
"time"
|
|
|
|
|
"x-agents/server/internal/config"
|
|
|
|
|
"x-agents/server/internal/handler"
|
2026-03-11 10:25:50 +08:00
|
|
|
"x-agents/server/internal/middleware"
|
2026-03-06 16:39:42 +08:00
|
|
|
"x-agents/server/internal/model"
|
|
|
|
|
"x-agents/server/internal/repository"
|
|
|
|
|
"x-agents/server/internal/service"
|
|
|
|
|
|
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Logger 日志记录器
|
|
|
|
|
type Logger struct {
|
|
|
|
|
successLog *log.Logger
|
|
|
|
|
errorLog *log.Logger
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewLogger() *Logger {
|
|
|
|
|
// 创建日志目录
|
|
|
|
|
today := time.Now().Format("2006-01-02")
|
|
|
|
|
logDir := filepath.Join("logs", today)
|
|
|
|
|
os.MkdirAll(logDir, 0755)
|
|
|
|
|
|
|
|
|
|
// 成功日志
|
|
|
|
|
successFile, _ := os.OpenFile(filepath.Join(logDir, "success.log"), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
|
|
|
|
successLogger := log.New(successFile, "", log.Ldate|log.Ltime|log.Lshortfile)
|
|
|
|
|
|
|
|
|
|
// 错误日志
|
|
|
|
|
errorFile, _ := os.OpenFile(filepath.Join(logDir, "failure.log"), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
|
|
|
|
errorLogger := log.New(errorFile, "", log.Ldate|log.Ltime|log.Lshortfile)
|
|
|
|
|
|
|
|
|
|
return &Logger{
|
|
|
|
|
successLog: successLogger,
|
|
|
|
|
errorLog: errorLogger,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// LogRequest 记录请求
|
|
|
|
|
func (l *Logger) LogRequest(method, path, body string, status int, duration time.Duration) {
|
|
|
|
|
entry := "[%s] %s %s %d %v"
|
|
|
|
|
|
|
|
|
|
if status >= 400 {
|
|
|
|
|
l.errorLog.Printf(entry, method, path, body, status, duration)
|
|
|
|
|
} else {
|
|
|
|
|
l.successLog.Printf(entry, method, path, body, status, duration)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var logger *Logger
|
|
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
|
// 初始化日志
|
|
|
|
|
logger = NewLogger()
|
|
|
|
|
|
|
|
|
|
// 1. 加载配置
|
|
|
|
|
cfg := config.Load()
|
|
|
|
|
log.Printf("=== Server starting, port=%s ===", cfg.Port)
|
|
|
|
|
|
|
|
|
|
// 2. 初始化数据库
|
|
|
|
|
db, err := config.InitDB(cfg)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Fatalf("Failed to connect database: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 3. 自动迁移表
|
2026-03-11 10:25:50 +08:00
|
|
|
db.AutoMigrate(&model.DatabaseInfo{}, &model.SubTableInfo{}, &model.ModelInfo{}, &model.KnowledgeBase{}, &model.KnowledgeDocument{}, &model.User{}, &model.Role{})
|
2026-03-06 16:39:42 +08:00
|
|
|
|
|
|
|
|
// 4. 初始化 Repository
|
|
|
|
|
dbRepo := repository.NewDatabaseRepository(db)
|
|
|
|
|
subTableRepo := repository.NewSubTableRepository(db)
|
2026-03-07 13:53:35 +08:00
|
|
|
modelRepo := repository.NewModelRepository(db)
|
2026-03-08 20:34:15 +08:00
|
|
|
knowledgeRepo := repository.NewKnowledgeRepository(db)
|
2026-03-11 10:25:50 +08:00
|
|
|
userRepo := repository.NewUserRepository(db)
|
2026-03-06 16:39:42 +08:00
|
|
|
|
|
|
|
|
// 5. 初始化 Service
|
|
|
|
|
dbService := service.NewDatabaseService(dbRepo, subTableRepo)
|
|
|
|
|
subTableService := service.NewSubTableService(subTableRepo, dbRepo)
|
2026-03-07 09:11:08 +08:00
|
|
|
neo4jService := service.NewNeo4jService(dbRepo)
|
2026-03-07 13:53:35 +08:00
|
|
|
modelService := service.NewModelService(modelRepo)
|
2026-03-08 16:54:41 +08:00
|
|
|
uploadService, err := service.NewUploadService(cfg)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Printf("Warning: Failed to initialize upload service: %v (files will not be available)", err)
|
|
|
|
|
}
|
2026-03-09 15:42:42 +08:00
|
|
|
knowledgeService := service.NewKnowledgeService(knowledgeRepo, modelRepo, uploadService, cfg.PythonServiceURL, cfg.AICoreServiceAddr, cfg.MarkdownLocalPath)
|
2026-03-11 10:25:50 +08:00
|
|
|
authService := service.NewAuthService(cfg.JWTSecret, userRepo)
|
2026-03-06 16:39:42 +08:00
|
|
|
|
|
|
|
|
// 6. 初始化 Handler
|
|
|
|
|
dbHandler := handler.NewDatabaseHandler(dbService)
|
|
|
|
|
subTableHandler := handler.NewSubTableHandler(subTableService)
|
2026-03-07 09:11:08 +08:00
|
|
|
neo4jHandler := handler.NewNeo4jHandler(neo4jService)
|
2026-03-07 13:53:35 +08:00
|
|
|
modelHandler := handler.NewModelHandler(modelService)
|
2026-03-06 16:39:42 +08:00
|
|
|
systemHandler := handler.NewSystemHandler()
|
2026-03-08 20:34:15 +08:00
|
|
|
knowledgeHandler := handler.NewKnowledgeHandler(knowledgeService)
|
2026-03-11 10:25:50 +08:00
|
|
|
authHandler := handler.NewAuthHandler(authService)
|
2026-03-08 16:54:41 +08:00
|
|
|
var uploadHandler *handler.UploadHandler
|
|
|
|
|
if uploadService != nil {
|
2026-03-08 23:02:09 +08:00
|
|
|
uploadHandler = handler.NewUploadHandler(uploadService, knowledgeRepo)
|
2026-03-08 16:54:41 +08:00
|
|
|
}
|
2026-03-06 16:39:42 +08:00
|
|
|
|
|
|
|
|
// 7. 设置路由
|
|
|
|
|
r := gin.New()
|
|
|
|
|
|
|
|
|
|
// 添加日志和恢复中间件
|
|
|
|
|
r.Use(gin.Logger())
|
|
|
|
|
r.Use(gin.Recovery())
|
|
|
|
|
|
|
|
|
|
// 请求日志中间件
|
|
|
|
|
r.Use(func(c *gin.Context) {
|
|
|
|
|
start := time.Now()
|
|
|
|
|
path := c.Request.URL.Path
|
|
|
|
|
|
|
|
|
|
// 记录请求体
|
|
|
|
|
var requestBody []byte
|
|
|
|
|
if c.Request.Method == "POST" || c.Request.Method == "PUT" {
|
|
|
|
|
requestBody, _ = io.ReadAll(c.Request.Body)
|
|
|
|
|
c.Request.Body = io.NopCloser(bytes.NewBuffer(requestBody))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c.Next()
|
|
|
|
|
|
|
|
|
|
// 记录响应日志
|
|
|
|
|
latency := time.Since(start)
|
|
|
|
|
status := c.Writer.Status()
|
|
|
|
|
|
|
|
|
|
// 使用日志系统记录
|
|
|
|
|
logger.LogRequest(c.Request.Method, path, string(requestBody), status, latency)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// CORS 中间件
|
|
|
|
|
r.Use(func(c *gin.Context) {
|
|
|
|
|
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
|
|
|
|
c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
|
|
|
|
|
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
|
|
|
|
|
|
|
|
|
|
if c.Request.Method == "OPTIONS" {
|
|
|
|
|
c.AbortWithStatus(204)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c.Next()
|
|
|
|
|
})
|
|
|
|
|
|
2026-03-11 10:25:50 +08:00
|
|
|
// 认证模块(无需登录)
|
|
|
|
|
authGroup := r.Group("/auth")
|
|
|
|
|
{
|
|
|
|
|
authGroup.POST("/register", authHandler.Register)
|
|
|
|
|
authGroup.POST("/login", authHandler.Login)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 需要登录的认证模块
|
|
|
|
|
authProtectedGroup := r.Group("/auth")
|
|
|
|
|
authProtectedGroup.Use(middleware.Auth(cfg.JWTSecret))
|
|
|
|
|
{
|
|
|
|
|
authProtectedGroup.GET("/me", authHandler.GetCurrentUser)
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-06 16:39:42 +08:00
|
|
|
// 数据库管理模块
|
|
|
|
|
databaseGroup := r.Group("/database")
|
|
|
|
|
{
|
|
|
|
|
databaseGroup.GET("/list", dbHandler.List)
|
|
|
|
|
databaseGroup.GET("/:id", dbHandler.GetByID)
|
|
|
|
|
databaseGroup.POST("/check", dbHandler.Check)
|
|
|
|
|
databaseGroup.POST("/add", dbHandler.Create)
|
|
|
|
|
databaseGroup.PUT("/:id", dbHandler.Update)
|
|
|
|
|
databaseGroup.DELETE("/:id", dbHandler.Delete)
|
2026-03-07 09:11:08 +08:00
|
|
|
databaseGroup.POST("/graph/save", dbHandler.SaveGraph)
|
2026-03-06 16:39:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 子表映射管理模块
|
|
|
|
|
subTableGroup := r.Group("/sub-table")
|
|
|
|
|
{
|
|
|
|
|
subTableGroup.POST("/add", subTableHandler.Create)
|
|
|
|
|
subTableGroup.GET("/:id", subTableHandler.GetByID)
|
|
|
|
|
subTableGroup.GET("/database/:database_id", subTableHandler.ListByDatabase)
|
|
|
|
|
subTableGroup.GET("/mapping/:database_id", subTableHandler.GetMappingFromFile)
|
|
|
|
|
subTableGroup.GET("/ddl/:database_id", subTableHandler.GetTablesDDL)
|
|
|
|
|
subTableGroup.PUT("/:id", subTableHandler.Update)
|
|
|
|
|
subTableGroup.DELETE("/:id", subTableHandler.Delete)
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-07 09:11:08 +08:00
|
|
|
// Neo4j 连接管理模块
|
|
|
|
|
neo4jGroup := r.Group("/neo4j")
|
|
|
|
|
{
|
|
|
|
|
neo4jGroup.POST("/check", neo4jHandler.Check)
|
|
|
|
|
neo4jGroup.POST("/graphs", neo4jHandler.GetGraphs)
|
|
|
|
|
neo4jGroup.POST("/nodes", neo4jHandler.GetNodes)
|
|
|
|
|
neo4jGroup.POST("/relationships", neo4jHandler.GetRelationships)
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-07 13:53:35 +08:00
|
|
|
// Model 管理模块
|
|
|
|
|
modelGroup := r.Group("/model")
|
|
|
|
|
{
|
|
|
|
|
modelGroup.GET("/list", modelHandler.List)
|
2026-03-07 16:21:29 +08:00
|
|
|
modelGroup.POST("/test", modelHandler.Test)
|
2026-03-07 13:53:35 +08:00
|
|
|
modelGroup.POST("/add", modelHandler.Create)
|
2026-03-07 16:21:29 +08:00
|
|
|
modelGroup.GET("/:id", modelHandler.GetByID)
|
2026-03-07 13:53:35 +08:00
|
|
|
modelGroup.PUT("/:id", modelHandler.Update)
|
|
|
|
|
modelGroup.DELETE("/:id", modelHandler.Delete)
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-08 20:34:15 +08:00
|
|
|
// 知识库管理模块
|
|
|
|
|
knowledgeGroup := r.Group("/api/knowledge")
|
|
|
|
|
{
|
|
|
|
|
knowledgeGroup.POST("/create", knowledgeHandler.Create)
|
|
|
|
|
knowledgeGroup.GET("/list", knowledgeHandler.List)
|
|
|
|
|
knowledgeGroup.GET("/:id", knowledgeHandler.GetByID)
|
|
|
|
|
knowledgeGroup.PUT("/:id", knowledgeHandler.Update)
|
|
|
|
|
knowledgeGroup.DELETE("/:id", knowledgeHandler.Delete)
|
|
|
|
|
// 文档管理
|
|
|
|
|
knowledgeGroup.GET("/:id/documents", knowledgeHandler.ListDocuments)
|
|
|
|
|
knowledgeGroup.POST("/:id/documents", knowledgeHandler.UploadDocument)
|
|
|
|
|
knowledgeGroup.DELETE("/:id/documents/:doc_id", knowledgeHandler.DeleteDocument)
|
|
|
|
|
knowledgeGroup.POST("/:id/documents/:doc_id/reparse", knowledgeHandler.ReparseDocument)
|
|
|
|
|
knowledgeGroup.GET("/:id/documents/:doc_id/preview", knowledgeHandler.GetDocumentPreview)
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-06 16:39:42 +08:00
|
|
|
// 系统信息模块
|
|
|
|
|
r.GET("/system/info", systemHandler.GetSystemInfo)
|
|
|
|
|
|
2026-03-08 16:54:41 +08:00
|
|
|
// 文件上传模块
|
|
|
|
|
if uploadHandler != nil {
|
|
|
|
|
// 本地文件静态服务
|
|
|
|
|
if cfg.UploadMode == "local" {
|
|
|
|
|
r.Static("/files", cfg.UploadLocalPath)
|
|
|
|
|
}
|
|
|
|
|
// 上传路由
|
2026-03-08 20:34:15 +08:00
|
|
|
r.POST("/api/file_upload", uploadHandler.Upload)
|
|
|
|
|
r.DELETE("/api/file_upload/:filename", uploadHandler.Delete)
|
2026-03-08 23:02:09 +08:00
|
|
|
// 文件代理路由(解决 MinIO 内网和 HTTPS 问题)
|
|
|
|
|
r.GET("/api/file_proxy", uploadHandler.ProxyFile)
|
2026-03-08 16:54:41 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-06 16:39:42 +08:00
|
|
|
// 8. 启动服务
|
|
|
|
|
log.Printf("Server starting on :%s", cfg.Port)
|
|
|
|
|
if err := r.Run(":" + cfg.Port); err != nil {
|
|
|
|
|
log.Fatalf("Failed to start server: %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|