mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-07 16:16:37 +08:00
refactor(core): decouple gin from pkg/util and reduce code duplication
This commit is contained in:
@@ -40,6 +40,23 @@ func ListAdminChannels(c *gin.Context) {
|
||||
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, "invalid channel id")
|
||||
return 0, false
|
||||
}
|
||||
return id, true
|
||||
}
|
||||
|
||||
func handleAdminChannelError(c *gin.Context, err error, fallback func(c *gin.Context, msg string)) {
|
||||
if err.Error() == 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
|
||||
@@ -52,17 +69,7 @@ func ListAdminChannels(c *gin.Context) {
|
||||
// @Failure 400 {object} response.Any
|
||||
// @Router /api/v1/admin/message-gateway/channels [post]
|
||||
func CreateAdminChannel(c *gin.Context) {
|
||||
var req CreateChannelRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.AbortBadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
dto, err := createChannel(c.Request.Context(), req)
|
||||
if err != nil {
|
||||
response.AbortBadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, response.OK(dto))
|
||||
handleJSONRequest(c, createChannel)
|
||||
}
|
||||
|
||||
// UpdateAdminChannel patches a messaging channel. Empty secrets keep the previous values.
|
||||
@@ -79,26 +86,9 @@ func CreateAdminChannel(c *gin.Context) {
|
||||
// @Failure 404 {object} response.Any
|
||||
// @Router /api/v1/admin/message-gateway/channels/{id} [patch]
|
||||
func UpdateAdminChannel(c *gin.Context) {
|
||||
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if err != nil {
|
||||
response.AbortBadRequest(c, "invalid channel id")
|
||||
return
|
||||
}
|
||||
var req UpdateChannelRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.AbortBadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
dto, err := updateChannel(c.Request.Context(), id, req)
|
||||
if err != nil {
|
||||
if err.Error() == errChannelNotFound {
|
||||
response.AbortNotFound(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.AbortBadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, response.OK(dto))
|
||||
handleEntityUpdate(c, parseAdminChannelID, updateChannel, func(c *gin.Context, err error) {
|
||||
handleAdminChannelError(c, err, response.AbortBadRequest)
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteAdminChannel removes a channel and its bindings/pairing codes.
|
||||
@@ -112,17 +102,12 @@ func UpdateAdminChannel(c *gin.Context) {
|
||||
// @Failure 404 {object} response.Any
|
||||
// @Router /api/v1/admin/message-gateway/channels/{id} [delete]
|
||||
func DeleteAdminChannel(c *gin.Context) {
|
||||
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if err != nil {
|
||||
response.AbortBadRequest(c, "invalid channel id")
|
||||
id, ok := parseAdminChannelID(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if err := deleteChannel(c.Request.Context(), id); err != nil {
|
||||
if err.Error() == errChannelNotFound {
|
||||
response.AbortNotFound(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.AbortInternal(c, err.Error())
|
||||
handleAdminChannelError(c, err, response.AbortInternal)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, response.OKNil())
|
||||
@@ -140,17 +125,12 @@ func DeleteAdminChannel(c *gin.Context) {
|
||||
// @Failure 404 {object} response.Any
|
||||
// @Router /api/v1/admin/message-gateway/channels/{id}/test [post]
|
||||
func TestAdminChannel(c *gin.Context) {
|
||||
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if err != nil {
|
||||
response.AbortBadRequest(c, "invalid channel id")
|
||||
id, ok := parseAdminChannelID(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if err := probeChannel(c.Request.Context(), id); err != nil {
|
||||
if err.Error() == errChannelNotFound {
|
||||
response.AbortNotFound(c, err.Error())
|
||||
return
|
||||
}
|
||||
response.AbortBadRequest(c, err.Error())
|
||||
handleAdminChannelError(c, err, response.AbortBadRequest)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, response.OKNil())
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package message_gateway
|
||||
|
||||
import (
|
||||
"Wavelet/pkg/response"
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
@@ -5,8 +5,8 @@ package message_gateway
|
||||
|
||||
import (
|
||||
"Wavelet/core/contracts"
|
||||
"Wavelet/pkg/ginutil"
|
||||
"Wavelet/pkg/response"
|
||||
"Wavelet/pkg/util"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
)
|
||||
|
||||
func currentUser(c *gin.Context) (*contracts.UserDTO, bool) {
|
||||
return util.GetFromContext[*contracts.UserDTO](c, contracts.AuthUserObjKey)
|
||||
return ginutil.GetFromContext[*contracts.UserDTO](c, contracts.AuthUserObjKey)
|
||||
}
|
||||
|
||||
// ListChannels lists enabled channels a user can bind.
|
||||
|
||||
@@ -214,20 +214,26 @@ type CreatePushChannelRequest struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
func parsePushChannelID(c *gin.Context) (uint64, bool) {
|
||||
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if err != nil {
|
||||
response.AbortBadRequest(c, "invalid channel id")
|
||||
return 0, false
|
||||
}
|
||||
return id, true
|
||||
}
|
||||
|
||||
func handlePushChannelNotFoundError(c *gin.Context, err error, fallback func(c *gin.Context, msg string)) {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
response.AbortNotFound(c, "channel not found")
|
||||
return
|
||||
}
|
||||
fallback(c, err.Error())
|
||||
}
|
||||
|
||||
// CreatePushChannel creates a push channel.
|
||||
func CreatePushChannel(c *gin.Context) {
|
||||
var req CreatePushChannelRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.AbortBadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
channel, err := createPushChannel(c.Request.Context(), req)
|
||||
if err != nil {
|
||||
response.AbortBadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, response.OK(channel))
|
||||
handleJSONRequest(c, createPushChannel)
|
||||
}
|
||||
|
||||
// UpdatePushChannelRequest is the update channel request payload.
|
||||
@@ -242,44 +248,20 @@ type UpdatePushChannelRequest struct {
|
||||
|
||||
// UpdatePushChannel updates a push channel.
|
||||
func UpdatePushChannel(c *gin.Context) {
|
||||
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if err != nil {
|
||||
response.AbortBadRequest(c, "invalid channel id")
|
||||
return
|
||||
}
|
||||
|
||||
var req UpdatePushChannelRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.AbortBadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
channel, err := updatePushChannel(c.Request.Context(), id, req)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
response.AbortNotFound(c, "channel not found")
|
||||
return
|
||||
}
|
||||
response.AbortInternal(c, err.Error())
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, response.OK(channel))
|
||||
handleEntityUpdate(c, parsePushChannelID, updatePushChannel, func(c *gin.Context, err error) {
|
||||
handlePushChannelNotFoundError(c, err, response.AbortInternal)
|
||||
})
|
||||
}
|
||||
|
||||
// DeletePushChannel deletes a push channel.
|
||||
func DeletePushChannel(c *gin.Context) {
|
||||
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if err != nil {
|
||||
response.AbortBadRequest(c, "invalid channel id")
|
||||
id, ok := parsePushChannelID(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if err := deletePushChannel(c.Request.Context(), id); err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
response.AbortNotFound(c, "channel not found")
|
||||
return
|
||||
}
|
||||
response.AbortInternal(c, err.Error())
|
||||
handlePushChannelNotFoundError(c, err, response.AbortInternal)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, response.OKNil())
|
||||
|
||||
@@ -56,36 +56,37 @@ func ListBuiltInPushEvents(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, response.OK(GetBuiltInEvents()))
|
||||
}
|
||||
|
||||
func parsePushEventID(c *gin.Context) (uint64, bool) {
|
||||
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if err != nil {
|
||||
response.AbortBadRequest(c, "invalid event id")
|
||||
return 0, false
|
||||
}
|
||||
return id, true
|
||||
}
|
||||
|
||||
func handlePushEventNotFoundError(c *gin.Context, err error, fallback func(c *gin.Context, msg string)) {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
response.AbortNotFound(c, "notification event not found")
|
||||
return
|
||||
}
|
||||
fallback(c, err.Error())
|
||||
}
|
||||
|
||||
// CreatePushEvent creates a new push event configuration.
|
||||
func CreatePushEvent(c *gin.Context) {
|
||||
var req CreatePushEventRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.AbortBadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
event, err := createPushEvent(c.Request.Context(), req)
|
||||
if err != nil {
|
||||
response.AbortBadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, response.OK(event))
|
||||
handleJSONRequest(c, createPushEvent)
|
||||
}
|
||||
|
||||
// DeletePushEvent deletes a push event configuration by ID.
|
||||
func DeletePushEvent(c *gin.Context) {
|
||||
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if err != nil {
|
||||
response.AbortBadRequest(c, "invalid event id")
|
||||
id, ok := parsePushEventID(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if err := deletePushEvent(c.Request.Context(), id); err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
response.AbortNotFound(c, "notification event not found")
|
||||
return
|
||||
}
|
||||
response.AbortInternal(c, err.Error())
|
||||
handlePushEventNotFoundError(c, err, response.AbortInternal)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, response.OKNil())
|
||||
@@ -93,9 +94,8 @@ func DeletePushEvent(c *gin.Context) {
|
||||
|
||||
// UpdatePushEvent updates an existing push event.
|
||||
func UpdatePushEvent(c *gin.Context) {
|
||||
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if err != nil {
|
||||
response.AbortBadRequest(c, "invalid event id")
|
||||
id, ok := parsePushEventID(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -106,11 +106,7 @@ func UpdatePushEvent(c *gin.Context) {
|
||||
}
|
||||
|
||||
if err := updatePushEvent(c.Request.Context(), id, req); err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
response.AbortNotFound(c, "notification event not found")
|
||||
return
|
||||
}
|
||||
response.AbortBadRequest(c, err.Error())
|
||||
handlePushEventNotFoundError(c, err, response.AbortBadRequest)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, response.OKNil())
|
||||
@@ -118,19 +114,14 @@ func UpdatePushEvent(c *gin.Context) {
|
||||
|
||||
// TogglePushEvent toggles the enabled state of a push event.
|
||||
func TogglePushEvent(c *gin.Context) {
|
||||
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if err != nil {
|
||||
response.AbortBadRequest(c, "invalid event id")
|
||||
id, ok := parsePushEventID(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
enabled, err := togglePushEvent(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
response.AbortNotFound(c, "notification event not found")
|
||||
return
|
||||
}
|
||||
response.AbortBadRequest(c, err.Error())
|
||||
handlePushEventNotFoundError(c, err, response.AbortBadRequest)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, response.OK(enabled))
|
||||
|
||||
@@ -217,25 +217,31 @@ func DeletePushChannelRecord(ctx context.Context, channel *PushChannel) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetActivePushChannelByName 根据名称获取启用的消息通道 (优先从 Redis 缓存获取)。
|
||||
func GetActivePushChannelByName(ctx context.Context, name string) (*PushChannel, error) {
|
||||
cacheKey := "push:channel:active:" + name
|
||||
var channel PushChannel
|
||||
func getCachedOrQuery[T any](ctx context.Context, cacheKey string, ttl time.Duration, query func(db *gorm.DB, dest *T) error) (*T, error) {
|
||||
var val T
|
||||
if cache := getCache(ctx); cache != nil {
|
||||
if err := cache.Get(ctx, cacheKey, &channel); err == nil {
|
||||
return &channel, nil
|
||||
if err := cache.Get(ctx, cacheKey, &val); err == nil {
|
||||
return &val, nil
|
||||
}
|
||||
}
|
||||
|
||||
if err := getDB(ctx).Where("name = ? AND enabled = ?", name, true).First(&channel).Error; err != nil {
|
||||
db := getDB(ctx)
|
||||
if err := query(db, &val); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if cache := getCache(ctx); cache != nil {
|
||||
_ = cache.Set(ctx, cacheKey, channel, activePushChannelCacheTTL)
|
||||
_ = cache.Set(ctx, cacheKey, val, ttl)
|
||||
}
|
||||
|
||||
return &channel, nil
|
||||
return &val, nil
|
||||
}
|
||||
|
||||
// GetActivePushChannelByName 根据名称获取启用的消息通道 (优先从 Redis 缓存获取)。
|
||||
func GetActivePushChannelByName(ctx context.Context, name string) (*PushChannel, error) {
|
||||
return getCachedOrQuery(ctx, "push:channel:active:"+name, activePushChannelCacheTTL, func(db *gorm.DB, dest *PushChannel) error {
|
||||
return db.Where("name = ? AND enabled = ?", name, true).First(dest).Error
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteActivePushChannelCache 清理启用消息通道的缓存。
|
||||
@@ -329,23 +335,9 @@ func ListActivePushEventsByTaskTypeRecord(ctx context.Context, taskType string)
|
||||
|
||||
// GetActivePushEventByKey 获取启用的通知事件 (优先从 Redis 缓存获取)。
|
||||
func GetActivePushEventByKey(ctx context.Context, key string) (*PushEvent, error) {
|
||||
cacheKey := "push:event:active:" + key
|
||||
var event PushEvent
|
||||
if cache := getCache(ctx); cache != nil {
|
||||
if err := cache.Get(ctx, cacheKey, &event); err == nil {
|
||||
return &event, nil
|
||||
}
|
||||
}
|
||||
|
||||
if err := getDB(ctx).Where("event_key = ? AND enabled = ?", key, true).First(&event).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if cache := getCache(ctx); cache != nil {
|
||||
_ = cache.Set(ctx, cacheKey, event, activePushEventCacheTTL)
|
||||
}
|
||||
|
||||
return &event, nil
|
||||
return getCachedOrQuery(ctx, "push:event:active:"+key, activePushEventCacheTTL, func(db *gorm.DB, dest *PushEvent) error {
|
||||
return db.Where("event_key = ? AND enabled = ?", key, true).First(dest).Error
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteActivePushEventCache 清理启用通知事件的缓存。
|
||||
|
||||
Reference in New Issue
Block a user