Files
geo/server/internal/tenant/transport/desktop_client_handler.go
T
root ce028c56bf feat(desktop): auto-recover in-progress desktop tasks on client lifecycle events
- reset stale in_progress tasks owned by a client on startup, offline, and lease expiry
- monitor tasks re-queue for another pick; publish tasks move to unknown for manual reconcile
- send startup=true flag on first heartbeat so the server can trigger recovery
- finalize the linked desktop task when a phase2 monitor callback arrives via desktop_tasks path
- short-circuit the desktop monitor attempt write after the callback already finalized it

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 09:11:23 +08:00

90 lines
2.3 KiB
Go

package transport
import (
"github.com/gin-gonic/gin"
"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/response"
"github.com/geo-platform/tenant-api/internal/tenant/app"
"github.com/geo-platform/tenant-api/internal/tenant/repository"
)
type DesktopClientHandler struct {
svc *app.DesktopClientService
}
func NewDesktopClientHandler(a *bootstrap.App) *DesktopClientHandler {
taskSvc := app.NewDesktopTaskService(
a.DB,
a.MonitoringDB,
a.RabbitMQ,
a.Logger,
).WithCache(a.Cache)
return &DesktopClientHandler{
svc: app.NewDesktopClientService(
repository.NewDesktopClientRepository(a.DB),
a.Redis,
).WithTaskService(taskSvc),
}
}
func (h *DesktopClientHandler) Register(c *gin.Context) {
actor := auth.MustActor(c.Request.Context())
var req app.RegisterDesktopClientRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, response.ErrBadRequest(40001, "invalid_params", err.Error()))
return
}
data, err := h.svc.Register(c.Request.Context(), actor, req)
if err != nil {
response.Error(c, err)
return
}
response.SuccessWithStatus(c, 201, data)
}
func (h *DesktopClientHandler) Rotate(c *gin.Context) {
data, err := h.svc.Rotate(c.Request.Context(), MustDesktopClient(c.Request.Context()))
if err != nil {
response.Error(c, err)
return
}
response.Success(c, data)
}
func (h *DesktopClientHandler) Heartbeat(c *gin.Context) {
var req app.HeartbeatDesktopClientRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, response.ErrBadRequest(40001, "invalid_params", err.Error()))
return
}
data, err := h.svc.Heartbeat(c.Request.Context(), MustDesktopClient(c.Request.Context()), req)
if err != nil {
response.Error(c, err)
return
}
response.Success(c, data)
}
func (h *DesktopClientHandler) Revoke(c *gin.Context) {
data, err := h.svc.Revoke(c.Request.Context(), MustDesktopClient(c.Request.Context()))
if err != nil {
response.Error(c, err)
return
}
response.Success(c, data)
}
func (h *DesktopClientHandler) Offline(c *gin.Context) {
data, err := h.svc.Offline(c.Request.Context(), MustDesktopClient(c.Request.Context()))
if err != nil {
response.Error(c, err)
return
}
response.Success(c, data)
}