feat(desktop): implement workspace foundation + desktop-client skeleton
Plan 0 (workspaces): add workspaces + workspace_memberships schema, extend JWT/Actor/claims with primary_workspace_id, seed default workspace per tenant, thread workspace_id through tenant monitoring quota. Plan A (desktop skeleton): new Electron app (apps/desktop-client) with main/ preload/renderer, shared Vue component package (packages/ui-shared), and server surface — desktop client registration + token rotation + heartbeat, SSE task event stream, desktop accounts/tasks/content handlers, publish job endpoint, and supporting repositories, services, sqlc queries, and migrations. Hard cutover per plan: remove browser-extension monitoring callback endpoints, stub legacy media API in admin-web, and delete monitoring_callback_handler.go.
This commit is contained in:
@@ -21,14 +21,6 @@ func RegisterRoutes(app *bootstrap.App) {
|
||||
callbacks.POST("/bind", pluginHandler.PluginBindCallback)
|
||||
callbacks.POST("/publish", pluginHandler.PluginPublishCallback)
|
||||
|
||||
monitoringCallbackHandler := NewMonitoringCallbackHandler(app)
|
||||
monitoringPlugin := app.Engine.Group("/api/plugin/monitoring")
|
||||
monitoringPlugin.POST("/heartbeat", monitoringCallbackHandler.Heartbeat)
|
||||
monitoringPlugin.POST("/tasks/lease", monitoringCallbackHandler.LeaseTasks)
|
||||
monitoringPlugin.POST("/tasks/resume", monitoringCallbackHandler.ResumeTasks)
|
||||
monitoringPlugin.POST("/tasks/:id/result", monitoringCallbackHandler.TaskResult)
|
||||
monitoringPlugin.POST("/tasks/:id/skip", monitoringCallbackHandler.SkipTask)
|
||||
|
||||
protected := app.Engine.Group("/api")
|
||||
protected.Use(auth.Middleware(app.JWT, app.Sessions))
|
||||
protected.Use(middleware.TenantScope())
|
||||
@@ -36,8 +28,38 @@ func RegisterRoutes(app *bootstrap.App) {
|
||||
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/revoke", desktopClientHandler.Revoke)
|
||||
desktopAuth.GET("/events", desktopEventsHandler.Stream)
|
||||
desktopAuth.GET("/accounts", desktopAccountHandler.List)
|
||||
desktopAuth.GET("/content/articles/:id", desktopContentHandler.Article)
|
||||
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/park", desktopTaskHandler.Park)
|
||||
desktopAuth.POST("/tasks/:id/cancel", desktopTaskHandler.Cancel)
|
||||
desktopAuth.POST("/tasks/:id/result", desktopTaskHandler.Result)
|
||||
|
||||
tenantProtected := protected.Group("/tenant")
|
||||
tenantProtected.Use(RequireActiveTenantSubscription(repository.NewTenantPlanRepository(app.DB)))
|
||||
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)
|
||||
|
||||
workspace := tenantProtected.Group("/workspace")
|
||||
wsHandler := NewWorkspaceHandler(app)
|
||||
@@ -125,13 +147,6 @@ func RegisterRoutes(app *bootstrap.App) {
|
||||
articles.POST("/:id/publish-batches", pluginHandler.CreatePublishBatch)
|
||||
articles.DELETE("/:id", artHandler.Delete)
|
||||
|
||||
media := tenantProtected.Group("/media")
|
||||
media.GET("/platforms", pluginHandler.ListPlatforms)
|
||||
media.GET("/platform-accounts", pluginHandler.ListPlatformAccounts)
|
||||
media.POST("/plugin-installations/register", pluginHandler.RegisterPluginInstallation)
|
||||
media.POST("/plugin-sessions", pluginHandler.CreatePluginSession)
|
||||
media.DELETE("/platform-accounts/:id", pluginHandler.DeletePlatformAccount)
|
||||
|
||||
brands := tenantProtected.Group("/brands")
|
||||
brandHandler := NewBrandHandler(app)
|
||||
brands.GET("", brandHandler.List)
|
||||
|
||||
Reference in New Issue
Block a user