refactor: 将客服后端重构为宿主可嵌入模块

- 注入数据库、运行时配置、统一响应、文件存储和平台 AI 能力,补充业务读写工具与客户快捷操作契约。

- 移除模块内重复的组织、客户、工单、标签、技能、旧工作流、MCP 和迁移实现,将身份权限与业务主体交由宿主管理。

- 使用 libSQL 重构向量存储,并完善图片消息、访客身份、排队调度、企业微信和支持聊天页面。

- 统一 HTTP、DTO 与 WebSocket 的 snake_case 协议,补齐模块初始化、业务动作和公共载荷等回归测试。
This commit is contained in:
t
2026-08-28 22:23:13 +08:00
parent 6845c728f8
commit 18c9354095
377 changed files with 13199 additions and 22881 deletions
+2 -2
View File
@@ -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
View File
@@ -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
}
+37 -63
View File
@@ -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")
}
}
+37 -7
View File
@@ -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
}
-15
View File
@@ -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()
}
+6 -97
View File
@@ -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)
+4 -11
View File
@@ -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"))
+7 -4
View File
@@ -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",