6066f43a7d
- Implement monitoring service with heartbeat, lease tasks, resume tasks, and task result handling. - Create monitoring time utilities for business date calculations. - Add unit tests for date window resolution and business day handling. - Define database schema for monitoring-related tables including quotas, daily reports, and task management. - Establish migration scripts for creating and dropping monitoring tables.
163 lines
4.3 KiB
Go
163 lines
4.3 KiB
Go
package transport
|
|
|
|
import (
|
|
"errors"
|
|
"io"
|
|
"strconv"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"github.com/geo-platform/tenant-api/internal/bootstrap"
|
|
"github.com/geo-platform/tenant-api/internal/shared/response"
|
|
"github.com/geo-platform/tenant-api/internal/tenant/app"
|
|
)
|
|
|
|
type MonitoringCallbackHandler struct {
|
|
svc *app.MonitoringCallbackService
|
|
}
|
|
|
|
func NewMonitoringCallbackHandler(a *bootstrap.App) *MonitoringCallbackHandler {
|
|
return &MonitoringCallbackHandler{
|
|
svc: app.NewMonitoringCallbackService(a.DB, a.MonitoringDB, a.RabbitMQ, a.LLM, a.Logger),
|
|
}
|
|
}
|
|
|
|
func (h *MonitoringCallbackHandler) Heartbeat(c *gin.Context) {
|
|
installationID, installationToken, ok := parseMonitoringInstallationHeaders(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
var req app.MonitoringHeartbeatRequest
|
|
if err := bindOptionalJSON(c, &req); err != nil {
|
|
response.Error(c, response.ErrBadRequest(40041, "invalid_params", err.Error()))
|
|
return
|
|
}
|
|
|
|
data, svcErr := h.svc.Heartbeat(c.Request.Context(), installationID, installationToken, req)
|
|
if svcErr != nil {
|
|
response.Error(c, svcErr)
|
|
return
|
|
}
|
|
response.Success(c, data)
|
|
}
|
|
|
|
func (h *MonitoringCallbackHandler) LeaseTasks(c *gin.Context) {
|
|
installationID, installationToken, ok := parseMonitoringInstallationHeaders(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
var req app.MonitoringLeaseTasksRequest
|
|
if err := bindOptionalJSON(c, &req); err != nil {
|
|
response.Error(c, response.ErrBadRequest(40041, "invalid_params", err.Error()))
|
|
return
|
|
}
|
|
|
|
data, svcErr := h.svc.LeaseTasks(c.Request.Context(), installationID, installationToken, req)
|
|
if svcErr != nil {
|
|
response.Error(c, svcErr)
|
|
return
|
|
}
|
|
response.Success(c, data)
|
|
}
|
|
|
|
func (h *MonitoringCallbackHandler) ResumeTasks(c *gin.Context) {
|
|
installationID, installationToken, ok := parseMonitoringInstallationHeaders(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
var req app.MonitoringResumeTasksRequest
|
|
if err := bindOptionalJSON(c, &req); err != nil {
|
|
response.Error(c, response.ErrBadRequest(40041, "invalid_params", err.Error()))
|
|
return
|
|
}
|
|
|
|
data, svcErr := h.svc.ResumeTasks(c.Request.Context(), installationID, installationToken, req)
|
|
if svcErr != nil {
|
|
response.Error(c, svcErr)
|
|
return
|
|
}
|
|
response.Success(c, data)
|
|
}
|
|
|
|
func (h *MonitoringCallbackHandler) TaskResult(c *gin.Context) {
|
|
taskID, err := strconv.ParseInt(c.Param("id"), 10, 64)
|
|
if err != nil {
|
|
response.Error(c, response.ErrBadRequest(40041, "invalid_task_id", "task id must be a number"))
|
|
return
|
|
}
|
|
|
|
installationID, installationToken, ok := parseMonitoringInstallationHeaders(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
var req app.MonitoringTaskResultRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
response.Error(c, response.ErrBadRequest(40041, "invalid_params", err.Error()))
|
|
return
|
|
}
|
|
|
|
data, svcErr := h.svc.HandleTaskResult(c.Request.Context(), installationID, installationToken, taskID, req)
|
|
if svcErr != nil {
|
|
response.Error(c, svcErr)
|
|
return
|
|
}
|
|
response.Success(c, data)
|
|
}
|
|
|
|
func (h *MonitoringCallbackHandler) SkipTask(c *gin.Context) {
|
|
taskID, err := strconv.ParseInt(c.Param("id"), 10, 64)
|
|
if err != nil {
|
|
response.Error(c, response.ErrBadRequest(40041, "invalid_task_id", "task id must be a number"))
|
|
return
|
|
}
|
|
|
|
installationID, installationToken, ok := parseMonitoringInstallationHeaders(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
var req app.MonitoringSkipTaskRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
response.Error(c, response.ErrBadRequest(40041, "invalid_params", err.Error()))
|
|
return
|
|
}
|
|
|
|
data, svcErr := h.svc.SkipTask(c.Request.Context(), installationID, installationToken, taskID, req)
|
|
if svcErr != nil {
|
|
response.Error(c, svcErr)
|
|
return
|
|
}
|
|
response.Success(c, data)
|
|
}
|
|
|
|
func parseMonitoringInstallationHeaders(c *gin.Context) (int64, string, bool) {
|
|
installationID, err := strconv.ParseInt(c.GetHeader("X-Geo-Installation-Id"), 10, 64)
|
|
if err != nil {
|
|
response.Error(c, response.ErrUnauthorized(40141, "invalid_installation_id", "installation id header is invalid"))
|
|
return 0, "", false
|
|
}
|
|
|
|
installationToken := c.GetHeader("X-Geo-Installation-Token")
|
|
if installationToken == "" {
|
|
response.Error(c, response.ErrUnauthorized(40141, "missing_installation_token", "installation token header is required"))
|
|
return 0, "", false
|
|
}
|
|
|
|
return installationID, installationToken, true
|
|
}
|
|
|
|
func bindOptionalJSON(c *gin.Context, target interface{}) error {
|
|
err := c.ShouldBindJSON(target)
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
if errors.Is(err, io.EOF) {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|