2020-09-29 12:05:13 +03:00
// Copyright 2020 The Gitea Authors. All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2020-09-29 12:05:13 +03:00
package setting
import (
2023-06-14 06:42:38 +03:00
"errors"
"fmt"
2020-10-13 06:58:34 +03:00
"path/filepath"
2023-11-01 14:17:18 +03:00
"strings"
2020-09-29 12:05:13 +03:00
)
2023-06-14 06:42:38 +03:00
// StorageType is a type of Storage
type StorageType string
const (
// LocalStorageType is the type descriptor for local storage
LocalStorageType StorageType = "local"
// MinioStorageType is the type descriptor for minio storage
MinioStorageType StorageType = "minio"
2024-05-30 10:33:50 +03:00
// AzureBlobStorageType is the type descriptor for azure blob storage
AzureBlobStorageType StorageType = "azureblob"
2023-06-14 06:42:38 +03:00
)
var storageTypes = [ ] StorageType {
LocalStorageType ,
MinioStorageType ,
2024-05-30 10:33:50 +03:00
AzureBlobStorageType ,
2023-06-14 06:42:38 +03:00
}
// IsValidStorageType returns true if the given storage type is valid
func IsValidStorageType ( storageType StorageType ) bool {
for _ , t := range storageTypes {
if t == storageType {
return true
}
}
return false
}
// MinioStorageConfig represents the configuration for a minio storage
type MinioStorageConfig struct {
Endpoint string ` ini:"MINIO_ENDPOINT" json:",omitempty" `
AccessKeyID string ` ini:"MINIO_ACCESS_KEY_ID" json:",omitempty" `
SecretAccessKey string ` ini:"MINIO_SECRET_ACCESS_KEY" json:",omitempty" `
2024-11-22 23:12:06 +03:00
IamEndpoint string ` ini:"MINIO_IAM_ENDPOINT" json:",omitempty" `
2023-06-14 06:42:38 +03:00
Bucket string ` ini:"MINIO_BUCKET" json:",omitempty" `
Location string ` ini:"MINIO_LOCATION" json:",omitempty" `
BasePath string ` ini:"MINIO_BASE_PATH" json:",omitempty" `
UseSSL bool ` ini:"MINIO_USE_SSL" `
InsecureSkipVerify bool ` ini:"MINIO_INSECURE_SKIP_VERIFY" `
ChecksumAlgorithm string ` ini:"MINIO_CHECKSUM_ALGORITHM" json:",omitempty" `
ServeDirect bool ` ini:"SERVE_DIRECT" `
2024-05-15 16:56:17 +03:00
BucketLookUpType string ` ini:"MINIO_BUCKET_LOOKUP_TYPE" json:",omitempty" `
2023-06-14 06:42:38 +03:00
}
2024-05-30 10:33:50 +03:00
func ( cfg * MinioStorageConfig ) ToShadow ( ) {
if cfg . AccessKeyID != "" {
cfg . AccessKeyID = "******"
}
if cfg . SecretAccessKey != "" {
cfg . SecretAccessKey = "******"
}
}
// MinioStorageConfig represents the configuration for a minio storage
type AzureBlobStorageConfig struct {
Endpoint string ` ini:"AZURE_BLOB_ENDPOINT" json:",omitempty" `
AccountName string ` ini:"AZURE_BLOB_ACCOUNT_NAME" json:",omitempty" `
AccountKey string ` ini:"AZURE_BLOB_ACCOUNT_KEY" json:",omitempty" `
Container string ` ini:"AZURE_BLOB_CONTAINER" json:",omitempty" `
BasePath string ` ini:"AZURE_BLOB_BASE_PATH" json:",omitempty" `
ServeDirect bool ` ini:"SERVE_DIRECT" `
}
func ( cfg * AzureBlobStorageConfig ) ToShadow ( ) {
if cfg . AccountKey != "" {
cfg . AccountKey = "******"
}
if cfg . AccountName != "" {
cfg . AccountName = "******"
}
}
2020-09-29 12:05:13 +03:00
// Storage represents configuration of storages
type Storage struct {
2024-05-30 10:33:50 +03:00
Type StorageType // local or minio or azureblob
Path string ` json:",omitempty" ` // for local type
TemporaryPath string ` json:",omitempty" `
MinioConfig MinioStorageConfig // for minio type
AzureBlobConfig AzureBlobStorageConfig // for azureblob type
2020-10-13 06:58:34 +03:00
}
2023-06-14 06:42:38 +03:00
func ( storage * Storage ) ToShadowCopy ( ) Storage {
shadowStorage := * storage
2024-05-30 10:33:50 +03:00
shadowStorage . MinioConfig . ToShadow ( )
shadowStorage . AzureBlobConfig . ToShadow ( )
2023-06-14 06:42:38 +03:00
return shadowStorage
2020-09-29 12:05:13 +03:00
}
2024-05-30 10:33:50 +03:00
func ( storage * Storage ) ServeDirect ( ) bool {
return ( storage . Type == MinioStorageType && storage . MinioConfig . ServeDirect ) ||
( storage . Type == AzureBlobStorageType && storage . AzureBlobConfig . ServeDirect )
}
2023-06-14 06:42:38 +03:00
const storageSectionName = "storage"
2020-10-13 06:58:34 +03:00
2023-06-14 06:42:38 +03:00
func getDefaultStorageSection ( rootCfg ConfigProvider ) ConfigSection {
storageSec := rootCfg . Section ( storageSectionName )
2020-10-13 06:58:34 +03:00
// Global Defaults
2023-06-14 06:42:38 +03:00
storageSec . Key ( "STORAGE_TYPE" ) . MustString ( "local" )
storageSec . Key ( "MINIO_ENDPOINT" ) . MustString ( "localhost:9000" )
storageSec . Key ( "MINIO_ACCESS_KEY_ID" ) . MustString ( "" )
storageSec . Key ( "MINIO_SECRET_ACCESS_KEY" ) . MustString ( "" )
storageSec . Key ( "MINIO_BUCKET" ) . MustString ( "gitea" )
storageSec . Key ( "MINIO_LOCATION" ) . MustString ( "us-east-1" )
storageSec . Key ( "MINIO_USE_SSL" ) . MustBool ( false )
storageSec . Key ( "MINIO_INSECURE_SKIP_VERIFY" ) . MustBool ( false )
storageSec . Key ( "MINIO_CHECKSUM_ALGORITHM" ) . MustString ( "default" )
2024-05-15 16:56:17 +03:00
storageSec . Key ( "MINIO_BUCKET_LOOKUP_TYPE" ) . MustString ( "auto" )
2024-05-30 10:33:50 +03:00
storageSec . Key ( "AZURE_BLOB_ENDPOINT" ) . MustString ( "" )
storageSec . Key ( "AZURE_BLOB_ACCOUNT_NAME" ) . MustString ( "" )
storageSec . Key ( "AZURE_BLOB_ACCOUNT_KEY" ) . MustString ( "" )
storageSec . Key ( "AZURE_BLOB_CONTAINER" ) . MustString ( "gitea" )
2023-06-14 06:42:38 +03:00
return storageSec
}
2020-10-13 06:58:34 +03:00
2023-08-04 06:41:16 +03:00
// getStorage will find target section and extra special section first and then read override
// items from extra section
2023-06-14 06:42:38 +03:00
func getStorage ( rootCfg ConfigProvider , name , typ string , sec ConfigSection ) ( * Storage , error ) {
if name == "" {
return nil , errors . New ( "no name for storage" )
2021-06-24 00:12:38 +03:00
}
2023-08-13 23:09:25 +03:00
targetSec , tp , err := getStorageTargetSection ( rootCfg , name , typ , sec )
if err != nil {
return nil , err
}
2024-04-29 11:47:56 +03:00
overrideSec := getStorageOverrideSection ( rootCfg , sec , tp , name )
2023-08-13 23:09:25 +03:00
targetType := targetSec . Key ( "STORAGE_TYPE" ) . String ( )
switch targetType {
case string ( LocalStorageType ) :
return getStorageForLocal ( targetSec , overrideSec , tp , name )
case string ( MinioStorageType ) :
return getStorageForMinio ( targetSec , overrideSec , tp , name )
2024-05-30 10:33:50 +03:00
case string ( AzureBlobStorageType ) :
return getStorageForAzureBlob ( targetSec , overrideSec , tp , name )
2023-08-13 23:09:25 +03:00
default :
return nil , fmt . Errorf ( "unsupported storage type %q" , targetType )
}
}
type targetSecType int
const (
targetSecIsTyp targetSecType = iota // target section is [storage.type] which the type from parameter
targetSecIsStorage // target section is [storage]
targetSecIsDefault // target section is the default value
targetSecIsStorageWithName // target section is [storage.name]
targetSecIsSec // target section is from the name seciont [name]
)
2024-06-11 21:47:45 +03:00
func getStorageSectionByType ( rootCfg ConfigProvider , typ string ) ( ConfigSection , targetSecType , error ) { //nolint:unparam
2023-08-13 23:09:25 +03:00
targetSec , err := rootCfg . GetSection ( storageSectionName + "." + typ )
if err != nil {
if ! IsValidStorageType ( StorageType ( typ ) ) {
return nil , 0 , fmt . Errorf ( "get section via storage type %q failed: %v" , typ , err )
2023-06-14 06:42:38 +03:00
}
2023-08-13 23:09:25 +03:00
// if typ is a valid storage type, but there is no [storage.local] or [storage.minio] section
// it's not an error
return nil , 0 , nil
}
targetType := targetSec . Key ( "STORAGE_TYPE" ) . String ( )
if targetType == "" {
if ! IsValidStorageType ( StorageType ( typ ) ) {
return nil , 0 , fmt . Errorf ( "unknow storage type %q" , typ )
2023-06-14 06:42:38 +03:00
}
2023-08-13 23:09:25 +03:00
targetSec . Key ( "STORAGE_TYPE" ) . SetValue ( typ )
} else if ! IsValidStorageType ( StorageType ( targetType ) ) {
return nil , 0 , fmt . Errorf ( "unknow storage type %q for section storage.%v" , targetType , typ )
2020-12-22 02:03:18 +03:00
}
2023-08-13 23:09:25 +03:00
return targetSec , targetSecIsTyp , nil
}
func getStorageTargetSection ( rootCfg ConfigProvider , name , typ string , sec ConfigSection ) ( ConfigSection , targetSecType , error ) {
// check typ first
if typ == "" {
if sec != nil { // check sec's type secondly
typ = sec . Key ( "STORAGE_TYPE" ) . String ( )
if IsValidStorageType ( StorageType ( typ ) ) {
if targetSec , _ := rootCfg . GetSection ( storageSectionName + "." + typ ) ; targetSec == nil {
return sec , targetSecIsSec , nil
}
}
2023-08-04 06:41:16 +03:00
}
2023-06-14 06:42:38 +03:00
}
2023-08-04 06:41:16 +03:00
2023-08-13 23:09:25 +03:00
if typ != "" {
targetSec , tp , err := getStorageSectionByType ( rootCfg , typ )
if targetSec != nil || err != nil {
return targetSec , tp , err
}
2023-06-14 06:42:38 +03:00
}
2023-08-04 06:41:16 +03:00
2023-08-13 23:09:25 +03:00
// check stoarge name thirdly
targetSec , _ := rootCfg . GetSection ( storageSectionName + "." + name )
if targetSec != nil {
2023-06-14 06:42:38 +03:00
targetType := targetSec . Key ( "STORAGE_TYPE" ) . String ( )
switch {
case targetType == "" :
2023-08-13 23:09:25 +03:00
if targetSec . Key ( "PATH" ) . String ( ) == "" { // both storage type and path are empty, use default
return getDefaultStorageSection ( rootCfg ) , targetSecIsDefault , nil
2023-06-14 06:42:38 +03:00
}
2023-08-13 23:09:25 +03:00
targetSec . Key ( "STORAGE_TYPE" ) . SetValue ( "local" )
2023-06-14 06:42:38 +03:00
default :
2023-08-13 23:09:25 +03:00
targetSec , tp , err := getStorageSectionByType ( rootCfg , targetType )
if targetSec != nil || err != nil {
return targetSec , tp , err
2023-06-14 06:42:38 +03:00
}
2020-12-22 02:03:18 +03:00
}
2023-08-13 23:09:25 +03:00
return targetSec , targetSecIsStorageWithName , nil
2020-12-22 02:03:18 +03:00
}
2023-08-13 23:09:25 +03:00
return getDefaultStorageSection ( rootCfg ) , targetSecIsDefault , nil
}
// getStorageOverrideSection override section will be read SERVE_DIRECT, PATH, MINIO_BASE_PATH, MINIO_BUCKET to override the targetsec when possible
2024-04-29 11:47:56 +03:00
func getStorageOverrideSection ( rootConfig ConfigProvider , sec ConfigSection , targetSecType targetSecType , name string ) ConfigSection {
2023-08-13 23:09:25 +03:00
if targetSecType == targetSecIsSec {
return nil
2023-06-14 06:42:38 +03:00
}
2023-08-13 23:09:25 +03:00
if sec != nil {
return sec
2023-08-04 06:41:16 +03:00
}
2023-08-13 23:09:25 +03:00
if targetSecType != targetSecIsStorageWithName {
nameSec , _ := rootConfig . GetSection ( storageSectionName + "." + name )
return nameSec
}
return nil
}
2023-06-14 06:42:38 +03:00
2023-08-13 23:09:25 +03:00
func getStorageForLocal ( targetSec , overrideSec ConfigSection , tp targetSecType , name string ) ( * Storage , error ) {
storage := Storage {
Type : StorageType ( targetSec . Key ( "STORAGE_TYPE" ) . String ( ) ) ,
}
2023-06-14 06:42:38 +03:00
2023-08-13 23:09:25 +03:00
targetPath := ConfigSectionKeyString ( targetSec , "PATH" , "" )
var fallbackPath string
if targetPath == "" { // no path
fallbackPath = filepath . Join ( AppDataPath , name )
} else {
if tp == targetSecIsStorage || tp == targetSecIsDefault {
2023-08-04 06:41:16 +03:00
fallbackPath = filepath . Join ( targetPath , name )
2023-08-13 23:09:25 +03:00
} else {
fallbackPath = targetPath
}
if ! filepath . IsAbs ( fallbackPath ) {
fallbackPath = filepath . Join ( AppDataPath , fallbackPath )
2023-06-14 06:42:38 +03:00
}
2023-08-13 23:09:25 +03:00
}
2023-08-04 06:41:16 +03:00
2023-08-13 23:09:25 +03:00
if overrideSec == nil { // no override section
storage . Path = fallbackPath
} else {
storage . Path = ConfigSectionKeyString ( overrideSec , "PATH" , "" )
if storage . Path == "" { // overrideSec has no path
2023-08-04 06:41:16 +03:00
storage . Path = fallbackPath
2023-08-13 23:09:25 +03:00
} else if ! filepath . IsAbs ( storage . Path ) {
if targetPath == "" {
storage . Path = filepath . Join ( AppDataPath , storage . Path )
} else {
2023-08-04 06:41:16 +03:00
storage . Path = filepath . Join ( targetPath , storage . Path )
}
}
2023-08-13 23:09:25 +03:00
}
2023-08-04 06:41:16 +03:00
2024-04-07 04:11:25 +03:00
checkOverlappedPath ( "[storage." + name + "].PATH" , storage . Path )
2024-02-09 17:06:03 +03:00
2023-08-13 23:09:25 +03:00
return & storage , nil
}
2020-12-21 20:59:18 +03:00
2024-05-30 10:33:50 +03:00
func getStorageForMinio ( targetSec , overrideSec ConfigSection , tp targetSecType , name string ) ( * Storage , error ) { //nolint:dupl
2023-08-13 23:09:25 +03:00
var storage Storage
storage . Type = StorageType ( targetSec . Key ( "STORAGE_TYPE" ) . String ( ) )
if err := targetSec . MapTo ( & storage . MinioConfig ) ; err != nil {
return nil , fmt . Errorf ( "map minio config failed: %v" , err )
}
2023-08-04 06:41:16 +03:00
2023-11-01 14:17:18 +03:00
var defaultPath string
if storage . MinioConfig . BasePath != "" {
if tp == targetSecIsStorage || tp == targetSecIsDefault {
defaultPath = strings . TrimSuffix ( storage . MinioConfig . BasePath , "/" ) + "/" + name + "/"
} else {
defaultPath = storage . MinioConfig . BasePath
}
}
if defaultPath == "" {
defaultPath = name + "/"
2020-10-13 06:58:34 +03:00
}
2023-08-13 23:09:25 +03:00
if overrideSec != nil {
storage . MinioConfig . ServeDirect = ConfigSectionKeyBool ( overrideSec , "SERVE_DIRECT" , storage . MinioConfig . ServeDirect )
2023-11-01 14:17:18 +03:00
storage . MinioConfig . BasePath = ConfigSectionKeyString ( overrideSec , "MINIO_BASE_PATH" , defaultPath )
2023-08-13 23:09:25 +03:00
storage . MinioConfig . Bucket = ConfigSectionKeyString ( overrideSec , "MINIO_BUCKET" , storage . MinioConfig . Bucket )
2023-11-01 14:17:18 +03:00
} else {
storage . MinioConfig . BasePath = defaultPath
2023-08-13 23:09:25 +03:00
}
2023-06-14 06:42:38 +03:00
return & storage , nil
2020-09-29 12:05:13 +03:00
}
2024-05-30 10:33:50 +03:00
func getStorageForAzureBlob ( targetSec , overrideSec ConfigSection , tp targetSecType , name string ) ( * Storage , error ) { //nolint:dupl
var storage Storage
storage . Type = StorageType ( targetSec . Key ( "STORAGE_TYPE" ) . String ( ) )
if err := targetSec . MapTo ( & storage . AzureBlobConfig ) ; err != nil {
return nil , fmt . Errorf ( "map azure blob config failed: %v" , err )
}
var defaultPath string
if storage . AzureBlobConfig . BasePath != "" {
if tp == targetSecIsStorage || tp == targetSecIsDefault {
defaultPath = strings . TrimSuffix ( storage . AzureBlobConfig . BasePath , "/" ) + "/" + name + "/"
} else {
defaultPath = storage . AzureBlobConfig . BasePath
}
}
if defaultPath == "" {
defaultPath = name + "/"
}
if overrideSec != nil {
storage . AzureBlobConfig . ServeDirect = ConfigSectionKeyBool ( overrideSec , "SERVE_DIRECT" , storage . AzureBlobConfig . ServeDirect )
storage . AzureBlobConfig . BasePath = ConfigSectionKeyString ( overrideSec , "AZURE_BLOB_BASE_PATH" , defaultPath )
storage . AzureBlobConfig . Container = ConfigSectionKeyString ( overrideSec , "AZURE_BLOB_CONTAINER" , storage . AzureBlobConfig . Container )
} else {
storage . AzureBlobConfig . BasePath = defaultPath
}
return & storage , nil
}