refactor(msg_gateway): restructure and rename message_gateway aligned with custom_example

This commit is contained in:
ryan
2026-09-02 22:50:04 +08:00
parent 87e3bfd0e6
commit 8395dd5019
57 changed files with 2150 additions and 2120 deletions
@@ -0,0 +1,139 @@
// Copyright 2026 Arctel.net
// SPDX-License-Identifier: Apache-2.0
package controller
import (
"Wavelet/pkg/response"
"Wavelet/plugins/domain/msg_gateway/consts"
"Wavelet/plugins/domain/msg_gateway/service"
"net/http"
"strconv"
"github.com/gin-gonic/gin"
)
// ListAdminChannelDefinitions returns form schemas for supported channel types.
// @Summary List message gateway channel definitions
// @Description Returns form field definitions for Telegram and QQ channels
// @Tags admin-message-gateway
// @Produce json
// @Security SessionCookie
// @Success 200 {object} response.Any{data=[]do.Definition}
// @Router /api/v1/admin/message-gateway/channels/definitions [get]
func ListAdminChannelDefinitions(c *gin.Context) {
c.JSON(http.StatusOK, response.OK(service.ListDefinitions()))
}
// ListAdminChannels lists configured messaging channels with secrets masked.
// @Summary List message gateway channels
// @Description Returns all messaging channels; secrets are masked
// @Tags admin-message-gateway
// @Produce json
// @Security SessionCookie
// @Success 200 {object} response.Any{data=[]do.ChannelDTO}
// @Router /api/v1/admin/message-gateway/channels [get]
func ListAdminChannels(c *gin.Context) {
rows, err := service.ListChannels(c.Request.Context())
if err != nil {
response.AbortInternal(c, err.Error())
return
}
c.JSON(http.StatusOK, response.OK(rows))
}
func parseAdminChannelID(c *gin.Context) (uint64, bool) {
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
if err != nil {
response.AbortBadRequest(c, consts.ErrInvalidChannelID)
return 0, false
}
return id, true
}
func handleAdminChannelError(c *gin.Context, err error, fallback func(c *gin.Context, msg string)) {
if err.Error() == consts.ErrChannelNotFound {
response.AbortNotFound(c, err.Error())
return
}
fallback(c, err.Error())
}
// CreateAdminChannel creates a messaging channel.
// @Summary Create message gateway channel
// @Description Creates a Telegram or QQ channel with encrypted credentials
// @Tags admin-message-gateway
// @Accept json
// @Produce json
// @Security SessionCookie
// @Param request body do.CreateChannelRequest true "create body"
// @Success 200 {object} response.Any{data=do.ChannelDTO}
// @Failure 400 {object} response.Any
// @Router /api/v1/admin/message-gateway/channels [post]
func CreateAdminChannel(c *gin.Context) {
handleJSONRequest(c, service.CreateChannel)
}
// UpdateAdminChannel patches a messaging channel. Empty secrets keep the previous values.
// @Summary Update message gateway channel
// @Description Updates a channel; empty secrets keep the current ciphertext
// @Tags admin-message-gateway
// @Accept json
// @Produce json
// @Security SessionCookie
// @Param id path int true "channel id"
// @Param request body do.UpdateChannelRequest true "update body"
// @Success 200 {object} response.Any{data=do.ChannelDTO}
// @Failure 400 {object} response.Any
// @Failure 404 {object} response.Any
// @Router /api/v1/admin/message-gateway/channels/{id} [patch]
func UpdateAdminChannel(c *gin.Context) {
handleEntityUpdate(c, parseAdminChannelID, service.UpdateChannel, func(c *gin.Context, err error) {
handleAdminChannelError(c, err, response.AbortBadRequest)
})
}
// DeleteAdminChannel removes a channel and its bindings/pairing codes.
// @Summary Delete message gateway channel
// @Description Deletes a channel and cascaded bindings and pairing codes
// @Tags admin-message-gateway
// @Produce json
// @Security SessionCookie
// @Param id path int true "channel id"
// @Success 200 {object} response.Any
// @Failure 404 {object} response.Any
// @Router /api/v1/admin/message-gateway/channels/{id} [delete]
func DeleteAdminChannel(c *gin.Context) {
id, ok := parseAdminChannelID(c)
if !ok {
return
}
if err := service.DeleteChannel(c.Request.Context(), id); err != nil {
handleAdminChannelError(c, err, response.AbortInternal)
return
}
c.JSON(http.StatusOK, response.OKNil())
}
// TestAdminChannel probes stored credentials (Telegram getMe or QQ token).
// @Summary Test message gateway channel
// @Description Probes stored credentials without returning secrets
// @Tags admin-message-gateway
// @Produce json
// @Security SessionCookie
// @Param id path int true "channel id"
// @Success 200 {object} response.Any
// @Failure 400 {object} response.Any
// @Failure 404 {object} response.Any
// @Router /api/v1/admin/message-gateway/channels/{id}/test [post]
func TestAdminChannel(c *gin.Context) {
id, ok := parseAdminChannelID(c)
if !ok {
return
}
if err := service.ProbeChannel(c.Request.Context(), id); err != nil {
handleAdminChannelError(c, err, response.AbortBadRequest)
return
}
c.JSON(http.StatusOK, response.OKNil())
}
@@ -0,0 +1,146 @@
// Copyright 2026 Arctel.net
// SPDX-License-Identifier: Apache-2.0
package controller
import (
"Wavelet/pkg/response"
"Wavelet/plugins/domain/msg_gateway/consts"
"Wavelet/plugins/domain/msg_gateway/model/do"
"Wavelet/plugins/domain/msg_gateway/service"
"errors"
"net/http"
"strconv"
"github.com/gin-gonic/gin"
)
// ListPushChannelDefinitions 获取各种消息通道的表单配置定义列表
// @Summary 获取所有消息通道配置字段定义
// @Description 返回系统支持的所有消息通道类型的动态表单定义,需要管理员权限
// @Tags admin-push
// @Produce json
// @Security SessionCookie
// @Success 200 {object} response.Any "通道配置定义列表"
// @Router /api/v1/admin/push/channels/definitions [get]
func ListPushChannelDefinitions(c *gin.Context) {
c.JSON(http.StatusOK, response.OK(do.ListPushDefinitions()))
}
// ListPushChannels 获取消息通道列表
// @Summary 获取所有消息通道
// @Description 返回系统配置的所有消息通道列表,需要管理员权限
// @Tags admin-push
// @Produce json
// @Security SessionCookie
// @Success 200 {object} response.Any{data=[]entity.PushChannel} "消息通道列表"
// @Router /api/v1/admin/push/channels [get]
func ListPushChannels(c *gin.Context) {
channels, err := service.ListPushChannels(c.Request.Context())
if err != nil {
response.AbortInternal(c, err.Error())
return
}
c.JSON(http.StatusOK, response.OK(channels))
}
// parsePushChannelID reads the path identifier of a push channel.
func parsePushChannelID(c *gin.Context) (uint64, bool) {
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
if err != nil {
response.AbortBadRequest(c, consts.ErrInvalidChannelID)
return 0, false
}
return id, true
}
// handlePushChannelNotFoundError maps a missing channel row to 404, others to fallback.
func handlePushChannelNotFoundError(c *gin.Context, err error, fallback func(c *gin.Context, msg string)) {
if errors.Is(err, consts.ErrRecordNotFound) {
response.AbortNotFound(c, consts.ErrChannelNotFound)
return
}
fallback(c, err.Error())
}
// CreatePushChannel 创建消息通道
// @Summary 创建消息通道
// @Description 新建一个消息通道配置,需要管理员权限
// @Tags admin-push
// @Accept json
// @Produce json
// @Security SessionCookie
// @Param request body do.CreatePushChannelRequest true "创建参数"
// @Success 200 {object} response.Any{data=entity.PushChannel} "创建成功"
// @Router /api/v1/admin/push/channels [post]
func CreatePushChannel(c *gin.Context) {
handleJSONRequest(c, service.CreatePushChannel)
}
// UpdatePushChannel 更新消息通道
// @Summary 更新消息通道
// @Description 修改消息通道配置,需要管理员权限
// @Tags admin-push
// @Accept json
// @Produce json
// @Security SessionCookie
// @Param id path uint64 true "通道ID"
// @Param request body do.UpdatePushChannelRequest true "更新参数"
// @Success 200 {object} response.Any{data=entity.PushChannel} "更新成功"
// @Router /api/v1/admin/push/channels/{id} [put]
func UpdatePushChannel(c *gin.Context) {
handleEntityUpdate(c, parsePushChannelID, service.UpdatePushChannel, func(c *gin.Context, err error) {
handlePushChannelNotFoundError(c, err, response.AbortInternal)
})
}
// DeletePushChannel 删除消息通道
// @Summary 删除消息通道
// @Description 根据ID删除消息通道,需要管理员权限
// @Tags admin-push
// @Produce json
// @Security SessionCookie
// @Param id path uint64 true "通道ID"
// @Success 200 {object} response.Any "删除成功"
// @Router /api/v1/admin/push/channels/{id} [delete]
func DeletePushChannel(c *gin.Context) {
id, ok := parsePushChannelID(c)
if !ok {
return
}
if err := service.DeletePushChannel(c.Request.Context(), id); err != nil {
handlePushChannelNotFoundError(c, err, response.AbortInternal)
return
}
c.JSON(http.StatusOK, response.OKNil())
}
// TestPushChannel 测试通道连通性
// @Summary 测试通道连通性
// @Description 触发一次临时的或现有的通道连通性推送测试,需要管理员权限
// @Tags admin-push
// @Accept json
// @Produce json
// @Security SessionCookie
// @Param request body do.TestPushChannelRequest true "测试参数"
// @Success 200 {object} response.Any "测试触发成功"
// @Router /api/v1/admin/push/channels/test [post]
func TestPushChannel(c *gin.Context) {
var req do.TestPushChannelRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.AbortBadRequest(c, err.Error())
return
}
payload, err := service.PreparePushChannelTest(c.Request.Context(), req)
if err != nil {
response.AbortBadRequest(c, err.Error())
return
}
if err := service.EnqueuePushTask(c.Request.Context(), payload); err != nil {
response.AbortInternal(c, err.Error())
return
}
c.JSON(http.StatusOK, response.OKNil())
}
@@ -0,0 +1,218 @@
// Copyright 2026 Arctel.net
// SPDX-License-Identifier: Apache-2.0
package controller
import (
"Wavelet/pkg/response"
"Wavelet/plugins/domain/msg_gateway/consts"
"Wavelet/plugins/domain/msg_gateway/model/do"
"Wavelet/plugins/domain/msg_gateway/service"
"errors"
"net/http"
"strconv"
"github.com/gin-gonic/gin"
)
// ListPushEvents 获取通知事件列表
// @Summary 获取所有通知事件
// @Description 返回系统配置的通知事件列表,包括预置和自定义事件,需要管理员权限
// @Tags admin-push
// @Produce json
// @Security SessionCookie
// @Success 200 {object} response.Any{data=[]entity.PushEvent} "通知事件列表"
// @Router /api/v1/admin/push/events [get]
func ListPushEvents(c *gin.Context) {
ctx := c.Request.Context()
events, err := service.ListPushEvents(ctx)
if err != nil {
response.AbortInternal(c, err.Error())
return
}
c.JSON(http.StatusOK, response.OK(events))
}
// ListBuiltInPushEvents 获取内置通知事件列表
// @Summary 获取所有内置通知事件
// @Description 返回系统定义的所有内置通知事件元数据,供前端下拉框选择,需要管理员权限
// @Tags admin-push
// @Produce json
// @Security SessionCookie
// @Success 200 {object} response.Any "内置通知事件列表"
// @Router /api/v1/admin/push/events/builtin [get]
func ListBuiltInPushEvents(c *gin.Context) {
c.JSON(http.StatusOK, response.OK(service.GetBuiltInEvents()))
}
// parsePushEventID reads the path identifier of a push event.
func parsePushEventID(c *gin.Context) (uint64, bool) {
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
if err != nil {
response.AbortBadRequest(c, consts.ErrInvalidEventID)
return 0, false
}
return id, true
}
// handlePushEventNotFoundError maps a missing event row to 404, others to fallback.
func handlePushEventNotFoundError(c *gin.Context, err error, fallback func(c *gin.Context, msg string)) {
if errors.Is(err, consts.ErrRecordNotFound) {
response.AbortNotFound(c, consts.ErrEventNotFound)
return
}
fallback(c, err.Error())
}
// CreatePushEvent 创建通知事件
// @Summary 创建通知事件
// @Description 绑定系统内置事件或异步任务、推送渠道、接收目标并创建通知事件配置,需要管理员权限
// @Tags admin-push
// @Accept json
// @Produce json
// @Security SessionCookie
// @Param request body do.CreatePushEventRequest true "创建参数"
// @Success 200 {object} response.Any{data=entity.PushEvent} "创建成功"
// @Router /api/v1/admin/push/events [post]
func CreatePushEvent(c *gin.Context) {
handleJSONRequest(c, service.CreatePushEvent)
}
// DeletePushEvent 删除通知事件配置
// @Summary 删除通知事件配置
// @Description 删除数据库中的特定通知事件配置,需要管理员权限
// @Tags admin-push
// @Produce json
// @Security SessionCookie
// @Param id path int true "事件 ID"
// @Success 200 {object} response.Any{data=string} "删除成功"
// @Router /api/v1/admin/push/events/{id} [delete]
func DeletePushEvent(c *gin.Context) {
id, ok := parsePushEventID(c)
if !ok {
return
}
if err := service.DeletePushEvent(c.Request.Context(), id); err != nil {
handlePushEventNotFoundError(c, err, response.AbortInternal)
return
}
c.JSON(http.StatusOK, response.OKNil())
}
// UpdatePushEvent 更新通知事件
// @Summary 更新通知事件
// @Description 更新已有通知事件的推送渠道、接收目标和内容模板,需要管理员权限
// @Tags admin-push
// @Accept json
// @Produce json
// @Security SessionCookie
// @Param id path int true "事件 ID"
// @Param request body do.UpdatePushEventRequest true "更新参数"
// @Success 200 {object} response.Any{data=string} "修改成功"
// @Router /api/v1/admin/push/events/{id} [put]
func UpdatePushEvent(c *gin.Context) {
id, ok := parsePushEventID(c)
if !ok {
return
}
var req do.UpdatePushEventRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.AbortBadRequest(c, err.Error())
return
}
if err := service.UpdatePushEvent(c.Request.Context(), id, req); err != nil {
handlePushEventNotFoundError(c, err, response.AbortBadRequest)
return
}
c.JSON(http.StatusOK, response.OKNil())
}
// TogglePushEvent 快捷切换通知事件启用状态
// @Summary 快捷切换通知事件启用状态
// @Description 启用或禁用指定的通知事件
// @Tags admin-push
// @Produce json
// @Security SessionCookie
// @Param id path int true "事件 ID"
// @Success 200 {object} response.Any{data=string} "切换成功"
// @Router /api/v1/admin/push/events/{id}/toggle [post]
func TogglePushEvent(c *gin.Context) {
id, ok := parsePushEventID(c)
if !ok {
return
}
enabled, err := service.TogglePushEvent(c.Request.Context(), id)
if err != nil {
handlePushEventNotFoundError(c, err, response.AbortBadRequest)
return
}
c.JSON(http.StatusOK, response.OK(enabled))
}
// ListPushHistories 分页获取通知推送历史
// @Summary 分页获取通知推送历史
// @Description 返回分页的通知历史日志数据,需要管理员权限
// @Tags admin-push
// @Produce json
// @Security SessionCookie
// @Param page query int false "当前页码"
// @Param page_size query int false "分页大小"
// @Param event_key query string false "过滤事件名称"
// @Param status query string false "过滤发送状态"
// @Success 200 {object} response.Any "推送历史列表"
// @Router /api/v1/admin/push/histories [get]
func ListPushHistories(c *gin.Context) {
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
if page < 1 {
page = 1
}
if pageSize < 1 {
pageSize = 20
}
total, results, err := service.ListPushHistories(c.Request.Context(), do.PushHistoryListFilter{
EventKey: c.Query("event_key"),
Channel: c.Query("channel"),
Status: c.Query("status"),
Page: page,
PageSize: pageSize,
})
if err != nil {
response.AbortInternal(c, err.Error())
return
}
c.JSON(http.StatusOK, response.OK(map[string]any{
"total": total,
"results": results,
}))
}
// TestPush 测试推送通道发送
// @Summary 测试推送通道发送
// @Description 接收临时通知渠道配置并在本地同步调用 Pusher.Send 发送测试消息
// @Tags admin-push
// @Accept json
// @Produce json
// @Security SessionCookie
// @Param request body do.TestPushRequest true "测试请求体"
// @Success 200 {object} response.Any{data=string} "测试成功"
// @Router /api/v1/admin/push/test [post]
func TestPush(c *gin.Context) {
var req do.TestPushRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.AbortBadRequest(c, err.Error())
return
}
if err := service.RunPushTest(c.Request.Context(), req.Config, req.Target); err != nil {
response.AbortBadRequest(c, err.Error())
return
}
c.JSON(http.StatusOK, response.OKNil())
}
@@ -0,0 +1,64 @@
// Copyright 2026 Arctel.net
// SPDX-License-Identifier: Apache-2.0
// Package controller provides HTTP endpoints for msg_gateway.
package controller
import (
"Wavelet/core/extpoints"
"github.com/gin-gonic/gin"
)
// RegisterUserRoutes mounts user-facing message gateway endpoints.
func RegisterUserRoutes(r extpoints.RouterExtension, loginMW gin.HandlerFunc) {
mg := r.Group("/message-gateway", loginMW)
{
mg.GET("/channels", ListChannels)
mg.GET("/bindings", ListBindings)
mg.POST("/bindings", BindBinding)
mg.DELETE("/bindings/:id", UnbindBinding)
}
}
// RegisterAdminRoutes mounts admin message-gateway APIs under /admin.
func RegisterAdminRoutes(adminRouter extpoints.RouterExtension, loginMW, adminMW gin.HandlerFunc) {
g := adminRouter.Group("/message-gateway", loginMW, adminMW)
{
g.GET("/channels/definitions", ListAdminChannelDefinitions)
g.GET("/channels", ListAdminChannels)
g.POST("/channels", CreateAdminChannel)
g.PATCH("/channels/:id", UpdateAdminChannel)
g.DELETE("/channels/:id", DeleteAdminChannel)
g.POST("/channels/:id/test", TestAdminChannel)
}
}
// RegisterAdminPushRoutes mounts admin push notification APIs under /admin.
func RegisterAdminPushRoutes(adminRouter extpoints.RouterExtension, loginMW, adminMW gin.HandlerFunc) {
adminPushGroup := adminRouter.Group("/push", loginMW, adminMW)
{
events := adminPushGroup.Group("/events")
{
events.GET("", ListPushEvents)
events.GET("/builtin", ListBuiltInPushEvents)
events.POST("", CreatePushEvent)
events.PUT("/:id", UpdatePushEvent)
events.DELETE("/:id", DeletePushEvent)
events.POST("/:id/toggle", TogglePushEvent)
}
adminPushGroup.GET("/histories", ListPushHistories)
adminPushGroup.POST("/test", TestPush)
channels := adminPushGroup.Group("/channels")
{
channels.GET("/definitions", ListPushChannelDefinitions)
channels.GET("", ListPushChannels)
channels.POST("", CreatePushChannel)
channels.PUT("/:id", UpdatePushChannel)
channels.DELETE("/:id", DeletePushChannel)
channels.POST("/test", TestPushChannel)
}
}
}
@@ -0,0 +1,181 @@
// Copyright 2026 Arctel.net
// SPDX-License-Identifier: Apache-2.0
package controller
import (
"Wavelet/core/contracts"
"Wavelet/pkg/ginutil"
"Wavelet/pkg/response"
"Wavelet/plugins/domain/msg_gateway/consts"
"Wavelet/plugins/domain/msg_gateway/model/do"
"Wavelet/plugins/domain/msg_gateway/service"
"context"
"errors"
"net/http"
"strconv"
"github.com/gin-gonic/gin"
)
func currentUser(c *gin.Context) (*contracts.UserDTO, bool) {
return ginutil.GetFromContext[*contracts.UserDTO](c, contracts.AuthUserObjKey)
}
// handleJSONRequest binds a JSON body, runs the service use case and writes the
// standard success envelope; any service error surfaces as a bad request.
func handleJSONRequest[Req any, Res any](c *gin.Context, handler func(ctx context.Context, req Req) (Res, error)) {
var req Req
if err := c.ShouldBindJSON(&req); err != nil {
response.AbortBadRequest(c, err.Error())
return
}
res, err := handler(c.Request.Context(), req)
if err != nil {
response.AbortBadRequest(c, err.Error())
return
}
c.JSON(http.StatusOK, response.OK(res))
}
// handleEntityUpdate resolves a path identifier plus JSON body, runs the updater
// use case and writes the success envelope; error classification is delegated to onErr.
func handleEntityUpdate[Req any, Res any](
c *gin.Context,
parseID func(*gin.Context) (uint64, bool),
updater func(ctx context.Context, id uint64, req Req) (Res, error),
onErr func(*gin.Context, error),
) {
id, ok := parseID(c)
if !ok {
return
}
var req Req
if err := c.ShouldBindJSON(&req); err != nil {
response.AbortBadRequest(c, err.Error())
return
}
dto, err := updater(c.Request.Context(), id, req)
if err != nil {
onErr(c, err)
return
}
c.JSON(http.StatusOK, response.OK(dto))
}
// ListChannels lists enabled channels a user can bind.
// @Summary List enabled messaging channels
// @Description Returns enabled system bots the current user can pair with
// @Tags message-gateway
// @Produce json
// @Security SessionCookie
// @Success 200 {object} response.Any{data=[]do.PublicChannelDTO}
// @Failure 401 {object} response.Any
// @Router /api/v1/message-gateway/channels [get]
func ListChannels(c *gin.Context) {
if user, ok := currentUser(c); !ok || user == nil {
response.AbortUnauthorized(c, consts.ErrLoginRequired)
return
}
rows, err := service.ListEnabledPublicChannels(c.Request.Context())
if err != nil {
response.AbortInternal(c, err.Error())
return
}
c.JSON(http.StatusOK, response.OK(rows))
}
// ListBindings lists the current user's bot bindings.
// @Summary List message gateway bindings
// @Description Returns the current user's bound messaging channels
// @Tags message-gateway
// @Produce json
// @Security SessionCookie
// @Success 200 {object} response.Any{data=[]do.BindingDTO}
// @Failure 401 {object} response.Any
// @Router /api/v1/message-gateway/bindings [get]
func ListBindings(c *gin.Context) {
user, ok := currentUser(c)
if !ok || user == nil {
response.AbortUnauthorized(c, consts.ErrLoginRequired)
return
}
rows, err := service.ListUserBindings(c.Request.Context(), user.ID)
if err != nil {
response.AbortInternal(c, err.Error())
return
}
c.JSON(http.StatusOK, response.OK(rows))
}
// BindBinding consumes a pairing code and binds the platform identity.
// @Summary Bind a messaging channel
// @Description Binds the current user to a platform identity using a one-time pairing code
// @Tags message-gateway
// @Accept json
// @Produce json
// @Security SessionCookie
// @Param request body do.BindRequest true "bind body"
// @Success 200 {object} response.Any{data=do.BindingDTO}
// @Failure 400 {object} response.Any
// @Failure 409 {object} response.Any
// @Router /api/v1/message-gateway/bindings [post]
func BindBinding(c *gin.Context) {
user, ok := currentUser(c)
if !ok || user == nil {
response.AbortUnauthorized(c, consts.ErrLoginRequired)
return
}
var req do.BindRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.AbortBadRequest(c, err.Error())
return
}
dto, err := service.BindChannel(c.Request.Context(), user.ID, req)
if err != nil {
if errors.Is(err, consts.ErrPlatformAlreadyBound) {
response.AbortConflict(c, err.Error())
return
}
response.AbortBadRequest(c, err.Error())
return
}
c.JSON(http.StatusOK, response.OK(dto))
}
// UnbindBinding removes the current user's binding.
// @Summary Unbind a messaging channel
// @Description Removes a binding owned by the current user
// @Tags message-gateway
// @Produce json
// @Security SessionCookie
// @Param id path int true "binding id"
// @Success 200 {object} response.Any
// @Failure 403 {object} response.Any
// @Failure 404 {object} response.Any
// @Router /api/v1/message-gateway/bindings/{id} [delete]
func UnbindBinding(c *gin.Context) {
user, ok := currentUser(c)
if !ok || user == nil {
response.AbortUnauthorized(c, consts.ErrLoginRequired)
return
}
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
if err != nil {
response.AbortBadRequest(c, consts.ErrInvalidBindingID)
return
}
if err := service.UnbindChannel(c.Request.Context(), user.ID, id); err != nil {
if errors.Is(err, consts.ErrBindingNotFound) {
response.AbortNotFound(c, err.Error())
return
}
if errors.Is(err, consts.ErrBindingForbidden) {
response.AbortForbidden(c, err.Error())
return
}
response.AbortInternal(c, err.Error())
return
}
c.JSON(http.StatusOK, response.OKNil())
}