kra-new/internal/data/integration/storage_email.go

220 lines
5.5 KiB
Go

package integration
import (
"encoding/json"
"errors"
"fmt"
"strings"
"kra/internal/config"
"gorm.io/gorm"
)
const (
kindStorage = "storage"
kindEmail = "email"
providerSMTP = "smtp"
)
var storageProviders = []string{
"local", "qiniu", "aliyun-oss", "huawei-obs", "tencent-cos", "aws-s3", "cloudflare-r2", "minio",
}
func normalizeStorageType(value string) string {
value = strings.ToLower(strings.TrimSpace(value))
if value == "" {
return "local"
}
return value
}
func storageProviderValue(storage *config.Storage, provider string) any {
if storage == nil {
storage = &config.Storage{}
}
switch provider {
case "qiniu":
if storage.Qiniu == nil {
storage.Qiniu = &config.Qiniu{}
}
return storage.Qiniu
case "aliyun-oss":
return ensureObjectStore(&storage.AliyunOSS)
case "huawei-obs":
return ensureObjectStore(&storage.HuaweiOBS)
case "tencent-cos":
return ensureObjectStore(&storage.TencentCOS)
case "aws-s3":
return ensureObjectStore(&storage.AWSS3)
case "cloudflare-r2":
return ensureObjectStore(&storage.CloudflareR2)
case "minio":
return ensureObjectStore(&storage.Minio)
default:
return nil
}
}
func ensureObjectStore(value **config.ObjectStore) *config.ObjectStore {
if *value == nil {
*value = &config.ObjectStore{}
}
return *value
}
func marshalStorageProvider(storage *config.Storage, provider string) (string, error) {
value := storageProviderValue(storage, provider)
if value == nil {
return "{}", nil
}
raw, err := json.Marshal(value)
if err != nil {
return "", err
}
return string(raw), nil
}
func unmarshalStorageProvider(storage *config.Storage, provider, value string) error {
if provider == "local" || strings.TrimSpace(value) == "" {
return nil
}
target := storageProviderValue(storage, provider)
if target == nil {
return nil
}
if !json.Valid([]byte(value)) {
return fmt.Errorf("invalid %s integration configuration", provider)
}
if err := json.Unmarshal([]byte(value), target); err != nil {
return fmt.Errorf("decode %s integration configuration: %w", provider, err)
}
return nil
}
func SaveStorageConfig(db *gorm.DB, storage *config.Storage) error {
if storage == nil {
storage = &config.Storage{}
}
active := normalizeStorageType(storage.Type)
known := false
for _, provider := range storageProviders {
if provider == active {
known = true
break
}
}
if !known {
return fmt.Errorf("unsupported storage type %q", active)
}
rows := make([]ConfigRow, 0, len(storageProviders))
for _, provider := range storageProviders {
value, err := marshalStorageProvider(storage, provider)
if err != nil {
return fmt.Errorf("encode %s integration configuration: %w", provider, err)
}
rows = append(rows, ConfigRow{Provider: provider, Enabled: provider == active, Config: value})
}
return UpsertConfigs(db, kindStorage, rows)
}
func LoadStorageConfig(db *gorm.DB) (*config.Storage, bool, error) {
rows, err := ListConfigs(db, kindStorage)
if err != nil {
return nil, false, err
}
if len(rows) == 0 {
return nil, false, nil
}
storage := &config.Storage{Type: "local"}
for _, row := range rows {
if err = unmarshalStorageProvider(storage, row.Provider, row.Config); err != nil {
return nil, false, err
}
if row.Enabled {
storage.Type = row.Provider
}
}
return storage, true, nil
}
// ResolveStorageConfig imports a legacy YAML value only when the database has
// no storage configuration yet. The database is authoritative afterwards.
func ResolveStorageConfig(db *gorm.DB, legacy *config.Storage) (*config.Storage, error) {
if !HasConfigTable(db) {
if legacy == nil {
return &config.Storage{Type: "local"}, nil
}
return config.CloneStorage(legacy), nil
}
storage, found, err := LoadStorageConfig(db)
if err != nil {
return nil, err
}
if found {
return storage, nil
}
if legacy == nil {
legacy = &config.Storage{Type: "local"}
}
if err = SaveStorageConfig(db, legacy); err != nil {
return nil, err
}
storage, _, err = LoadStorageConfig(db)
return storage, err
}
func defaultEmailConfig() *config.Email { return &config.Email{Port: 465, IsSSL: true} }
func SaveEmailConfig(db *gorm.DB, email *config.Email) error {
if email == nil {
email = defaultEmailConfig()
}
raw, err := json.Marshal(email)
if err != nil {
return fmt.Errorf("encode smtp integration configuration: %w", err)
}
enabled := email.Host != "" && email.From != "" && email.Secret != "" && email.Port > 0
return UpsertConfigs(db, kindEmail, []ConfigRow{{Provider: providerSMTP, Enabled: enabled, Config: string(raw)}})
}
func LoadEmailConfig(db *gorm.DB) (*config.Email, bool, error) {
row, found, err := FindConfig(db, kindEmail, providerSMTP)
if err != nil || !found {
return nil, false, err
}
if !json.Valid([]byte(row.Config)) {
return nil, false, errors.New("invalid smtp integration configuration")
}
email := defaultEmailConfig()
if err = json.Unmarshal([]byte(row.Config), email); err != nil {
return nil, false, fmt.Errorf("decode smtp integration configuration: %w", err)
}
return email, true, nil
}
func ResolveEmailConfig(db *gorm.DB, legacy *config.Email) (*config.Email, error) {
if !HasConfigTable(db) {
if legacy == nil {
return defaultEmailConfig(), nil
}
return config.CloneEmail(legacy), nil
}
email, found, err := LoadEmailConfig(db)
if err != nil {
return nil, err
}
if found {
return email, nil
}
if legacy == nil {
legacy = defaultEmailConfig()
}
if err = SaveEmailConfig(db, legacy); err != nil {
return nil, err
}
email, _, err = LoadEmailConfig(db)
return email, err
}