150 lines
5.9 KiB
Go
150 lines
5.9 KiB
Go
// 资源对应的对象存储配置解析:系统存储、用户自有存储与历史位置。
|
|
|
|
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
|
|
}
|