refactor(tenant): drop legacy plugin_installations, migrate monitoring to desktop_clients

Hard cutover from the browser-extension plugin flow to desktop clients:
remove plugin_installations/plugin_sessions tables and related service,
handler, router, and generated model code; migrate monitoring quotas
and collector types to desktop_clients (UUID primary_client_id);
recreate platform_access_snapshots keyed by client_id; update dev-seed
and callback types accordingly; mark legacy design docs as historical.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-20 13:52:35 +08:00
parent f5a1cd81b2
commit 5ff2e2e74c
25 changed files with 535 additions and 1384 deletions
@@ -4,7 +4,6 @@ import (
"strconv"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
"github.com/geo-platform/tenant-api/internal/bootstrap"
"github.com/geo-platform/tenant-api/internal/shared/response"
@@ -12,14 +11,12 @@ import (
)
type MediaHandler struct {
svc *app.MediaService
logger *zap.Logger
svc *app.MediaService
}
func NewMediaHandler(a *bootstrap.App) *MediaHandler {
return &MediaHandler{
svc: app.NewMediaService(a.DB, a.Logger).WithCache(a.Cache),
logger: a.Logger,
svc: app.NewMediaService(a.DB),
}
}
@@ -32,75 +29,6 @@ func (h *MediaHandler) ListPlatforms(c *gin.Context) {
response.Success(c, data)
}
func (h *MediaHandler) ListPlatformAccounts(c *gin.Context) {
data, err := h.svc.ListPlatformAccounts(c.Request.Context())
if err != nil {
response.Error(c, err)
return
}
response.Success(c, data)
}
func (h *MediaHandler) DeletePlatformAccount(c *gin.Context) {
id, err := strconv.ParseInt(c.Param("id"), 10, 64)
if err != nil {
response.Error(c, response.ErrBadRequest(40001, "invalid_id", "platform account id must be a number"))
return
}
if err := h.svc.DeletePlatformAccount(c.Request.Context(), id); err != nil {
response.Error(c, err)
return
}
response.Success(c, nil)
}
func (h *MediaHandler) CreatePluginSession(c *gin.Context) {
var req app.CreatePluginSessionRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, response.ErrBadRequest(40001, "invalid_params", err.Error()))
return
}
data, err := h.svc.CreatePluginSession(c.Request.Context(), req)
if err != nil {
response.Error(c, err)
return
}
response.Success(c, data)
}
func (h *MediaHandler) RegisterPluginInstallation(c *gin.Context) {
var req app.RegisterPluginInstallationRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, response.ErrBadRequest(40001, "invalid_params", err.Error()))
return
}
data, err := h.svc.RegisterPluginInstallation(c.Request.Context(), req)
if err != nil {
response.Error(c, err)
return
}
response.Success(c, data)
}
func (h *MediaHandler) CreatePublishBatch(c *gin.Context) {
articleID, err := strconv.ParseInt(c.Param("id"), 10, 64)
if err != nil {
response.Error(c, response.ErrBadRequest(40001, "invalid_id", "article id must be a number"))
return
}
var req app.CreatePublishBatchRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, response.ErrBadRequest(40001, "invalid_params", err.Error()))
return
}
data, err := h.svc.CreatePublishBatch(c.Request.Context(), articleID, req)
if err != nil {
response.Error(c, err)
return
}
response.SuccessWithStatus(c, 201, data)
}
func (h *MediaHandler) ListPublishRecords(c *gin.Context) {
articleID, err := strconv.ParseInt(c.Param("id"), 10, 64)
if err != nil {
@@ -114,61 +42,3 @@ func (h *MediaHandler) ListPublishRecords(c *gin.Context) {
}
response.Success(c, data)
}
func (h *MediaHandler) PluginBindCallback(c *gin.Context) {
var req app.PluginBindCallbackRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, response.ErrBadRequest(40001, "invalid_params", err.Error()))
return
}
data, err := h.svc.HandleBindCallback(c.Request.Context(), req)
if err != nil {
if h.logger != nil {
appErr := response.Normalize(err)
h.logger.Warn("publisher bind callback failed",
zap.String("request_id", c.GetString("request_id")),
zap.Int64("plugin_session_id", req.PluginSessionID),
zap.String("platform_id", req.PlatformID),
zap.String("platform_uid", req.PlatformUID),
zap.Int("code", appErr.Code),
zap.String("message", appErr.Message),
zap.String("detail", appErr.Detail),
)
}
response.Error(c, err)
return
}
response.Success(c, data)
}
func (h *MediaHandler) PluginPublishCallback(c *gin.Context) {
var req app.PluginPublishCallbackRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, response.ErrBadRequest(40001, "invalid_params", err.Error()))
return
}
data, err := h.svc.HandlePublishCallback(c.Request.Context(), req)
if err != nil {
if h.logger != nil {
appErr := response.Normalize(err)
h.logger.Warn("publisher publish callback failed",
zap.String("request_id", c.GetString("request_id")),
zap.Int64("plugin_session_id", req.PluginSessionID),
zap.Int64("publish_record_id", req.PublishRecordID),
zap.Int64("platform_account_id", req.PlatformAccountID),
zap.Int64("article_id", req.ArticleID),
zap.String("platform_id", req.PlatformID),
zap.String("status", req.Status),
zap.Stringp("error_message", req.ErrorMessage),
zap.Int("code", appErr.Code),
zap.String("message", appErr.Message),
zap.String("detail", appErr.Detail),
zap.Any("request_payload", req.RequestPayload),
zap.Any("response_payload", req.ResponsePayload),
)
}
response.Error(c, err)
return
}
response.Success(c, data)
}
+3 -7
View File
@@ -16,10 +16,7 @@ func RegisterRoutes(app *bootstrap.App) {
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)
mediaHandler := NewMediaHandler(app)
protected := app.Engine.Group("/api")
protected.Use(auth.Middleware(app.JWT, app.Sessions))
@@ -65,7 +62,7 @@ func RegisterRoutes(app *bootstrap.App) {
tenantProtected.POST("/publish-jobs", publishJobHandler.Create)
media := tenantProtected.Group("/media")
media.GET("/platforms", pluginHandler.ListPlatforms)
media.GET("/platforms", mediaHandler.ListPlatforms)
workspace := tenantProtected.Group("/workspace")
wsHandler := NewWorkspaceHandler(app)
@@ -149,8 +146,7 @@ func RegisterRoutes(app *bootstrap.App) {
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.GET("/:id/publish-records", mediaHandler.ListPublishRecords)
articles.DELETE("/:id", artHandler.Delete)
brands := tenantProtected.Group("/brands")