kra-new/internal/data/websocket_config.go

97 lines
3.2 KiB
Go

package data
import (
"context"
"encoding/json"
"errors"
"fmt"
"kra/internal/conf"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/proto"
"gorm.io/gorm"
)
const integrationKindWebSocket = "websocket"
// saveWebSocketIntegrationConfig persists the Melody settings in the shared
// integration table. It intentionally lives separately from storage/payment
// persistence so adding another transport does not expand their API surface.
func saveWebSocketIntegrationConfig(db *gorm.DB, config *conf.AdminBackend_WebSocket) error {
if config == nil {
config = &conf.AdminBackend_WebSocket{}
}
raw, err := protojson.MarshalOptions{UseProtoNames: true, EmitDefaultValues: true}.Marshal(config)
if err != nil {
return fmt.Errorf("encode websocket integration configuration: %w", err)
}
clean := db.Session(&gorm.Session{NewDB: true})
var current integrationConfigPO
err = clean.Where("kind = ? AND provider = ?", integrationKindWebSocket, "melody").First(&current).Error
switch {
case errors.Is(err, gorm.ErrRecordNotFound):
return clean.Create(&integrationConfigPO{Kind: integrationKindWebSocket, Provider: "melody", Enabled: config.Enabled, Config: string(raw)}).Error
case err != nil:
return err
default:
return clean.Model(&current).Updates(map[string]any{"enabled": config.Enabled, "config": string(raw)}).Error
}
}
func loadWebSocketIntegrationConfig(db *gorm.DB) (*conf.AdminBackend_WebSocket, bool, error) {
var row integrationConfigPO
err := db.Session(&gorm.Session{NewDB: true}).Where("kind = ? AND provider = ?", integrationKindWebSocket, "melody").First(&row).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, false, nil
}
if err != nil {
return nil, false, err
}
if !json.Valid([]byte(row.Config)) {
return nil, false, errors.New("invalid websocket integration configuration")
}
config := &conf.AdminBackend_WebSocket{}
if err = (protojson.UnmarshalOptions{DiscardUnknown: true}).Unmarshal([]byte(row.Config), config); err != nil {
return nil, false, fmt.Errorf("decode websocket integration configuration: %w", err)
}
config.Enabled = row.Enabled
return config, true, nil
}
func resolveWebSocketIntegrationConfig(db *gorm.DB, legacy *conf.AdminBackend_WebSocket) (*conf.AdminBackend_WebSocket, error) {
clean := db.Session(&gorm.Session{NewDB: true})
if !clean.Migrator().HasTable(&integrationConfigPO{}) {
if legacy == nil {
return &conf.AdminBackend_WebSocket{}, nil
}
return proto.Clone(legacy).(*conf.AdminBackend_WebSocket), nil
}
config, found, err := loadWebSocketIntegrationConfig(clean)
if err != nil {
return nil, err
}
if found {
return config, nil
}
if legacy == nil {
legacy = &conf.AdminBackend_WebSocket{}
}
if err = saveWebSocketIntegrationConfig(clean, legacy); err != nil {
return nil, err
}
config, _, err = loadWebSocketIntegrationConfig(clean)
return config, err
}
func (d *Data) PersistWebSocketConfig(ctx context.Context, config *conf.AdminBackend_WebSocket) error {
if !d.databaseReady.Load() {
return errors.New("database is not initialized")
}
db := d.gormDB.WithContext(ctx)
if !db.Migrator().HasTable(&integrationConfigPO{}) {
return errors.New("integration configuration table does not exist")
}
return saveWebSocketIntegrationConfig(db, config)
}