将安装向导写入、域名设置和安装完成流程统一接入分区配置事务,保留旧版配置覆盖保护。
This commit is contained in:
parent
eb50d062b1
commit
84c267472b
@ -26,9 +26,11 @@ func isPartitionedConfig(data []byte) (bool, error) {
|
||||
|
||||
// loadPartitionedCompatibility 仅构造旧业务所需的只读快照,不重写分区配置。
|
||||
func loadPartitionedCompatibility() (*Config, error) {
|
||||
runtime, err := NewConfigLoader(ROOT_PATH, ConfigLoaderOptions{
|
||||
Validation: RuntimeValidationOptions{HasSuperAdmin: true, Production: !DevelopmentMode},
|
||||
}).Load()
|
||||
return loadPartitionedCompatibilityWithOptions(RuntimeValidationOptions{HasSuperAdmin: true, Production: !DevelopmentMode})
|
||||
}
|
||||
|
||||
func loadPartitionedCompatibilityWithOptions(validation RuntimeValidationOptions) (*Config, error) {
|
||||
runtime, err := NewConfigLoader(ROOT_PATH, ConfigLoaderOptions{Validation: validation}).Load()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -8,6 +8,17 @@ func FinalizeSetupConfig(cfg *Config) error {
|
||||
if cfg == nil {
|
||||
return ErrInvalidRuntimeConfig
|
||||
}
|
||||
data, err := readConfigFile("config.json")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
partitioned, err := isPartitionedConfig(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if partitioned {
|
||||
return finalizePartitionedSetupConfig(cfg)
|
||||
}
|
||||
|
||||
pending := *cfg
|
||||
pending.SetupState = SetupStateBootstrap
|
||||
@ -28,3 +39,10 @@ func FinalizeSetupConfig(cfg *Config) error {
|
||||
PublishConfigSnapshot(loaded)
|
||||
return nil
|
||||
}
|
||||
|
||||
func finalizePartitionedSetupConfig(cfg *Config) error {
|
||||
data := *cfg
|
||||
data.SetupState = SetupStateReady
|
||||
data.IsInit = true
|
||||
return writeSetupConfigUnlocked(&data)
|
||||
}
|
||||
|
||||
178
backend/config/setup_writer.go
Normal file
178
backend/config/setup_writer.go
Normal file
@ -0,0 +1,178 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.pcux.cn/xuans/Smail/utils/credential"
|
||||
)
|
||||
|
||||
// WriteSetupConfig 将安装向导使用的兼容配置安全提交到分区配置。
|
||||
// 运行配置已经分区化时,安装流程不得直接覆盖 config.json。
|
||||
func WriteSetupConfig(cfg *Config) error {
|
||||
legacyConfigMu.Lock()
|
||||
defer legacyConfigMu.Unlock()
|
||||
return writeSetupConfigUnlocked(cfg)
|
||||
}
|
||||
|
||||
func writeSetupConfigUnlocked(cfg *Config) error {
|
||||
if cfg == nil {
|
||||
return ErrInvalidRuntimeConfig
|
||||
}
|
||||
|
||||
data, err := readConfigFile("config.json")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
partitioned, err := isPartitionedConfig(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !partitioned {
|
||||
return writeConfig(cfg)
|
||||
}
|
||||
|
||||
current, err := loadSetupRuntime()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
candidate, err := setupRuntimeFromConfig(current, cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
validation := RuntimeValidationOptions{
|
||||
HasSuperAdmin: candidate.SetupState == SetupStateReady,
|
||||
Production: !DevelopmentMode,
|
||||
}
|
||||
if err := NewTransactionStore(ROOT_PATH, validation).Commit(current, candidate); err != nil {
|
||||
return err
|
||||
}
|
||||
loaded, err := loadPartitionedCompatibilityWithOptions(validation)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
PublishConfigSnapshot(loaded)
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadSetupRuntime() (RuntimeConfig, error) {
|
||||
production := !DevelopmentMode
|
||||
var lastErr error
|
||||
for _, hasSuperAdmin := range []bool{false, true} {
|
||||
runtime, err := NewConfigLoader(ROOT_PATH, ConfigLoaderOptions{
|
||||
Validation: RuntimeValidationOptions{HasSuperAdmin: hasSuperAdmin, Production: production},
|
||||
}).Load()
|
||||
if err == nil {
|
||||
return runtime, nil
|
||||
}
|
||||
lastErr = err
|
||||
}
|
||||
return RuntimeConfig{}, lastErr
|
||||
}
|
||||
|
||||
func setupRuntimeFromConfig(current RuntimeConfig, cfg *Config) (RuntimeConfig, error) {
|
||||
vault, err := credential.NewVault(ROOT_PATH)
|
||||
if err != nil {
|
||||
return RuntimeConfig{}, err
|
||||
}
|
||||
legacy := legacyMigrationConfigFromConfig(cfg)
|
||||
database, manualFields, err := legacyDatabaseConfig(ROOT_PATH, vault, legacy)
|
||||
if err != nil {
|
||||
return RuntimeConfig{}, err
|
||||
}
|
||||
if len(manualFields) != 0 {
|
||||
return RuntimeConfig{}, fmt.Errorf("安装配置无法安全转换:%s", strings.Join(manualFields, ", "))
|
||||
}
|
||||
relay, manualFields, err := legacySMTPConfig(ROOT_PATH, vault, legacy)
|
||||
if err != nil {
|
||||
return RuntimeConfig{}, err
|
||||
}
|
||||
if len(manualFields) != 0 {
|
||||
return RuntimeConfig{}, fmt.Errorf("安装配置无法安全转换:%s", strings.Join(manualFields, ", "))
|
||||
}
|
||||
ssl, manualFields := legacySSLConfig(legacy)
|
||||
if len(manualFields) != 0 {
|
||||
return RuntimeConfig{}, fmt.Errorf("安装配置无法安全转换:%s", strings.Join(manualFields, ", "))
|
||||
}
|
||||
|
||||
candidate := cloneRuntimeConfig(current)
|
||||
candidate.Generation = current.Generation + 1
|
||||
candidate.SetupState = cfg.SetupState
|
||||
if !candidate.SetupState.Valid() {
|
||||
candidate.SetupState = SetupStateBootstrap
|
||||
}
|
||||
candidate.Root.Generation = candidate.Generation
|
||||
candidate.Root.SetupState = candidate.SetupState
|
||||
candidate.Root.LogLevel = cfg.LogLevel
|
||||
candidate.Root.Database = database
|
||||
candidate.Root.Domains = DomainConfig{
|
||||
Primary: cfg.Domain,
|
||||
Accepted: append([]string(nil), cfg.Domains...),
|
||||
Web: cfg.WebDomain,
|
||||
AdminRecovery: cfg.AdminRecoveryDomain,
|
||||
AccessHosts: append([]string(nil), cfg.AccessHosts...),
|
||||
}
|
||||
httpsEnabled := cfg.HttpsEnabled != 2
|
||||
httpsPort := cfg.HttpsPort
|
||||
tlsMode := strings.ToLower(strings.TrimSpace(cfg.TLSMode))
|
||||
if tlsMode == HTTPSTLSModeProxy {
|
||||
httpsEnabled = false
|
||||
httpsPort = 0
|
||||
}
|
||||
candidate.Root.HTTP = HTTPConfig{
|
||||
Port: cfg.HttpPort,
|
||||
HTTPSPort: httpsPort,
|
||||
PublicHTTPSPort: cfg.PublicHTTPSPort,
|
||||
HTTPSEnabled: httpsEnabled,
|
||||
TLSMode: tlsMode,
|
||||
TrustedProxyCIDRs: append([]string(nil), cfg.TrustedProxyCIDRs...),
|
||||
}
|
||||
candidate.SMTP.Relay = relay
|
||||
candidate.SMTP.Relay.VerificationEnabled = cfg.VerificationEnabled
|
||||
candidate.SMTP.Relay.LoginPolicy = cfg.LoginPolicy
|
||||
candidate.Email.Policy.SpamFilterLevel = cfg.SpamFilterLevel
|
||||
candidate.Email.Policy.ExternalRecoveryEnabled = cfg.ExternalRecoveryEnabled
|
||||
candidate.SSL.TLS = ssl
|
||||
candidate.Root.UpdatedAt = time.Now().UTC()
|
||||
return candidate, nil
|
||||
}
|
||||
|
||||
func legacyMigrationConfigFromConfig(cfg *Config) legacyMigrationConfig {
|
||||
return legacyMigrationConfig{
|
||||
LogLevel: cfg.LogLevel,
|
||||
Domain: cfg.Domain,
|
||||
Domains: append([]string(nil), cfg.Domains...),
|
||||
WebDomain: cfg.WebDomain,
|
||||
AdminRecoveryDomain: cfg.AdminRecoveryDomain,
|
||||
AccessHosts: append([]string(nil), cfg.AccessHosts...),
|
||||
SSLType: cfg.SSLType,
|
||||
SSLPrivateKeyPath: cfg.SSLPrivateKeyPath,
|
||||
SSLPublicKeyPath: cfg.SSLPublicKeyPath,
|
||||
SSL: cfg.SSL,
|
||||
DbDSN: cfg.DbDSN,
|
||||
DbType: cfg.DbType,
|
||||
HttpsEnabled: cfg.HttpsEnabled,
|
||||
HttpPort: cfg.HttpPort,
|
||||
HttpsPort: cfg.HttpsPort,
|
||||
PublicHTTPSPort: cfg.PublicHTTPSPort,
|
||||
TLSMode: cfg.TLSMode,
|
||||
TrustedProxyCIDRs: append([]string(nil), cfg.TrustedProxyCIDRs...),
|
||||
SMTPHost: cfg.SMTPHost,
|
||||
SMTPPort: cfg.SMTPPort,
|
||||
SMTPUsername: cfg.SMTPUsername,
|
||||
SMTPPassword: cfg.SMTPPassword,
|
||||
SMTPSecurity: cfg.SMTPSecurity,
|
||||
SMTPFrom: cfg.SMTPFrom,
|
||||
SMTPFromDomain: cfg.SMTPFromDomain,
|
||||
VerificationEnabled: boolPointer(cfg.VerificationEnabled),
|
||||
ExternalRecoveryEnabled: boolPointer(cfg.ExternalRecoveryEnabled),
|
||||
LoginPolicy: cfg.LoginPolicy,
|
||||
SetupState: cfg.SetupState,
|
||||
IsInit: cfg.IsInit,
|
||||
}
|
||||
}
|
||||
|
||||
func boolPointer(value bool) *bool {
|
||||
return &value
|
||||
}
|
||||
@ -286,7 +286,7 @@ func SetDatabaseSettingsWithOptions(ctx *context.Context, settings DatabaseSetti
|
||||
markSetupInProgress(&candidateConfig)
|
||||
candidateConfig.DbType = dbType
|
||||
candidateConfig.DbDSN = dbDSN
|
||||
if err = config.WriteConfig(&candidateConfig); err != nil {
|
||||
if err = config.WriteSetupConfig(&candidateConfig); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
if err = config.Init(); err != nil {
|
||||
@ -685,7 +685,7 @@ func restoreDatabaseConfig(previous *config.Config) {
|
||||
return
|
||||
}
|
||||
closeDatabaseInstance()
|
||||
if err := config.WriteConfig(previous); err != nil {
|
||||
if err := config.WriteSetupConfig(previous); err != nil {
|
||||
return
|
||||
}
|
||||
_ = config.Init()
|
||||
|
||||
@ -63,7 +63,7 @@ func setDomainSettings(smtpDomain, webDomain, multiDomains string, publicHTTPSPo
|
||||
|
||||
// 检查域名是否指向本机 todo
|
||||
|
||||
err = config.WriteConfig(configData)
|
||||
err = config.WriteSetupConfig(configData)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
@ -25,7 +25,7 @@ func Finish() error {
|
||||
portsChanged := applyServicePorts(cfg)
|
||||
if cfg.IsSetupReady() {
|
||||
if portsChanged {
|
||||
if err = config.WriteConfig(cfg); err != nil {
|
||||
if err = config.WriteSetupConfig(cfg); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user