Smail/backend/config/deployment_state.go
smail d7837de561
Some checks are pending
Security Checks / security (push) Waiting to run
保存模拟部署迁移与最新构建校验阶段成果
2026-09-08 18:33:21 +08:00

97 lines
3.3 KiB
Go

package config
import (
"errors"
"os"
"path/filepath"
fileutil "git.pcux.cn/xuans/Smail/utils/file"
"git.pcux.cn/xuans/Smail/utils/runtimepath"
)
// InitializeDeployment 仅对全新安装启用新布局,不触碰已有部署或生成替代密钥。
func InitializeDeployment(root string) error {
for _, name := range []string{"config/config.json", "data/credential.key", "config/credential.key"} {
if _, err := os.Lstat(filepath.Join(root, filepath.FromSlash(name))); err == nil {
return nil
} else if !os.IsNotExist(err) {
return err
}
}
db, err := NewTransactionStore(root, RuntimeValidationOptions{}).openState()
if err != nil {
return err
}
return db.Close()
}
// ImportDeploymentHistory 在停服迁移期间导入旧历史原始字节,原目录保持不动。
func ImportDeploymentHistory(source, target string, validation RuntimeValidationOptions) error {
if !runtimepath.DeploymentLayout(target) {
return errors.New("目标部署密钥尚未迁移到 ssl")
}
if entries, err := os.ReadDir(filepath.Join(source, "data", "transaction")); err == nil && len(entries) != 0 {
return ErrConfigRecoveryRequired
} else if err != nil && !os.IsNotExist(err) {
return err
}
store := NewTransactionStore(target, validation)
unlock, err := store.lockState()
if err != nil {
return err
}
defer unlock()
entries, err := os.ReadDir(filepath.Join(source, "data", "backups-v2"))
if os.IsNotExist(err) {
return nil
}
if err != nil {
return err
}
for _, entry := range entries {
directory := filepath.Join(source, "data", "backups-v2", entry.Name())
_, manifest, err := loadConfigBackup(directory, validation)
if err != nil {
return err
}
files := make(map[string][]byte)
for _, name := range transactionFileNames() {
files[name], err = fileutil.ReadRegularFile(filepath.Join(directory, "files", name), maxConfigFileSize)
if err != nil {
return err
}
}
if err := validateStateFiles(files, manifest.Files); err != nil {
return err
}
if err := store.saveState("backup", manifest.ID, stateRecord{Backup: &manifest, Original: files}); err != nil {
return err
}
}
_, err = store.stateBackups()
return err
}
// NormalizeDeploymentReferences 通过正常事务更新路径及摘要,不能直接编辑配置 JSON。
func NormalizeDeploymentReferences(root string, validation RuntimeValidationOptions) error {
loader := NewConfigLoader(root, ConfigLoaderOptions{Validation: validation})
current, err := loader.Load()
if err != nil {
return err
}
if !runtimepath.DeploymentLayout(root) {
return ErrManagerStorage
}
candidate := cloneRuntimeConfig(current)
ssl := &candidate.SSL.TLS
ssl.DefaultDirectory = runtimepath.SSLReference(root, ssl.DefaultDirectory)
ssl.CustomDirectory = runtimepath.SSLReference(root, ssl.CustomDirectory)
ssl.CertificateRef = runtimepath.SSLReference(root, ssl.CertificateRef)
ssl.PrivateKeyRef = runtimepath.SSLReference(root, ssl.PrivateKeyRef)
if candidate.SSL.TLS.CertificateRef == current.SSL.TLS.CertificateRef && candidate.SSL.TLS.PrivateKeyRef == current.SSL.TLS.PrivateKeyRef && candidate.SSL.TLS.DefaultDirectory == current.SSL.TLS.DefaultDirectory && candidate.SSL.TLS.CustomDirectory == current.SSL.TLS.CustomDirectory {
return nil
}
setCandidateGeneration(&candidate, current.Generation+1)
return NewTransactionStore(root, validation).Commit(current, candidate)
}