refactor: 将客服后端重构为宿主可嵌入模块
- 注入数据库、运行时配置、统一响应、文件存储和平台 AI 能力,补充业务读写工具与客户快捷操作契约。 - 移除模块内重复的组织、客户、工单、标签、技能、旧工作流、MCP 和迁移实现,将身份权限与业务主体交由宿主管理。 - 使用 libSQL 重构向量存储,并完善图片消息、访客身份、排队调度、企业微信和支持聊天页面。 - 统一 HTTP、DTO 与 WebSocket 的 snake_case 协议,补齐模块初始化、业务动作和公共载荷等回归测试。
This commit is contained in:
@@ -10,13 +10,13 @@ import (
|
||||
func TestRenderBanner(t *testing.T) {
|
||||
got := renderBanner(config.Config{
|
||||
Server: config.ServerConfig{Port: 8083},
|
||||
DB: config.DBConfig{Type: "sqlite"},
|
||||
DB: config.DBConfig{Type: "postgres"},
|
||||
})
|
||||
|
||||
expected := []string{
|
||||
":: AGENT DESK ::",
|
||||
"Port : 8083",
|
||||
"DB : sqlite",
|
||||
"DB : postgres",
|
||||
"Address : http://127.0.0.1:8083",
|
||||
}
|
||||
for _, item := range expected {
|
||||
|
||||
+29
-45
@@ -4,18 +4,13 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/config"
|
||||
|
||||
"github.com/mlogclub/simple/sqls"
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/driver/postgres"
|
||||
|
||||
// "gorm.io/driver/sqlite" // Sqlite driver based on CGO
|
||||
"github.com/glebarez/sqlite" // Pure go SQLite driver, checkout https://github.com/glebarez/sqlite for details
|
||||
"gorm.io/gorm"
|
||||
|
||||
// "gorm.io/gorm/logger"
|
||||
@@ -69,50 +64,39 @@ func InitDB(cfg config.DBConfig) (*gorm.DB, error) {
|
||||
return db, nil
|
||||
}
|
||||
|
||||
// UseDatabase scopes the host application's connection to AI Agent tables.
|
||||
// The connection pool is shared, while the naming strategy is copied so the
|
||||
// host database naming rules remain untouched.
|
||||
func UseDatabase(database *gorm.DB, tablePrefix string) error {
|
||||
moduleDB, err := ScopedDatabase(database, tablePrefix)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sqls.SetDB(moduleDB)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ScopedDatabase shares the host connection pool while applying the AI Agent
|
||||
// table prefix to an isolated GORM session.
|
||||
func ScopedDatabase(database *gorm.DB, tablePrefix string) (*gorm.DB, error) {
|
||||
if database == nil {
|
||||
return nil, fmt.Errorf("database is required")
|
||||
}
|
||||
moduleDB := database.Session(&gorm.Session{NewDB: true})
|
||||
moduleConfig := *moduleDB.Config
|
||||
moduleConfig.NamingStrategy = schema.NamingStrategy{
|
||||
TablePrefix: tablePrefix,
|
||||
SingularTable: true,
|
||||
}
|
||||
moduleDB.Config = &moduleConfig
|
||||
return moduleDB, nil
|
||||
}
|
||||
|
||||
func newDialector(cfg config.DBConfig) (gorm.Dialector, error) {
|
||||
switch strings.ToLower(strings.TrimSpace(cfg.Type)) {
|
||||
case "sqlite":
|
||||
if err := ensureSQLiteDir(cfg.DSN); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sqlite.Open(cfg.DSN), nil
|
||||
case "mysql":
|
||||
return mysql.Open(cfg.DSN), nil
|
||||
case "postgres", "postgresql":
|
||||
return postgres.Open(cfg.DSN), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported db type: %s", cfg.Type)
|
||||
return nil, fmt.Errorf("unsupported db type %q: only postgres is supported", cfg.Type)
|
||||
}
|
||||
}
|
||||
|
||||
func ensureSQLiteDir(dsn string) error {
|
||||
dbPath := sqliteFilePath(dsn)
|
||||
if dbPath == "" {
|
||||
return nil
|
||||
}
|
||||
dir := filepath.Dir(dbPath)
|
||||
if dir == "." || dir == "" {
|
||||
return nil
|
||||
}
|
||||
return os.MkdirAll(dir, 0o755)
|
||||
}
|
||||
|
||||
func sqliteFilePath(dsn string) string {
|
||||
if dsn == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
path := dsn
|
||||
if after, ok := strings.CutPrefix(path, "file:"); ok {
|
||||
path = after
|
||||
}
|
||||
if idx := strings.Index(path, "?"); idx >= 0 {
|
||||
path = path[:idx]
|
||||
}
|
||||
|
||||
normalized := strings.TrimSpace(path)
|
||||
if normalized == "" || normalized == ":memory:" || strings.Contains(normalized, "mode=memory") {
|
||||
return ""
|
||||
}
|
||||
return normalized
|
||||
}
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
package bootstrap
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/models"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/config"
|
||||
|
||||
"github.com/glebarez/sqlite"
|
||||
"github.com/mlogclub/simple/sqls"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/schema"
|
||||
)
|
||||
|
||||
func TestNewDialector(t *testing.T) {
|
||||
@@ -15,18 +19,15 @@ func TestNewDialector(t *testing.T) {
|
||||
dbType string
|
||||
want string
|
||||
}{
|
||||
{dbType: "sqlite", want: "sqlite"},
|
||||
{dbType: "mysql", want: "mysql"},
|
||||
{dbType: "postgres", want: "postgres"},
|
||||
{dbType: "postgresql", want: "postgres"},
|
||||
{dbType: " PostgreSQL ", want: "postgres"},
|
||||
}
|
||||
|
||||
for _, tt := range cases {
|
||||
tt := tt
|
||||
t.Run(tt.dbType, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
dialector, err := newDialector(config.DBConfig{Type: tt.dbType, DSN: ":memory:"})
|
||||
dialector, err := newDialector(config.DBConfig{Type: tt.dbType, DSN: "postgres-dsn"})
|
||||
if err != nil {
|
||||
t.Fatalf("newDialector() error = %v", err)
|
||||
}
|
||||
@@ -37,69 +38,42 @@ func TestNewDialector(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewDialectorRejectsUnsupportedType(t *testing.T) {
|
||||
func TestNewDialectorRejectsRemovedAndUnsupportedTypes(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if _, err := newDialector(config.DBConfig{Type: "oracle"}); err == nil {
|
||||
t.Fatal("newDialector() error = nil, want unsupported type error")
|
||||
for _, dbType := range []string{"sqlite", "mysql", "oracle", ""} {
|
||||
if _, err := newDialector(config.DBConfig{Type: dbType}); err == nil {
|
||||
t.Fatalf("newDialector(%q) error = nil, want unsupported type error", dbType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSQLiteFilePath(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
dsn string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "plain relative path",
|
||||
dsn: "./data/app.db",
|
||||
want: "./data/app.db",
|
||||
},
|
||||
{
|
||||
name: "file uri with query",
|
||||
dsn: "file:./data/app.db?_busy_timeout=5000",
|
||||
want: "./data/app.db",
|
||||
},
|
||||
{
|
||||
name: "memory dsn",
|
||||
dsn: "file::memory:?cache=shared",
|
||||
want: "",
|
||||
},
|
||||
{
|
||||
name: "memory alias",
|
||||
dsn: ":memory:",
|
||||
want: "",
|
||||
},
|
||||
func TestUseDatabaseSharesConnectionWithoutChangingHostNaming(t *testing.T) {
|
||||
// SQLite remains only as a lightweight in-memory unit-test fixture. Runtime
|
||||
// database initialization accepts PostgreSQL exclusively.
|
||||
host, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
|
||||
NamingStrategy: schema.NamingStrategy{TablePrefix: "iot_"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("gorm.Open() error = %v", err)
|
||||
}
|
||||
hostTable := host.NamingStrategy.TableName("Conversation")
|
||||
|
||||
for _, tt := range cases {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
if got := sqliteFilePath(tt.dsn); got != tt.want {
|
||||
t.Fatalf("sqliteFilePath(%q) = %q, want %q", tt.dsn, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnsureSQLiteDir(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
baseDir := t.TempDir()
|
||||
dbPath := filepath.Join(baseDir, "nested", "app.db")
|
||||
dsn := "file:" + dbPath + "?_busy_timeout=5000"
|
||||
|
||||
if err := ensureSQLiteDir(dsn); err != nil {
|
||||
t.Fatalf("ensureSQLiteDir() error = %v", err)
|
||||
}
|
||||
|
||||
if info, err := os.Stat(filepath.Dir(dbPath)); err != nil {
|
||||
t.Fatalf("os.Stat() error = %v", err)
|
||||
} else if !info.IsDir() {
|
||||
t.Fatalf("expected %q to be a directory", filepath.Dir(dbPath))
|
||||
if err := UseDatabase(host, "iot_ai_"); err != nil {
|
||||
t.Fatalf("UseDatabase() error = %v", err)
|
||||
}
|
||||
moduleDB := sqls.DB()
|
||||
statement := &gorm.Statement{DB: moduleDB}
|
||||
if err := statement.Parse(&models.Conversation{}); err != nil {
|
||||
t.Fatalf("Statement.Parse() error = %v", err)
|
||||
}
|
||||
if statement.Schema.Table != "iot_ai_conversation" {
|
||||
t.Fatalf("module table=%q want iot_ai_conversation", statement.Schema.Table)
|
||||
}
|
||||
if got := host.NamingStrategy.TableName("Conversation"); got != hostTable {
|
||||
t.Fatalf("host table changed from %q to %q", hostTable, got)
|
||||
}
|
||||
if moduleDB.ConnPool != host.ConnPool {
|
||||
t.Fatal("module database does not share the host connection pool")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,50 @@
|
||||
package bootstrap
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/ai/rag/vectordb"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/config"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/i18nx"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/logx"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/services/cronx"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/wxwork"
|
||||
"log/slog"
|
||||
|
||||
_ "code.tczkiot.com/wlw/ai-agent/internal/services/event_handlers"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/wxwork"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type LoadSettingsFunc func(ctx context.Context, prefix string) (map[string]string, error)
|
||||
|
||||
// InitModule initializes AI Agent with infrastructure owned by the host
|
||||
// application. It never opens a database connection or reads a private
|
||||
// configuration table, and it never creates or migrates database tables.
|
||||
func InitModule(database *gorm.DB, tablePrefix string, loadSettings LoadSettingsFunc) error {
|
||||
settings, err := loadSettings(context.Background(), config.SettingsPrefix)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cfg, err := config.FromSettings(settings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
config.SetCurrent(cfg)
|
||||
i18nx.SetDefaultLocale(cfg.LanguageOrDefault())
|
||||
|
||||
if err := UseDatabase(database, tablePrefix); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := vectordb.Init(&cfg.VectorDB); err != nil {
|
||||
slog.Error("init vector db failed", "error", err)
|
||||
return err
|
||||
}
|
||||
cronx.Init()
|
||||
wxwork.SetSettingsLoader(wxwork.SettingsLoader(loadSettings))
|
||||
wxwork.Init()
|
||||
return nil
|
||||
}
|
||||
|
||||
func Init(configPath string) error {
|
||||
cfg, err := config.Load(configPath)
|
||||
if err != nil {
|
||||
@@ -31,10 +64,6 @@ func Init(configPath string) error {
|
||||
slog.Error("init db failed", "error", err)
|
||||
return err
|
||||
}
|
||||
if err := InitMigrations(); err != nil {
|
||||
slog.Error("init migrations failed", "error", err)
|
||||
return err
|
||||
}
|
||||
if err := vectordb.Init(&cfg.VectorDB); err != nil {
|
||||
slog.Error("init vector db failed", "error", err)
|
||||
return err
|
||||
@@ -43,6 +72,7 @@ func Init(configPath string) error {
|
||||
// 启动任务调度器
|
||||
cronx.Init()
|
||||
|
||||
wxwork.SetSettingsLoader(nil)
|
||||
wxwork.Init()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
package bootstrap
|
||||
|
||||
import (
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/migration"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/models"
|
||||
|
||||
"github.com/mlogclub/simple/sqls"
|
||||
)
|
||||
|
||||
func InitMigrations() error {
|
||||
if err := sqls.DB().AutoMigrate(models.Models...); err != nil {
|
||||
return err
|
||||
}
|
||||
return migration.Migrate()
|
||||
}
|
||||
@@ -13,6 +13,8 @@ func registerApiChannelRoutes(group *gin.RouterGroup) {
|
||||
}
|
||||
|
||||
func registerApiConversationRoutes(group *gin.RouterGroup) {
|
||||
group.GET("/quick_actions", api.ConversationGetQuick_actions)
|
||||
group.POST("/quick_action", api.ConversationPostQuick_action)
|
||||
group.GET("/:id", api.ConversationGetBy)
|
||||
group.POST("/close", api.ConversationPostClose)
|
||||
group.POST("/create_or_match", api.ConversationPostCreate_or_match)
|
||||
@@ -30,79 +32,22 @@ func registerDashboardDashboardRoutes(group *gin.RouterGroup) {
|
||||
group.GET("/overview", dashboard.DashboardGetOverview)
|
||||
}
|
||||
|
||||
func registerDashboardCompanyRoutes(group *gin.RouterGroup) {
|
||||
group.GET("/:id", dashboard.CompanyGetBy)
|
||||
group.POST("/create", dashboard.CompanyPostCreate)
|
||||
group.POST("/delete", dashboard.CompanyPostDelete)
|
||||
group.Any("/list", dashboard.CompanyAnyList)
|
||||
group.POST("/update", dashboard.CompanyPostUpdate)
|
||||
group.POST("/update_status", dashboard.CompanyPostUpdate_status)
|
||||
}
|
||||
|
||||
func registerDashboardCustomerRoutes(group *gin.RouterGroup) {
|
||||
group.GET("/:id", dashboard.CustomerGetBy)
|
||||
group.POST("/create", dashboard.CustomerPostCreate)
|
||||
group.POST("/delete", dashboard.CustomerPostDelete)
|
||||
group.POST("/list", dashboard.CustomerPostList)
|
||||
group.POST("/save_profile", dashboard.CustomerPostSave_profile)
|
||||
group.POST("/update", dashboard.CustomerPostUpdate)
|
||||
group.POST("/update_status", dashboard.CustomerPostUpdate_status)
|
||||
}
|
||||
|
||||
func registerDashboardCustomerContactRoutes(group *gin.RouterGroup) {
|
||||
group.POST("/create", dashboard.CustomerContactPostCreate)
|
||||
group.POST("/delete", dashboard.CustomerContactPostDelete)
|
||||
group.Any("/list", dashboard.CustomerContactAnyList)
|
||||
group.POST("/update", dashboard.CustomerContactPostUpdate)
|
||||
}
|
||||
|
||||
func registerDashboardTagRoutes(group *gin.RouterGroup) {
|
||||
group.GET("/:id", dashboard.TagGetBy)
|
||||
group.POST("/create", dashboard.TagPostCreate)
|
||||
group.POST("/delete", dashboard.TagPostDelete)
|
||||
group.Any("/list", dashboard.TagAnyList)
|
||||
group.GET("/list_all", dashboard.TagGetList_all)
|
||||
group.POST("/update", dashboard.TagPostUpdate)
|
||||
group.POST("/update_sort", dashboard.TagPostUpdate_sort)
|
||||
group.POST("/update_status", dashboard.TagPostUpdate_status)
|
||||
}
|
||||
|
||||
func registerDashboardConversationRoutes(group *gin.RouterGroup) {
|
||||
group.GET("/:id", dashboard.ConversationGetBy)
|
||||
group.POST("/add_tag", dashboard.ConversationPostAdd_tag)
|
||||
group.POST("/assign", dashboard.ConversationPostAssign)
|
||||
group.POST("/close", dashboard.ConversationPostClose)
|
||||
group.Any("/conversations", dashboard.ConversationAnyConversations)
|
||||
group.POST("/dispatch", dashboard.ConversationPostDispatch)
|
||||
group.POST("/link_customer", dashboard.ConversationPostLink_customer)
|
||||
group.Any("/list", dashboard.ConversationAnyList)
|
||||
group.Any("/message_list", dashboard.ConversationAnyMessage_list)
|
||||
group.POST("/read", dashboard.ConversationPostRead)
|
||||
group.POST("/recall_message", dashboard.ConversationPostRecall_message)
|
||||
group.POST("/remove_tag", dashboard.ConversationPostRemove_tag)
|
||||
group.POST("/send_message", dashboard.ConversationPostSend_message)
|
||||
group.POST("/transfer", dashboard.ConversationPostTransfer)
|
||||
group.POST("/upload_attachment", dashboard.ConversationPostUpload_attachment)
|
||||
group.POST("/upload_image", dashboard.ConversationPostUpload_image)
|
||||
}
|
||||
|
||||
func registerDashboardTicketRoutes(group *gin.RouterGroup) {
|
||||
group.GET("/:id", dashboard.TicketGetBy)
|
||||
group.POST("/assign", dashboard.TicketPostAssign)
|
||||
group.POST("/change_status", dashboard.TicketPostChange_status)
|
||||
group.POST("/create", dashboard.TicketPostCreate)
|
||||
group.POST("/create_from_conversation", dashboard.TicketPostCreate_from_conversation)
|
||||
group.POST("/delete_view", dashboard.TicketPostDelete_view)
|
||||
group.POST("/link_customer", dashboard.TicketPostLink_customer)
|
||||
group.Any("/list", dashboard.TicketAnyList)
|
||||
group.POST("/progress/create", dashboard.TicketPostProgressCreate)
|
||||
group.Any("/progress/list", dashboard.TicketAnyProgressList)
|
||||
group.POST("/save_view", dashboard.TicketPostSave_view)
|
||||
group.Any("/summary", dashboard.TicketAnySummary)
|
||||
group.POST("/update", dashboard.TicketPostUpdate)
|
||||
group.Any("/view_list", dashboard.TicketAnyView_list)
|
||||
}
|
||||
|
||||
func registerDashboardNotificationRoutes(group *gin.RouterGroup) {
|
||||
group.Any("/list", dashboard.NotificationAnyList)
|
||||
group.POST("/mark_all_read", dashboard.NotificationPostMark_all_read)
|
||||
@@ -178,25 +123,6 @@ func registerDashboardAIAgentRoutes(group *gin.RouterGroup) {
|
||||
group.POST("/update_status", dashboard.AIAgentPostUpdate_status)
|
||||
}
|
||||
|
||||
func registerDashboardAIWorkflowRoutes(group *gin.RouterGroup) {
|
||||
group.POST("/create", dashboard.AIWorkflowPostCreate)
|
||||
group.POST("/update", dashboard.AIWorkflowPostUpdate)
|
||||
group.POST("/delete", dashboard.AIWorkflowPostDelete)
|
||||
group.POST("/restore-version", dashboard.AIWorkflowPostRestoreVersion)
|
||||
group.Any("/list", dashboard.AIWorkflowAnyList)
|
||||
group.GET("/node-spec/list", dashboard.AIWorkflowGetNodeSpecList)
|
||||
group.GET("/default-definition", dashboard.AIWorkflowGetDefaultDefinition)
|
||||
group.GET("/template/list", dashboard.AIWorkflowGetTemplateList)
|
||||
group.POST("/validate", dashboard.AIWorkflowPostValidate)
|
||||
group.POST("/publish", dashboard.AIWorkflowPostPublish)
|
||||
group.Any("/run/list", dashboard.AIWorkflowAnyRunList)
|
||||
group.GET("/run/:id", dashboard.AIWorkflowGetRunBy)
|
||||
group.Any("/version/list", dashboard.AIWorkflowAnyVersionList)
|
||||
group.GET("/version/:id", dashboard.AIWorkflowGetVersionBy)
|
||||
group.GET("/:id/usage", dashboard.AIWorkflowGetUsage)
|
||||
group.GET("/:id", dashboard.AIWorkflowGetBy)
|
||||
}
|
||||
|
||||
func registerDashboardAgentRunRoutes(group *gin.RouterGroup) {
|
||||
group.Any("/metrics", dashboard.AgentRunAnyMetrics)
|
||||
group.POST("/evaluate", dashboard.AgentRunPostEvaluate)
|
||||
@@ -216,6 +142,10 @@ func registerDashboardAIConfigRoutes(group *gin.RouterGroup) {
|
||||
group.POST("/update_status", dashboard.AIConfigPostUpdate_status)
|
||||
}
|
||||
|
||||
func registerDashboardPlatformAIRoutes(group *gin.RouterGroup) {
|
||||
group.GET("/status", dashboard.PlatformAIGetStatus)
|
||||
}
|
||||
|
||||
func registerDashboardAssetRoutes(group *gin.RouterGroup) {
|
||||
group.GET("/:id", dashboard.AssetGetBy)
|
||||
group.POST("/create", dashboard.AssetPostCreate)
|
||||
@@ -276,27 +206,6 @@ func registerDashboardKnowledgeRetrieveLogRoutes(group *gin.RouterGroup) {
|
||||
group.Any("/list", dashboard.KnowledgeRetrieveLogAnyList)
|
||||
}
|
||||
|
||||
func registerDashboardSkillDefinitionRoutes(group *gin.RouterGroup) {
|
||||
group.GET("/:id", dashboard.SkillDefinitionGetBy)
|
||||
group.POST("/create", dashboard.SkillDefinitionPostCreate)
|
||||
group.POST("/debug_resume", dashboard.SkillDefinitionPostDebug_resume)
|
||||
group.POST("/debug_run", dashboard.SkillDefinitionPostDebug_run)
|
||||
group.POST("/delete", dashboard.SkillDefinitionPostDelete)
|
||||
group.Any("/list", dashboard.SkillDefinitionAnyList)
|
||||
group.GET("/list_all", dashboard.SkillDefinitionGetList_all)
|
||||
group.POST("/restore", dashboard.SkillDefinitionPostRestore)
|
||||
group.POST("/update", dashboard.SkillDefinitionPostUpdate)
|
||||
group.POST("/update_status", dashboard.SkillDefinitionPostUpdate_status)
|
||||
}
|
||||
|
||||
func registerDashboardMCPRoutes(group *gin.RouterGroup) {
|
||||
group.POST("/call_tool", dashboard.MCPPostCall_tool)
|
||||
group.Any("/catalog", dashboard.MCPAnyCatalog)
|
||||
group.Any("/list_servers", dashboard.MCPAnyList_servers)
|
||||
group.POST("/list_tools", dashboard.MCPPostList_tools)
|
||||
group.POST("/test_connection", dashboard.MCPPostTest_connection)
|
||||
}
|
||||
|
||||
func registerThirdWechatRoutes(group *gin.RouterGroup) {
|
||||
group.GET("/callback", third.WechatGetCallback)
|
||||
group.POST("/callback", third.WechatPostCallback)
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/ai/mcps"
|
||||
_ "code.tczkiot.com/wlw/ai-agent/internal/ai/runtime"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/handlers/api"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/middleware"
|
||||
@@ -16,6 +15,7 @@ import (
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/i18nx"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/tracex"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/services"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/web/supportchat"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/mlogclub/simple/web"
|
||||
@@ -60,7 +60,7 @@ func NewServer() (*gin.Engine, error) {
|
||||
|
||||
func corsMiddleware() gin.HandlerFunc {
|
||||
allowedOrigins := config.Current().Server.CORS.AllowedOrigins
|
||||
allowHeaders := "Origin, Content-Type, Accept, Authorization, X-Requested-With, X-Channel-Id"
|
||||
allowHeaders := "Origin, Content-Type, Accept, Authorization, X-Requested-With, X-Channel-Id, X-External-Id, X-External-Name"
|
||||
exposeHeaders := "Content-Length, Content-Type, Authorization"
|
||||
allowMethods := "GET, POST, PUT, PATCH, DELETE, OPTIONS"
|
||||
allowedOriginSet := make(map[string]struct{}, len(allowedOrigins))
|
||||
@@ -147,7 +147,7 @@ func isWebsocketUpgrade(ctx *gin.Context) bool {
|
||||
}
|
||||
|
||||
func addRouter(app *gin.Engine) {
|
||||
app.Any("/api/mcp", gin.WrapH(mcps.NewHTTPHandler()))
|
||||
supportchat.RegisterRoutes(app)
|
||||
|
||||
apiGroup := app.Group("/api")
|
||||
apiGroup.GET("/health", api.Health)
|
||||
@@ -163,12 +163,7 @@ func addRouter(app *gin.Engine) {
|
||||
|
||||
dashboardGroup := app.Group("/api/dashboard", middleware.AuthMiddleware)
|
||||
registerDashboardDashboardRoutes(dashboardGroup.Group("/dashboard"))
|
||||
registerDashboardCompanyRoutes(dashboardGroup.Group("/company"))
|
||||
registerDashboardCustomerRoutes(dashboardGroup.Group("/customer"))
|
||||
registerDashboardCustomerContactRoutes(dashboardGroup.Group("/customer-contact"))
|
||||
registerDashboardTagRoutes(dashboardGroup.Group("/tag"))
|
||||
registerDashboardConversationRoutes(dashboardGroup.Group("/conversation"))
|
||||
registerDashboardTicketRoutes(dashboardGroup.Group("/ticket"))
|
||||
registerDashboardNotificationRoutes(dashboardGroup.Group("/notification"))
|
||||
registerDashboardQuickReplyRoutes(dashboardGroup.Group("/quick-reply"))
|
||||
registerDashboardChannelRoutes(dashboardGroup.Group("/channel"))
|
||||
@@ -176,9 +171,9 @@ func addRouter(app *gin.Engine) {
|
||||
registerDashboardAgentTeamRoutes(dashboardGroup.Group("/agent-team"))
|
||||
registerDashboardAgentTeamScheduleRoutes(dashboardGroup.Group("/agent-team-schedule"))
|
||||
registerDashboardAIAgentRoutes(dashboardGroup.Group("/ai-agent"))
|
||||
registerDashboardAIWorkflowRoutes(dashboardGroup.Group("/ai-workflow"))
|
||||
registerDashboardAgentRunRoutes(dashboardGroup.Group("/agent-run"))
|
||||
registerDashboardAIConfigRoutes(dashboardGroup.Group("/ai-config"))
|
||||
registerDashboardPlatformAIRoutes(dashboardGroup.Group("/platform-ai"))
|
||||
registerDashboardAssetRoutes(dashboardGroup.Group("/asset"))
|
||||
registerDashboardKnowledgeBaseRoutes(dashboardGroup.Group("/knowledge-base"))
|
||||
registerDashboardKnowledgeDirectoryRoutes(dashboardGroup.Group("/knowledge-directory"))
|
||||
@@ -186,8 +181,6 @@ func addRouter(app *gin.Engine) {
|
||||
registerDashboardKnowledgeFAQRoutes(dashboardGroup.Group("/knowledge-faq"))
|
||||
registerDashboardKnowledgeRetrieveRoutes(dashboardGroup.Group("/knowledge-retrieve"))
|
||||
registerDashboardKnowledgeRetrieveLogRoutes(dashboardGroup.Group("/knowledge-retrieve-log"))
|
||||
registerDashboardSkillDefinitionRoutes(dashboardGroup.Group("/skill-definition"))
|
||||
registerDashboardMCPRoutes(dashboardGroup.Group("/mcp"))
|
||||
|
||||
thirdGroup := app.Group("/api/third")
|
||||
registerThirdWechatRoutes(thirdGroup.Group("/wechat"))
|
||||
|
||||
@@ -34,10 +34,6 @@ func TestNewServerRegistersGinRoutes(t *testing.T) {
|
||||
http.MethodGet + " /api/config",
|
||||
http.MethodGet + " /api/health",
|
||||
http.MethodPost + " /api/dashboard/conversation/send_message",
|
||||
http.MethodGet + " /api/dashboard/ai-workflow/default-definition",
|
||||
http.MethodGet + " /api/dashboard/ai-workflow/template/list",
|
||||
http.MethodGet + " /api/dashboard/ai-workflow/run/list",
|
||||
http.MethodGet + " /api/dashboard/ai-workflow/run/:id",
|
||||
http.MethodGet + " /api/dashboard/agent-run/metrics",
|
||||
http.MethodPost + " /api/dashboard/agent-run/evaluate",
|
||||
http.MethodGet + " /api/dashboard/agent-run/:id",
|
||||
@@ -45,6 +41,7 @@ func TestNewServerRegistersGinRoutes(t *testing.T) {
|
||||
http.MethodPost + " /api/dashboard/channel/rollback_ai_agent_rollout",
|
||||
http.MethodPost + " /api/dashboard/agent-run/quality_feedback",
|
||||
http.MethodGet + " /api/dashboard/agent-run/list",
|
||||
http.MethodGet + " /api/dashboard/platform-ai/status",
|
||||
http.MethodGet + " /api/ws/dashboard",
|
||||
http.MethodGet + " /api/ws/open",
|
||||
}
|
||||
@@ -55,6 +52,12 @@ func TestNewServerRegistersGinRoutes(t *testing.T) {
|
||||
}
|
||||
|
||||
removed := []string{
|
||||
http.MethodGet + " /api/dashboard/ai-workflow/default-definition",
|
||||
http.MethodGet + " /api/dashboard/ai-workflow/template/list",
|
||||
http.MethodGet + " /api/dashboard/ai-workflow/run/list",
|
||||
http.MethodGet + " /api/dashboard/ai-workflow/run/:id",
|
||||
http.MethodGet + " /api/dashboard/skill-definition/list",
|
||||
http.MethodGet + " /api/dashboard/mcp/server/list",
|
||||
http.MethodPost + " /api/auth/login",
|
||||
http.MethodGet + " /api/auth/profile",
|
||||
http.MethodGet + " /api/dashboard/user/list",
|
||||
|
||||
Reference in New Issue
Block a user