| package admin |
|
|
| import ( |
| "strconv" |
| "strings" |
| "time" |
|
|
| infraerrors "github.com/Wei-Shaw/sub2api/internal/pkg/errors" |
| "github.com/Wei-Shaw/sub2api/internal/pkg/response" |
| "github.com/Wei-Shaw/sub2api/internal/service" |
|
|
| "github.com/gin-gonic/gin" |
| ) |
|
|
| |
| type ChannelMonitorRequestTemplateHandler struct { |
| templateService *service.ChannelMonitorRequestTemplateService |
| } |
|
|
| |
| func NewChannelMonitorRequestTemplateHandler(templateService *service.ChannelMonitorRequestTemplateService) *ChannelMonitorRequestTemplateHandler { |
| return &ChannelMonitorRequestTemplateHandler{templateService: templateService} |
| } |
|
|
| |
|
|
| type channelMonitorTemplateCreateRequest struct { |
| Name string `json:"name" binding:"required,max=100"` |
| Provider string `json:"provider" binding:"required,oneof=openai anthropic gemini"` |
| APIMode string `json:"api_mode" binding:"omitempty,oneof=chat_completions responses"` |
| Description string `json:"description" binding:"max=500"` |
| ExtraHeaders map[string]string `json:"extra_headers"` |
| BodyOverrideMode string `json:"body_override_mode" binding:"omitempty,oneof=off merge replace"` |
| BodyOverride map[string]any `json:"body_override"` |
| } |
|
|
| type channelMonitorTemplateUpdateRequest struct { |
| Name *string `json:"name" binding:"omitempty,max=100"` |
| APIMode *string `json:"api_mode" binding:"omitempty,oneof=chat_completions responses"` |
| Description *string `json:"description" binding:"omitempty,max=500"` |
| ExtraHeaders *map[string]string `json:"extra_headers"` |
| BodyOverrideMode *string `json:"body_override_mode" binding:"omitempty,oneof=off merge replace"` |
| BodyOverride *map[string]any `json:"body_override"` |
| } |
|
|
| type channelMonitorTemplateResponse struct { |
| ID int64 `json:"id"` |
| Name string `json:"name"` |
| Provider string `json:"provider"` |
| APIMode string `json:"api_mode"` |
| Description string `json:"description"` |
| ExtraHeaders map[string]string `json:"extra_headers"` |
| BodyOverrideMode string `json:"body_override_mode"` |
| BodyOverride map[string]any `json:"body_override"` |
| CreatedAt string `json:"created_at"` |
| UpdatedAt string `json:"updated_at"` |
| AssociatedMonitors int64 `json:"associated_monitors"` |
| } |
|
|
| func (h *ChannelMonitorRequestTemplateHandler) toResponse(c *gin.Context, t *service.ChannelMonitorRequestTemplate) *channelMonitorTemplateResponse { |
| if t == nil { |
| return nil |
| } |
| headers := t.ExtraHeaders |
| if headers == nil { |
| headers = map[string]string{} |
| } |
| count, _ := h.templateService.CountAssociatedMonitors(c.Request.Context(), t.ID) |
| return &channelMonitorTemplateResponse{ |
| ID: t.ID, |
| Name: t.Name, |
| Provider: t.Provider, |
| APIMode: t.APIMode, |
| Description: t.Description, |
| ExtraHeaders: headers, |
| BodyOverrideMode: t.BodyOverrideMode, |
| BodyOverride: t.BodyOverride, |
| CreatedAt: t.CreatedAt.UTC().Format(time.RFC3339), |
| UpdatedAt: t.UpdatedAt.UTC().Format(time.RFC3339), |
| AssociatedMonitors: count, |
| } |
| } |
|
|
| |
| func parseTemplateID(c *gin.Context) (int64, bool) { |
| id, err := strconv.ParseInt(c.Param("id"), 10, 64) |
| if err != nil || id <= 0 { |
| response.ErrorFrom(c, infraerrors.BadRequest("INVALID_TEMPLATE_ID", "invalid template id")) |
| return 0, false |
| } |
| return id, true |
| } |
|
|
| |
|
|
| |
| func (h *ChannelMonitorRequestTemplateHandler) List(c *gin.Context) { |
| items, err := h.templateService.List(c.Request.Context(), service.ChannelMonitorRequestTemplateListParams{ |
| Provider: strings.TrimSpace(c.Query("provider")), |
| APIMode: strings.TrimSpace(c.Query("api_mode")), |
| }) |
| if err != nil { |
| response.ErrorFrom(c, err) |
| return |
| } |
| out := make([]*channelMonitorTemplateResponse, 0, len(items)) |
| for _, t := range items { |
| out = append(out, h.toResponse(c, t)) |
| } |
| response.Success(c, gin.H{"items": out}) |
| } |
|
|
| |
| func (h *ChannelMonitorRequestTemplateHandler) Get(c *gin.Context) { |
| id, ok := parseTemplateID(c) |
| if !ok { |
| return |
| } |
| t, err := h.templateService.Get(c.Request.Context(), id) |
| if err != nil { |
| response.ErrorFrom(c, err) |
| return |
| } |
| response.Success(c, h.toResponse(c, t)) |
| } |
|
|
| |
| func (h *ChannelMonitorRequestTemplateHandler) Create(c *gin.Context) { |
| var req channelMonitorTemplateCreateRequest |
| if err := c.ShouldBindJSON(&req); err != nil { |
| response.ErrorFrom(c, infraerrors.BadRequest("VALIDATION_ERROR", err.Error())) |
| return |
| } |
| t, err := h.templateService.Create(c.Request.Context(), service.ChannelMonitorRequestTemplateCreateParams{ |
| Name: req.Name, |
| Provider: req.Provider, |
| APIMode: req.APIMode, |
| Description: req.Description, |
| ExtraHeaders: req.ExtraHeaders, |
| BodyOverrideMode: req.BodyOverrideMode, |
| BodyOverride: req.BodyOverride, |
| }) |
| if err != nil { |
| response.ErrorFrom(c, err) |
| return |
| } |
| response.Created(c, h.toResponse(c, t)) |
| } |
|
|
| |
| func (h *ChannelMonitorRequestTemplateHandler) Update(c *gin.Context) { |
| id, ok := parseTemplateID(c) |
| if !ok { |
| return |
| } |
| var req channelMonitorTemplateUpdateRequest |
| if err := c.ShouldBindJSON(&req); err != nil { |
| response.ErrorFrom(c, infraerrors.BadRequest("VALIDATION_ERROR", err.Error())) |
| return |
| } |
| t, err := h.templateService.Update(c.Request.Context(), id, service.ChannelMonitorRequestTemplateUpdateParams{ |
| Name: req.Name, |
| APIMode: req.APIMode, |
| Description: req.Description, |
| ExtraHeaders: req.ExtraHeaders, |
| BodyOverrideMode: req.BodyOverrideMode, |
| BodyOverride: req.BodyOverride, |
| }) |
| if err != nil { |
| response.ErrorFrom(c, err) |
| return |
| } |
| response.Success(c, h.toResponse(c, t)) |
| } |
|
|
| |
| func (h *ChannelMonitorRequestTemplateHandler) Delete(c *gin.Context) { |
| id, ok := parseTemplateID(c) |
| if !ok { |
| return |
| } |
| if err := h.templateService.Delete(c.Request.Context(), id); err != nil { |
| response.ErrorFrom(c, err) |
| return |
| } |
| response.Success(c, nil) |
| } |
|
|
| type channelMonitorTemplateApplyRequest struct { |
| |
| |
| MonitorIDs []int64 `json:"monitor_ids" binding:"required,min=1"` |
| } |
|
|
| |
| |
| func (h *ChannelMonitorRequestTemplateHandler) Apply(c *gin.Context) { |
| id, ok := parseTemplateID(c) |
| if !ok { |
| return |
| } |
| var req channelMonitorTemplateApplyRequest |
| if err := c.ShouldBindJSON(&req); err != nil { |
| response.ErrorFrom(c, infraerrors.BadRequest("VALIDATION_ERROR", err.Error())) |
| return |
| } |
| affected, err := h.templateService.ApplyToMonitors(c.Request.Context(), id, req.MonitorIDs) |
| if err != nil { |
| response.ErrorFrom(c, err) |
| return |
| } |
| response.Success(c, gin.H{"affected": affected}) |
| } |
|
|
| type associatedMonitorBriefResponse struct { |
| ID int64 `json:"id"` |
| Name string `json:"name"` |
| Provider string `json:"provider"` |
| APIMode string `json:"api_mode"` |
| Enabled bool `json:"enabled"` |
| } |
|
|
| |
| |
| func (h *ChannelMonitorRequestTemplateHandler) AssociatedMonitors(c *gin.Context) { |
| id, ok := parseTemplateID(c) |
| if !ok { |
| return |
| } |
| items, err := h.templateService.ListAssociatedMonitors(c.Request.Context(), id) |
| if err != nil { |
| response.ErrorFrom(c, err) |
| return |
| } |
| out := make([]associatedMonitorBriefResponse, 0, len(items)) |
| for _, m := range items { |
| out = append(out, associatedMonitorBriefResponse{ |
| ID: m.ID, Name: m.Name, Provider: m.Provider, APIMode: m.APIMode, Enabled: m.Enabled, |
| }) |
| } |
| response.Success(c, gin.H{"items": out}) |
| } |
|
|