Init V4 community edition (#2265)
* Init V4 community edition * Init V4 community edition
This commit is contained in:
255
pkg/conf/conf.go
255
pkg/conf/conf.go
@@ -1,75 +1,135 @@
|
||||
package conf
|
||||
|
||||
import (
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/util"
|
||||
"fmt"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/logging"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/util"
|
||||
"github.com/go-ini/ini"
|
||||
"github.com/go-playground/validator/v10"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// database 数据库
|
||||
type database struct {
|
||||
Type string
|
||||
User string
|
||||
Password string
|
||||
Host string
|
||||
Name string
|
||||
TablePrefix string
|
||||
DBFile string
|
||||
Port int
|
||||
Charset string
|
||||
UnixSocket bool
|
||||
const (
|
||||
envConfOverrideKey = "CR_CONF_"
|
||||
)
|
||||
|
||||
type ConfigProvider interface {
|
||||
Database() *Database
|
||||
System() *System
|
||||
SSL() *SSL
|
||||
Unix() *Unix
|
||||
Slave() *Slave
|
||||
Redis() *Redis
|
||||
Cors() *Cors
|
||||
OptionOverwrite() map[string]any
|
||||
}
|
||||
|
||||
// system 系统通用配置
|
||||
type system struct {
|
||||
Mode string `validate:"eq=master|eq=slave"`
|
||||
Listen string `validate:"required"`
|
||||
Debug bool
|
||||
SessionSecret string
|
||||
HashIDSalt string
|
||||
GracePeriod int `validate:"gte=0"`
|
||||
ProxyHeader string `validate:"required_with=Listen"`
|
||||
// NewIniConfigProvider initializes a new Ini config file provider. A default config file
|
||||
// will be created if the given path does not exist.
|
||||
func NewIniConfigProvider(configPath string, l logging.Logger) (ConfigProvider, error) {
|
||||
if configPath == "" || !util.Exists(configPath) {
|
||||
l.Info("Config file %q not found, creating a new one.", configPath)
|
||||
// 创建初始配置文件
|
||||
confContent := util.Replace(map[string]string{
|
||||
"{SessionSecret}": util.RandStringRunes(64),
|
||||
}, defaultConf)
|
||||
f, err := util.CreatNestedFile(configPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create config file: %w", err)
|
||||
}
|
||||
|
||||
// 写入配置文件
|
||||
_, err = f.WriteString(confContent)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to write config file: %w", err)
|
||||
}
|
||||
|
||||
f.Close()
|
||||
}
|
||||
|
||||
cfg, err := ini.Load(configPath, []byte(getOverrideConfFromEnv(l)))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse config file %q: %w", configPath, err)
|
||||
}
|
||||
|
||||
provider := &iniConfigProvider{
|
||||
database: *DatabaseConfig,
|
||||
system: *SystemConfig,
|
||||
ssl: *SSLConfig,
|
||||
unix: *UnixConfig,
|
||||
slave: *SlaveConfig,
|
||||
redis: *RedisConfig,
|
||||
cors: *CORSConfig,
|
||||
optionOverwrite: make(map[string]interface{}),
|
||||
}
|
||||
|
||||
sections := map[string]interface{}{
|
||||
"Database": &provider.database,
|
||||
"System": &provider.system,
|
||||
"SSL": &provider.ssl,
|
||||
"UnixSocket": &provider.unix,
|
||||
"Redis": &provider.redis,
|
||||
"CORS": &provider.cors,
|
||||
"Slave": &provider.slave,
|
||||
}
|
||||
for sectionName, sectionStruct := range sections {
|
||||
err = mapSection(cfg, sectionName, sectionStruct)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse config section %q: %w", sectionName, err)
|
||||
}
|
||||
}
|
||||
|
||||
// 映射数据库配置覆盖
|
||||
for _, key := range cfg.Section("OptionOverwrite").Keys() {
|
||||
provider.optionOverwrite[key.Name()] = key.Value()
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
|
||||
type ssl struct {
|
||||
CertPath string `validate:"omitempty,required"`
|
||||
KeyPath string `validate:"omitempty,required"`
|
||||
Listen string `validate:"required"`
|
||||
type iniConfigProvider struct {
|
||||
database Database
|
||||
system System
|
||||
ssl SSL
|
||||
unix Unix
|
||||
slave Slave
|
||||
redis Redis
|
||||
cors Cors
|
||||
optionOverwrite map[string]any
|
||||
}
|
||||
|
||||
type unix struct {
|
||||
Listen string
|
||||
Perm uint32
|
||||
func (i *iniConfigProvider) Database() *Database {
|
||||
return &i.database
|
||||
}
|
||||
|
||||
// slave 作为slave存储端配置
|
||||
type slave struct {
|
||||
Secret string `validate:"omitempty,gte=64"`
|
||||
CallbackTimeout int `validate:"omitempty,gte=1"`
|
||||
SignatureTTL int `validate:"omitempty,gte=1"`
|
||||
func (i *iniConfigProvider) System() *System {
|
||||
return &i.system
|
||||
}
|
||||
|
||||
// redis 配置
|
||||
type redis struct {
|
||||
Network string
|
||||
Server string
|
||||
User string
|
||||
Password string
|
||||
DB string
|
||||
func (i *iniConfigProvider) SSL() *SSL {
|
||||
return &i.ssl
|
||||
}
|
||||
|
||||
// 跨域配置
|
||||
type cors struct {
|
||||
AllowOrigins []string
|
||||
AllowMethods []string
|
||||
AllowHeaders []string
|
||||
AllowCredentials bool
|
||||
ExposeHeaders []string
|
||||
SameSite string
|
||||
Secure bool
|
||||
func (i *iniConfigProvider) Unix() *Unix {
|
||||
return &i.unix
|
||||
}
|
||||
|
||||
var cfg *ini.File
|
||||
func (i *iniConfigProvider) Slave() *Slave {
|
||||
return &i.slave
|
||||
}
|
||||
|
||||
func (i *iniConfigProvider) Redis() *Redis {
|
||||
return &i.redis
|
||||
}
|
||||
|
||||
func (i *iniConfigProvider) Cors() *Cors {
|
||||
return &i.cors
|
||||
}
|
||||
|
||||
func (i *iniConfigProvider) OptionOverwrite() map[string]any {
|
||||
return i.optionOverwrite
|
||||
}
|
||||
|
||||
const defaultConf = `[System]
|
||||
Debug = false
|
||||
@@ -79,67 +139,8 @@ SessionSecret = {SessionSecret}
|
||||
HashIDSalt = {HashIDSalt}
|
||||
`
|
||||
|
||||
// Init 初始化配置文件
|
||||
func Init(path string) {
|
||||
var err error
|
||||
|
||||
if path == "" || !util.Exists(path) {
|
||||
// 创建初始配置文件
|
||||
confContent := util.Replace(map[string]string{
|
||||
"{SessionSecret}": util.RandStringRunes(64),
|
||||
"{HashIDSalt}": util.RandStringRunes(64),
|
||||
}, defaultConf)
|
||||
f, err := util.CreatNestedFile(path)
|
||||
if err != nil {
|
||||
util.Log().Panic("Failed to create config file: %s", err)
|
||||
}
|
||||
|
||||
// 写入配置文件
|
||||
_, err = f.WriteString(confContent)
|
||||
if err != nil {
|
||||
util.Log().Panic("Failed to write config file: %s", err)
|
||||
}
|
||||
|
||||
f.Close()
|
||||
}
|
||||
|
||||
cfg, err = ini.Load(path)
|
||||
if err != nil {
|
||||
util.Log().Panic("Failed to parse config file %q: %s", path, err)
|
||||
}
|
||||
|
||||
sections := map[string]interface{}{
|
||||
"Database": DatabaseConfig,
|
||||
"System": SystemConfig,
|
||||
"SSL": SSLConfig,
|
||||
"UnixSocket": UnixConfig,
|
||||
"Redis": RedisConfig,
|
||||
"CORS": CORSConfig,
|
||||
"Slave": SlaveConfig,
|
||||
}
|
||||
for sectionName, sectionStruct := range sections {
|
||||
err = mapSection(sectionName, sectionStruct)
|
||||
if err != nil {
|
||||
util.Log().Panic("Failed to parse config section %q: %s", sectionName, err)
|
||||
}
|
||||
}
|
||||
|
||||
// 映射数据库配置覆盖
|
||||
for _, key := range cfg.Section("OptionOverwrite").Keys() {
|
||||
OptionOverwrite[key.Name()] = key.Value()
|
||||
}
|
||||
|
||||
// 重设log等级
|
||||
if !SystemConfig.Debug {
|
||||
util.Level = util.LevelInformational
|
||||
util.GloablLogger = nil
|
||||
util.Log()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// mapSection 将配置文件的 Section 映射到结构体上
|
||||
func mapSection(section string, confStruct interface{}) error {
|
||||
func mapSection(cfg *ini.File, section string, confStruct interface{}) error {
|
||||
err := cfg.Section(section).MapTo(confStruct)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -154,3 +155,35 @@ func mapSection(section string, confStruct interface{}) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getOverrideConfFromEnv(l logging.Logger) string {
|
||||
confMaps := make(map[string]map[string]string)
|
||||
for _, env := range os.Environ() {
|
||||
if !strings.HasPrefix(env, envConfOverrideKey) {
|
||||
continue
|
||||
}
|
||||
|
||||
// split by key=value and get key
|
||||
kv := strings.SplitN(env, "=", 2)
|
||||
configKey := strings.TrimPrefix(kv[0], envConfOverrideKey)
|
||||
configValue := kv[1]
|
||||
sectionKey := strings.SplitN(configKey, ".", 2)
|
||||
if confMaps[sectionKey[0]] == nil {
|
||||
confMaps[sectionKey[0]] = make(map[string]string)
|
||||
}
|
||||
|
||||
confMaps[sectionKey[0]][sectionKey[1]] = configValue
|
||||
l.Info("Override config %q = %q", configKey, configValue)
|
||||
}
|
||||
|
||||
// generate ini content
|
||||
var sb strings.Builder
|
||||
for section, kvs := range confMaps {
|
||||
sb.WriteString(fmt.Sprintf("[%s]\n", section))
|
||||
for k, v := range kvs {
|
||||
sb.WriteString(fmt.Sprintf("%s = %s\n", k, v))
|
||||
}
|
||||
}
|
||||
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user