Files
ttstd-infinite-canvas/backend/internal/app/resource_oss.go

150 lines
5.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 资源对应的对象存储配置解析:系统存储、用户自有存储与历史位置。
package app
import (
"errors"
"strings"
"gorm.io/gorm"
"yingce/backend/internal/model"
)
func (s *Service) activeOSSSetting() (ossSettingValue, error) {
_, setting, err := s.readOSSSetting()
if err != nil {
return ossSettingValue{}, err
}
return validateActiveOSSSetting(setting, "管理员尚未启用 OSS", "平台 OSS 配置不完整,请联系管理员")
}
func (s *Service) activeResourceOSSSetting(userID string) (ossSettingValue, string, bool, error) {
userSetting, value, err := s.readUserOSSSetting(userID)
if err != nil {
return ossSettingValue{}, "", false, err
}
_, systemValue, err := s.readOSSSetting()
if err != nil {
return ossSettingValue{}, "", false, err
}
userAllowed := value.Provider != s3Provider || systemValue.AllowUserS3
if userSetting != nil && value.Enabled && userAllowed {
value, err = validateActiveOSSSetting(value, "用户 OSS 尚未启用", "你的 OSS 配置不完整")
return value, firstNonEmpty(value.StorageLocationID, userSetting.ID), true, err
}
if !systemValue.Enabled {
return ossSettingValue{}, "", false, nil
}
systemValue, err = validateActiveOSSSetting(systemValue, "管理员尚未启用 OSS", "平台 OSS 配置不完整,请联系管理员")
return systemValue, systemValue.StorageLocationID, true, err
}
func (s *Service) ossSettingForResource(userID string, resource *model.Resource) (ossSettingValue, error) {
var setting ossSettingValue
var err error
if resource.StorageSettingID != "" {
_, setting, err = s.storageLocationValue(resource.StorageSettingID)
if errors.Is(err, gorm.ErrRecordNotFound) {
_, setting, err = s.readUserOSSSettingByID(userID, resource.StorageSettingID)
}
if err == nil {
_, current, currentErr := s.readUserOSSSetting(userID)
if currentErr != nil {
return ossSettingValue{}, currentErr
}
// 密钥固定在资源绑定的历史版本;只有存储位置完全一致时,才允许沿用当前 CDN。
if resourceStorageMatches(current, resource) {
setting.CDNBaseURL = current.CDNBaseURL
// CDN 的鉴权方式和回退策略属于分发配置,而不是资源创建时的
// 凭据。存储位置未变时沿用当前策略,避免历史资源因管理员
// 刚补齐 CDN 鉴权配置而继续回源。
setting.Delivery = current.Delivery
}
}
} else {
// 早期资源没有 StorageSettingID,但资源本身仍记录了 provider/endpoint/bucket。
// 先从用户 OSS 历史版本中按存储位置反查,不能把当前七牛配置猜给历史阿里云对象。
setting, err = s.userOSSSettingForResource(userID, resource)
if errors.Is(err, gorm.ErrRecordNotFound) {
_, setting, err = s.readOSSSetting()
}
}
if err != nil {
return ossSettingValue{}, err
}
resourceProvider := strings.ToLower(strings.TrimSpace(resource.Provider))
resourceMatchesSetting := resourceStorageMatches(setting, resource)
setting, err = ossSettingForProvider(setting, firstNonEmpty(resource.Provider, setting.Provider))
if err != nil {
return ossSettingValue{}, err
}
setting.Endpoint = firstNonEmpty(resource.Endpoint, setting.Endpoint)
setting.Bucket = firstNonEmpty(resource.Bucket, setting.Bucket)
// CDN 是具体存储位置的出口,不得在 provider/endpoint/bucket 不匹配时继续沿用,
// 否则切换到七牛后会把历史阿里云 objectKey 拼成七牛域名。
if !resourceMatchesSetting || (resourceProvider != "" && resourceProvider != setting.Provider) {
setting.CDNBaseURL = ""
}
if setting.AccessKeyID == "" || setting.AccessKeySecret == "" {
return ossSettingValue{}, errors.New("对象存储访问密钥不可用")
}
return setting, nil
}
func (s *Service) userOSSSettingForResource(userID string, resource *model.Resource) (ossSettingValue, error) {
settings, err := s.repo.UserOSSSettingsForUser(userID)
if err != nil {
return ossSettingValue{}, err
}
for index := range settings {
value, valueErr := s.userOSSSettingValue(&settings[index])
if valueErr != nil {
return ossSettingValue{}, valueErr
}
if resourceStorageMatches(value, resource) {
return value, nil
}
}
return ossSettingValue{}, gorm.ErrRecordNotFound
}
func resourceStorageMatches(setting ossSettingValue, resource *model.Resource) bool {
if resource == nil {
return false
}
setting = normalizeOSSSetting(setting)
return setting.Provider == strings.ToLower(strings.TrimSpace(resource.Provider)) &&
setting.Endpoint == strings.TrimRight(strings.TrimSpace(resource.Endpoint), "/") &&
setting.Bucket == strings.TrimSpace(resource.Bucket)
}
func ossSettingForProvider(setting ossSettingValue, provider string) (ossSettingValue, error) {
setting = normalizeOSSSetting(setting)
provider = strings.ToLower(strings.TrimSpace(provider))
if provider == "" || provider == setting.Provider {
return setting, nil
}
credentials, ok := setting.ArchivedCredentials[provider]
if !ok || credentials.AccessKeyID == "" || credentials.AccessKeySecret == "" {
return ossSettingValue{}, errors.New("历史对象存储访问密钥不可用")
}
setting.Provider = provider
setting.AccessKeyID = credentials.AccessKeyID
setting.AccessKeySecret = credentials.AccessKeySecret
return setting, nil
}
func validateActiveOSSSetting(setting ossSettingValue, disabledMessage string, incompleteMessage string) (ossSettingValue, error) {
setting = normalizeOSSSetting(setting)
if !setting.Enabled {
return ossSettingValue{}, BadAuthRequest(disabledMessage)
}
if setting.Provider != aliyunOSSProvider && setting.Provider != tencentCOSProvider && setting.Provider != qiniuKodoProvider && setting.Provider != s3Provider {
return ossSettingValue{}, BadAuthRequest("仅支持阿里云 OSS、腾讯云 COS、七牛云 Kodo 和通用 S3")
}
if setting.Bucket == "" || setting.Endpoint == "" || setting.AccessKeyID == "" || setting.AccessKeySecret == "" {
return ossSettingValue{}, BadAuthRequest(incompleteMessage)
}
return setting, nil
}