206 lines
5.9 KiB
Go
206 lines
5.9 KiB
Go
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"sync/atomic"
|
|
|
|
"git.pcux.cn/xuans/Smail/utils/credential"
|
|
)
|
|
|
|
// publishedConfig 是旧配置模型的只读兼容快照。
|
|
// 新业务应通过 CurrentConfig 读取,禁止在请求处理中直接持有并修改 Instance。
|
|
var publishedConfig atomic.Pointer[Config]
|
|
var hasPublishedConfig atomic.Bool
|
|
var setupPort atomic.Int64
|
|
|
|
func IsSetupReady() bool {
|
|
value := CurrentConfig()
|
|
if value != nil && value.SetupState.Valid() {
|
|
return value.SetupState == SetupStateReady
|
|
}
|
|
return legacyInitReady()
|
|
}
|
|
|
|
// PublishConfigSnapshot 发布一份深拷贝,供运行时只读访问。
|
|
// 调用方仍可继续使用旧的 Instance 兼容路径,但不能把返回值当作可变全局状态。
|
|
func PublishConfigSnapshot(value *Config) {
|
|
hasPublishedConfig.Store(true)
|
|
if value == nil {
|
|
publishedConfig.Store(nil)
|
|
return
|
|
}
|
|
clone := cloneConfig(value)
|
|
publishedConfig.Store(clone)
|
|
}
|
|
|
|
// CurrentConfig 返回当前运行时配置的独立副本。
|
|
// 返回值可以安全地在业务代码中读取或局部修改,不会改变全局配置快照。
|
|
func CurrentConfig() *Config {
|
|
if value := publishedConfig.Load(); value != nil {
|
|
return cloneConfig(value)
|
|
}
|
|
if hasPublishedConfig.Load() {
|
|
return nil
|
|
}
|
|
if Instance == nil {
|
|
return nil
|
|
}
|
|
return cloneConfig(Instance)
|
|
}
|
|
|
|
// CurrentHTTPConfig 返回当前进程实际使用的 HTTP 配置。
|
|
// 分区配置存在时优先读取分区配置,旧版配置仅作为兼容回退。
|
|
func CurrentHTTPConfig() HTTPConfig {
|
|
if manager := CurrentManager(); manager != nil {
|
|
runtime := manager.Snapshot()
|
|
if runtime.Generation != 0 {
|
|
return cloneHTTPConfig(runtime.Root.HTTP)
|
|
}
|
|
}
|
|
cfg := CurrentConfig()
|
|
if cfg == nil {
|
|
return HTTPConfig{}
|
|
}
|
|
mode := strings.ToLower(strings.TrimSpace(cfg.TLSMode))
|
|
return HTTPConfig{
|
|
Port: cfg.HttpPort,
|
|
HTTPSPort: cfg.HttpsPort,
|
|
HTTPSEnabled: cfg.HttpsEnabled != 2,
|
|
TLSMode: mode,
|
|
TrustedProxyCIDRs: append([]string(nil), cfg.TrustedProxyCIDRs...),
|
|
}
|
|
}
|
|
|
|
// CurrentWebDomain 返回当前进程实际使用的 Web 域名。
|
|
// 分区配置存在时优先读取分区配置,旧版配置仅作为兼容回退。
|
|
func CurrentWebDomain() string {
|
|
if manager := CurrentManager(); manager != nil {
|
|
runtime := manager.Snapshot()
|
|
if runtime.Generation != 0 {
|
|
return runtime.Root.Domains.Web
|
|
}
|
|
}
|
|
cfg := CurrentConfig()
|
|
if cfg == nil {
|
|
return ""
|
|
}
|
|
return cfg.WebDomain
|
|
}
|
|
|
|
// SetRuntimeSetupPort stores the temporary setup listener port outside the persisted configuration.
|
|
func SetRuntimeSetupPort(port int) {
|
|
setupPort.Store(int64(port))
|
|
}
|
|
|
|
func RuntimeSetupPort() int {
|
|
return int(setupPort.Load())
|
|
}
|
|
|
|
func cloneConfig(source *Config) *Config {
|
|
if source == nil {
|
|
return nil
|
|
}
|
|
clone := *source
|
|
clone.Domains = append([]string(nil), source.Domains...)
|
|
clone.AccessHosts = append([]string(nil), source.AccessHosts...)
|
|
clone.TrustedProxyCIDRs = append([]string(nil), source.TrustedProxyCIDRs...)
|
|
clone.PluginAllowList = append([]string(nil), source.PluginAllowList...)
|
|
if source.Tables != nil {
|
|
clone.Tables = make(map[string]string, len(source.Tables))
|
|
for key, value := range source.Tables {
|
|
clone.Tables[key] = value
|
|
}
|
|
}
|
|
if source.TablesInitData != nil {
|
|
clone.TablesInitData = make(map[string]string, len(source.TablesInitData))
|
|
for key, value := range source.TablesInitData {
|
|
clone.TablesInitData[key] = value
|
|
}
|
|
}
|
|
return &clone
|
|
}
|
|
|
|
type MailPorts struct {
|
|
SMTP int
|
|
IMAP int
|
|
POP3 int
|
|
}
|
|
|
|
type SMTPRuntimeSettings struct {
|
|
Relay SMTPConfig
|
|
Password string
|
|
VerificationEnabled bool
|
|
ExternalRecoveryEnabled bool
|
|
}
|
|
|
|
func CurrentSMTPRuntimeSettings() (SMTPRuntimeSettings, error) {
|
|
settings := SMTPRuntimeSettings{}
|
|
if manager := CurrentManager(); manager != nil {
|
|
runtime := manager.Snapshot()
|
|
if runtime.Generation != 0 {
|
|
settings.Relay = runtime.SMTP.Relay
|
|
settings.ExternalRecoveryEnabled = runtime.Email.Policy.ExternalRecoveryEnabled
|
|
if settings.Relay.Username != "" {
|
|
vault, err := credential.NewVault(ROOT_PATH)
|
|
if err != nil {
|
|
return SMTPRuntimeSettings{}, fmt.Errorf("create SMTP credential vault: %w", err)
|
|
}
|
|
password, err := OpenStoredSecret(vault, settings.Relay.Password)
|
|
if err != nil {
|
|
return SMTPRuntimeSettings{}, fmt.Errorf("open SMTP relay password: %w", err)
|
|
}
|
|
settings.Password = password
|
|
}
|
|
settings.VerificationEnabled = settings.Relay.VerificationEnabled
|
|
return settings, nil
|
|
}
|
|
}
|
|
|
|
cfg := CurrentConfig()
|
|
if cfg == nil {
|
|
return SMTPRuntimeSettings{}, fmt.Errorf("runtime configuration is unavailable")
|
|
}
|
|
settings.Relay = SMTPConfig{
|
|
RelayHost: cfg.SMTPHost,
|
|
RelayPort: cfg.SMTPPort,
|
|
Username: cfg.SMTPUsername,
|
|
Security: cfg.SMTPSecurity,
|
|
From: cfg.SMTPFrom,
|
|
FromDomain: cfg.SMTPFromDomain,
|
|
VerificationEnabled: cfg.VerificationEnabled,
|
|
LoginPolicy: cfg.LoginPolicy,
|
|
}
|
|
settings.Password = cfg.SMTPPassword
|
|
settings.VerificationEnabled = cfg.VerificationEnabled
|
|
settings.ExternalRecoveryEnabled = cfg.ExternalRecoveryEnabled
|
|
return settings, nil
|
|
}
|
|
|
|
func CurrentMailPorts() MailPorts {
|
|
ports := MailPorts{SMTP: 25, IMAP: 143, POP3: 110}
|
|
manager := CurrentManager()
|
|
if manager == nil {
|
|
return ports
|
|
}
|
|
runtime := manager.Snapshot()
|
|
if runtime.Generation == 0 {
|
|
return ports
|
|
}
|
|
if runtime.Email.Policy.SMTPPort > 0 && runtime.Email.Policy.SMTPPort <= 65535 {
|
|
ports.SMTP = runtime.Email.Policy.SMTPPort
|
|
}
|
|
if runtime.Email.Policy.IMAPPort > 0 && runtime.Email.Policy.IMAPPort <= 65535 {
|
|
ports.IMAP = runtime.Email.Policy.IMAPPort
|
|
}
|
|
if runtime.Email.Policy.POP3Port > 0 && runtime.Email.Policy.POP3Port <= 65535 {
|
|
ports.POP3 = runtime.Email.Policy.POP3Port
|
|
}
|
|
return ports
|
|
}
|
|
func cloneHTTPConfig(source HTTPConfig) HTTPConfig {
|
|
clone := source
|
|
clone.TrustedProxyCIDRs = append([]string(nil), source.TrustedProxyCIDRs...)
|
|
return clone
|
|
}
|