mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-09-29 22:06:38 +08:00
159 lines
4.2 KiB
Go
159 lines
4.2 KiB
Go
// Copyright 2026 Arctel.net
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package message_gateway
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/Rain-kl/Wavelet/internal/model"
|
|
"github.com/Rain-kl/Wavelet/internal/repository"
|
|
pkgmg "github.com/Rain-kl/Wavelet/pkg/message_gateway"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// BindRequest is the user bind body.
|
|
type BindRequest struct {
|
|
ChannelID string `json:"channel_id"`
|
|
Code string `json:"code"`
|
|
}
|
|
|
|
// BindingDTO is a user-facing binding row.
|
|
type BindingDTO struct {
|
|
ID uint64 `json:"id,string"`
|
|
UserID uint64 `json:"user_id,string"`
|
|
ChannelID uint64 `json:"channel_id,string"`
|
|
ChannelName string `json:"channel_name"`
|
|
ChannelType string `json:"channel_type"`
|
|
PlatformUserID string `json:"platform_user_id"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
}
|
|
|
|
func bindChannel(ctx context.Context, userID uint64, req BindRequest) (BindingDTO, error) {
|
|
channelID, err := strconv.ParseUint(strings.TrimSpace(req.ChannelID), 10, 64)
|
|
if err != nil || channelID == 0 {
|
|
return BindingDTO{}, errChannelIDRequired
|
|
}
|
|
code := pkgmg.NormalizeCode(req.Code)
|
|
if code == "" {
|
|
return BindingDTO{}, errCodeInvalid
|
|
}
|
|
pairing, err := repository.GetPairingCode(ctx, code)
|
|
if err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return BindingDTO{}, errCodeInvalid
|
|
}
|
|
return BindingDTO{}, err
|
|
}
|
|
if !pairing.ExpiresAt.After(time.Now()) {
|
|
return BindingDTO{}, errCodeInvalid
|
|
}
|
|
if pairing.ChannelID != channelID {
|
|
return BindingDTO{}, errChannelMismatch
|
|
}
|
|
ch, err := repository.GetMessageChannel(ctx, channelID)
|
|
if err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return BindingDTO{}, errCodeInvalid
|
|
}
|
|
return BindingDTO{}, err
|
|
}
|
|
if !ch.Enabled {
|
|
return BindingDTO{}, errChannelDisabled
|
|
}
|
|
|
|
existing, err := repository.GetBindingByChannelPlatform(ctx, channelID, pairing.PlatformUserID)
|
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return BindingDTO{}, err
|
|
}
|
|
if err == nil && existing != nil {
|
|
if existing.UserID != userID {
|
|
return BindingDTO{}, errPlatformAlreadyBound
|
|
}
|
|
_ = repository.DeletePairingCode(ctx, pairing.Code)
|
|
return toBindingDTO(existing, ch), nil
|
|
}
|
|
|
|
row := &model.MessageBinding{
|
|
UserID: userID,
|
|
ChannelID: channelID,
|
|
PlatformUserID: pairing.PlatformUserID,
|
|
}
|
|
if err := repository.CreateMessageBinding(ctx, row); err != nil {
|
|
return BindingDTO{}, err
|
|
}
|
|
if err := repository.DeletePairingCode(ctx, pairing.Code); err != nil {
|
|
return BindingDTO{}, err
|
|
}
|
|
return toBindingDTO(row, ch), nil
|
|
}
|
|
|
|
// PublicChannelDTO is an enabled channel a user can bind to.
|
|
type PublicChannelDTO struct {
|
|
ID uint64 `json:"id,string"`
|
|
Name string `json:"name"`
|
|
Type string `json:"type"`
|
|
}
|
|
|
|
func listEnabledPublicChannels(ctx context.Context) ([]PublicChannelDTO, error) {
|
|
rows, err := repository.ListEnabledMessageChannels(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out := make([]PublicChannelDTO, 0, len(rows))
|
|
for _, row := range rows {
|
|
out = append(out, PublicChannelDTO{ID: row.ID, Name: row.Name, Type: row.Type})
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func listUserBindings(ctx context.Context, userID uint64) ([]BindingDTO, error) {
|
|
rows, err := repository.ListBindingsByUser(ctx, userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out := make([]BindingDTO, 0, len(rows))
|
|
for i := range rows {
|
|
ch, err := repository.GetMessageChannel(ctx, rows[i].ChannelID)
|
|
if err != nil {
|
|
out = append(out, toBindingDTO(&rows[i], nil))
|
|
continue
|
|
}
|
|
out = append(out, toBindingDTO(&rows[i], ch))
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func unbindChannel(ctx context.Context, userID, bindingID uint64) error {
|
|
row, err := repository.GetMessageBinding(ctx, bindingID)
|
|
if err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return errBindingNotFound
|
|
}
|
|
return err
|
|
}
|
|
if row.UserID != userID {
|
|
return errBindingForbidden
|
|
}
|
|
return repository.DeleteMessageBinding(ctx, bindingID)
|
|
}
|
|
|
|
func toBindingDTO(row *model.MessageBinding, ch *model.MessageChannel) BindingDTO {
|
|
dto := BindingDTO{
|
|
ID: row.ID,
|
|
UserID: row.UserID,
|
|
ChannelID: row.ChannelID,
|
|
PlatformUserID: row.PlatformUserID,
|
|
CreatedAt: row.CreatedAt,
|
|
}
|
|
if ch != nil {
|
|
dto.ChannelName = ch.Name
|
|
dto.ChannelType = ch.Type
|
|
}
|
|
return dto
|
|
}
|