Files
geo/server/internal/tenant/transport/router.go
T
root a617d39a4a feat(desktop): drop parked review flow, add publish management
SaaS 侧人工审核后才创建 publish job,desktop 不再做二次审核:移除
manual/waiting_user/parked/from_parked 状态机与 LeaseFromParked 查询,
desktop client 只执行发布并新增"发布管理"页(待发布队列 / 历史 / 再次
发送)。同时抽离 @geo/publisher-platforms 共享适配器包、新增 Redis-based
desktop_presence 与 publish_record_support,刷新 admin-web 发布弹窗与
媒体库;plan A / spec 文档同步口径。
2026-04-20 09:52:48 +08:00

242 lines
12 KiB
Go

package transport
import (
"github.com/geo-platform/tenant-api/internal/bootstrap"
"github.com/geo-platform/tenant-api/internal/shared/auth"
"github.com/geo-platform/tenant-api/internal/shared/middleware"
"github.com/geo-platform/tenant-api/internal/tenant/repository"
)
func RegisterRoutes(app *bootstrap.App) {
authAPI := app.Engine.Group("/api/auth")
authHandler := NewAuthHandler(app)
authAPI.POST("/login", authHandler.Login)
authAPI.POST("/refresh", authHandler.Refresh)
publicAssets := NewAssetHandler(app)
app.Engine.GET("/api/public/assets/:token", publicAssets.Serve)
pluginHandler := NewMediaHandler(app)
callbacks := app.Engine.Group("/api/callback/plugin")
callbacks.POST("/bind", pluginHandler.PluginBindCallback)
callbacks.POST("/publish", pluginHandler.PluginPublishCallback)
protected := app.Engine.Group("/api")
protected.Use(auth.Middleware(app.JWT, app.Sessions))
protected.Use(middleware.TenantScope())
protected.GET("/auth/me", authHandler.Me)
protected.POST("/auth/logout", authHandler.Logout)
desktopClientHandler := NewDesktopClientHandler(app)
desktopAccountHandler := NewDesktopAccountHandler(app)
desktopTaskHandler := NewDesktopTaskHandler(app)
desktopEventsHandler := NewDesktopEventsHandler(app)
desktopContentHandler := NewDesktopContentHandler(app)
publishJobHandler := NewPublishJobHandler(app)
protected.POST("/desktop/clients/register", desktopClientHandler.Register)
desktopAuth := app.Engine.Group("/api/desktop")
desktopAuth.Use(DesktopClientMiddleware(repository.NewDesktopClientRepository(app.DB)))
desktopAuth.POST("/clients/rotate", desktopClientHandler.Rotate)
desktopAuth.POST("/clients/heartbeat", desktopClientHandler.Heartbeat)
desktopAuth.POST("/clients/offline", desktopClientHandler.Offline)
desktopAuth.POST("/clients/revoke", desktopClientHandler.Revoke)
desktopAuth.GET("/events", desktopEventsHandler.Stream)
desktopAuth.GET("/accounts", desktopAccountHandler.List)
desktopAuth.GET("/content/articles/:id", desktopContentHandler.Article)
desktopAuth.GET("/publish-tasks", desktopTaskHandler.ListPublish)
desktopAuth.POST("/accounts", desktopAccountHandler.Upsert)
desktopAuth.PATCH("/accounts/:id", desktopAccountHandler.Patch)
desktopAuth.DELETE("/accounts/:id", desktopAccountHandler.Delete)
desktopAuth.POST("/tasks/lease", desktopTaskHandler.Lease)
desktopAuth.POST("/tasks/:id/lease", desktopTaskHandler.Lease)
desktopAuth.POST("/tasks/:id/extend", desktopTaskHandler.Extend)
desktopAuth.POST("/tasks/:id/cancel", desktopTaskHandler.Cancel)
desktopAuth.POST("/tasks/:id/result", desktopTaskHandler.Result)
desktopAuth.POST("/publish-tasks/:id/retry", desktopTaskHandler.RetryPublish)
tenantProtected := protected.Group("/tenant")
tenantProtected.Use(RequireActiveTenantSubscription(repository.NewTenantPlanRepository(app.DB)))
tenantProtected.GET("/accounts", desktopAccountHandler.TenantList)
tenantProtected.POST("/accounts/:id/request-delete", desktopAccountHandler.RequestDelete)
tenantProtected.POST("/tasks/:id/reconcile", desktopTaskHandler.Reconcile)
tenantProtected.POST("/tasks/:id/cancel", desktopTaskHandler.TenantCancel)
tenantProtected.POST("/publish-jobs", publishJobHandler.Create)
media := tenantProtected.Group("/media")
media.GET("/platforms", pluginHandler.ListPlatforms)
workspace := tenantProtected.Group("/workspace")
wsHandler := NewWorkspaceHandler(app)
workspace.GET("/overview", wsHandler.Overview)
workspace.GET("/recent-articles", wsHandler.RecentArticles)
workspace.GET("/quota-summary", wsHandler.QuotaSummary)
workspace.GET("/template-cards", wsHandler.TemplateCards)
workspace.GET("/kol-cards", wsHandler.KolCards)
templates := tenantProtected.Group("/templates")
tplHandler := NewTemplateHandler(app)
templates.GET("", tplHandler.List)
templates.GET("/:id", tplHandler.Detail)
templates.POST("/:id/drafts", tplHandler.SaveDraft)
templates.POST("/:id/analyze-tasks", tplHandler.CreateAnalyzeTask)
templates.GET("/:id/analyze_task_result", tplHandler.GetAnalyzeTaskResult)
templates.POST("/:id/title-tasks", tplHandler.CreateTitleTask)
templates.GET("/:id/gen_title_task_result", tplHandler.GetTitleTaskResult)
templates.POST("/:id/outline-tasks", tplHandler.CreateOutlineTask)
templates.GET("/:id/gen_outline_task_result", tplHandler.GetOutlineTaskResult)
templates.POST("/:id/generate", tplHandler.Generate)
kolManage := tenantProtected.Group("/kol/manage")
kolManageHandler := NewKolManageHandler(app, publicAssets)
kolManage.GET("/profile", kolManageHandler.GetProfile)
kolManage.PUT("/profile", kolManageHandler.UpdateProfile)
kolManage.POST("/profile/avatar", kolManageHandler.UploadAvatar)
kolManage.GET("/packages", kolManageHandler.ListPackages)
kolManage.POST("/packages", kolManageHandler.CreatePackage)
kolManage.PUT("/packages/:id", kolManageHandler.UpdatePackage)
kolManage.DELETE("/packages/:id", kolManageHandler.DeletePackage)
kolManage.PUT("/packages/:id/publish", kolManageHandler.PublishPackage)
kolManage.PUT("/packages/:id/archive", kolManageHandler.ArchivePackage)
kolManage.GET("/packages/:id/prompts", kolManageHandler.ListPrompts)
kolManage.POST("/packages/:id/prompts", kolManageHandler.CreatePrompt)
kolManage.GET("/prompts/:id", kolManageHandler.GetPromptLatest)
kolManage.PUT("/prompts/:id", kolManageHandler.UpdatePromptMetadata)
kolManage.DELETE("/prompts/:id", kolManageHandler.DeletePrompt)
kolManage.POST("/prompts/:id/save", kolManageHandler.SavePrompt)
kolManage.POST("/prompts/:id/publish", kolManageHandler.PublishPrompt)
kolManage.PUT("/prompts/:id/activate", kolManageHandler.ActivatePrompt)
kolManage.PUT("/prompts/:id/archive", kolManageHandler.ArchivePrompt)
kolManage.POST("/assist", kolManageHandler.AssistSubmit)
kolManage.POST("/assist/stream", kolManageHandler.AssistStream)
kolManage.GET("/assist/:id", kolManageHandler.AssistGet)
kolDash := tenantProtected.Group("/kol/dashboard")
kolDashboardHandler := NewKolDashboardHandler(app)
kolDash.GET("/overview", kolDashboardHandler.Overview)
kolDash.GET("/packages", kolDashboardHandler.Packages)
kolDash.GET("/trend", kolDashboardHandler.Trend)
kolMarketplace := tenantProtected.Group("/kol/marketplace")
kolMarketplaceHandler := NewKolMarketplaceHandler(app)
kolMarketplace.GET("/packages", kolMarketplaceHandler.ListPackages)
kolMarketplace.GET("/packages/:id", kolMarketplaceHandler.GetPackage)
kolMarketplace.POST("/packages/:id/subscribe", kolMarketplaceHandler.Subscribe)
kolSubscriptions := tenantProtected.Group("/kol")
kolSubscriptions.GET("/subscriptions", kolMarketplaceHandler.ListSubscriptions)
kolSubscriptions.GET("/subscription-prompts", kolMarketplaceHandler.ListSubscriptionPrompts)
kolSubscriptions.GET("/subscription-prompts/:id/schema", kolMarketplaceHandler.SubscriptionPromptSchema)
kolSubscriptions.POST("/subscription-prompts/:id/generate", kolMarketplaceHandler.Generate)
// V1 compromise: these admin operations live on tenant-api and are gated by
// tenant_role=tenant_admin until the platform-api/module is built.
kolAdmin := tenantProtected.Group("/kol/admin")
kolAdminHandler := NewKolAdminHandler(app)
kolAdmin.PUT("/subscriptions/:id/approve", kolAdminHandler.ApproveSubscription)
kolAdmin.POST("/subscriptions", kolAdminHandler.ManualBind)
kolAdmin.PUT("/subscriptions/:id/revoke", kolAdminHandler.RevokeSubscription)
articles := tenantProtected.Group("/articles")
artHandler := NewArticleHandler(app)
articles.GET("", artHandler.List)
articles.POST("", artHandler.Create)
articles.POST("/generate-from-rule", artHandler.GenerateFromRule)
articles.POST("/:id/images", artHandler.UploadImage)
articles.POST("/:id/selection-optimize/stream", artHandler.OptimizeSelectionStream)
articles.GET("/:id", artHandler.Detail)
articles.PUT("/:id", artHandler.Update)
articles.GET("/:id/stream", artHandler.Stream)
articles.GET("/:id/versions", artHandler.Versions)
articles.GET("/:id/publish-records", pluginHandler.ListPublishRecords)
articles.POST("/:id/publish-batches", pluginHandler.CreatePublishBatch)
articles.DELETE("/:id", artHandler.Delete)
brands := tenantProtected.Group("/brands")
brandHandler := NewBrandHandler(app)
brands.GET("", brandHandler.List)
brands.GET("/library-summary", brandHandler.Summary)
brands.POST("", brandHandler.Create)
brands.GET("/:id", brandHandler.Detail)
brands.PUT("/:id", brandHandler.Update)
brands.DELETE("/:id", brandHandler.Delete)
brands.GET("/:id/keywords", brandHandler.ListKeywords)
brands.POST("/:id/keywords", brandHandler.CreateKeyword)
brands.PUT("/:id/keywords/:kid", brandHandler.UpdateKeyword)
brands.DELETE("/:id/keywords/:kid", brandHandler.DeleteKeyword)
brands.GET("/:id/questions", brandHandler.ListQuestions)
brands.POST("/:id/questions", brandHandler.CreateQuestion)
brands.PUT("/:id/questions/:qid", brandHandler.UpdateQuestion)
brands.DELETE("/:id/questions/:qid", brandHandler.DeleteQuestion)
brands.GET("/:id/competitors", brandHandler.ListCompetitors)
brands.POST("/:id/competitors", brandHandler.CreateCompetitor)
brands.PUT("/:id/competitors/:cid", brandHandler.UpdateCompetitor)
brands.DELETE("/:id/competitors/:cid", brandHandler.DeleteCompetitor)
monitoring := tenantProtected.Group("/monitoring")
monitoringHandler := NewMonitoringHandler(app)
monitoring.GET("/dashboard/composite", monitoringHandler.DashboardComposite)
monitoring.GET("/brands/:brand_id/questions/:question_id/detail", monitoringHandler.QuestionDetail)
monitoring.POST("/brands/:brand_id/collect-now", monitoringHandler.CollectNow)
knowledge := tenantProtected.Group("/knowledge")
knowledgeHandler := NewKnowledgeHandler(app)
knowledge.GET("/groups", knowledgeHandler.ListGroups)
knowledge.POST("/groups", knowledgeHandler.CreateGroup)
knowledge.PUT("/groups/:gid", knowledgeHandler.UpdateGroup)
knowledge.DELETE("/groups/:gid", knowledgeHandler.DeleteGroup)
knowledge.GET("/items", knowledgeHandler.ListItems)
knowledge.GET("/items/:id", knowledgeHandler.GetItemDetail)
knowledge.POST("/items/text", knowledgeHandler.CreateTextItem)
knowledge.POST("/items/url", knowledgeHandler.CreateURLItem)
knowledge.POST("/items/file", knowledgeHandler.CreateFileItem)
knowledge.DELETE("/items/:id", knowledgeHandler.DeleteItem)
// Prompt Rules
promptRules := tenantProtected.Group("/prompt-rules")
prHandler := NewPromptRuleHandler(app)
promptRules.GET("", prHandler.List)
promptRules.GET("/simple", prHandler.ListSimple)
promptRules.POST("", prHandler.Create)
promptRules.GET("/:id", prHandler.Detail)
promptRules.PUT("/:id", prHandler.Update)
promptRules.DELETE("/:id", prHandler.Delete)
promptRules.PUT("/:id/status", prHandler.ToggleStatus)
// Prompt Rule Groups
promptRules.GET("/groups", prHandler.ListGroups)
promptRules.POST("/groups", prHandler.CreateGroup)
promptRules.PUT("/groups/:gid", prHandler.UpdateGroup)
promptRules.DELETE("/groups/:gid", prHandler.DeleteGroup)
// Schedule Tasks
schedules := tenantProtected.Group("/schedules")
schHandler := NewScheduleTaskHandler(app)
schedules.GET("", schHandler.List)
schedules.POST("", schHandler.Create)
schedules.GET("/:id", schHandler.Detail)
schedules.PUT("/:id", schHandler.Update)
schedules.DELETE("/:id", schHandler.Delete)
schedules.PUT("/:id/status", schHandler.ToggleStatus)
instantTasks := tenantProtected.Group("/instant-tasks")
instHandler := NewInstantTaskHandler(app)
instantTasks.GET("", instHandler.List)
images := tenantProtected.Group("/images")
imgHandler := NewImageHandler(app)
images.GET("", imgHandler.ListImages)
images.POST("", imgHandler.UploadImage)
images.PUT("/:id", imgHandler.UpdateImage)
images.GET("/:id/references", imgHandler.GetImageReferences)
images.DELETE("/:id", imgHandler.DeleteImage)
images.GET("/storage-usage", imgHandler.StorageUsage)
imageFolders := tenantProtected.Group("/images/folders")
imageFolders.GET("", imgHandler.ListFolders)
imageFolders.POST("", imgHandler.CreateFolder)
imageFolders.PUT("/:id", imgHandler.UpdateFolder)
imageFolders.GET("/:id/delete-preview", imgHandler.FolderDeletePreview)
imageFolders.DELETE("/:id", imgHandler.DeleteFolder)
}