继续更新 update 门户站点界面和功能
build-winui / winui (push) Has been cancelled

This commit is contained in:
QWQLwToo
2026-06-26 20:17:34 +08:00
parent f525e5f3ba
commit 2513eb2903
68 changed files with 5586 additions and 3195 deletions
@@ -4,6 +4,7 @@ import (
"encoding/json"
"os"
"path/filepath"
"runtime"
"strconv"
"strings"
)
@@ -67,16 +68,28 @@ func Load() (*Config, error) {
}
cfg := defaults(root)
path := firstNonEmpty(os.Getenv("YMHUT_UNIFIED_CONFIG"), filepath.Join(root, "config.json"))
var rawConfig []byte
loaded := false
if data, err := os.ReadFile(path); err == nil {
if err := json.Unmarshal(data, cfg); err != nil {
return nil, err
}
cfg.Initialized = true
rawConfig = data
loaded = true
}
cfg.BaseDir = root
cfg.ConfigPath = path
if loaded {
sanitizeNonPortablePaths(cfg)
}
applyEnv(cfg)
normalize(root, cfg)
if loaded && shouldRewriteRelativeConfig(rawConfig) {
if err := Save(cfg); err != nil {
return nil, err
}
}
return cfg, nil
}
@@ -333,13 +346,110 @@ func Save(cfg *Config) error {
if err := os.MkdirAll(filepath.Dir(cfg.ConfigPath), 0o750); err != nil {
return err
}
data, err := json.MarshalIndent(cfg, "", " ")
persisted := cfg.relativeCopy()
data, err := json.MarshalIndent(persisted, "", " ")
if err != nil {
return err
}
return os.WriteFile(cfg.ConfigPath, data, 0o600)
}
func (cfg *Config) relativeCopy() Config {
next := *cfg
base := cfg.BaseDir
next.BaseDir = "."
next.StorageDir = relativePath(base, cfg.StorageDir)
next.DataDir = relativePath(base, cfg.DataDir)
next.UpdatePublicDir = relativePath(base, cfg.UpdatePublicDir)
next.UpdateNoticeDir = relativePath(base, cfg.UpdateNoticeDir)
next.DownloadsDir = relativePath(base, cfg.DownloadsDir)
next.AdminWebDir = relativePath(base, cfg.AdminWebDir)
next.PortalWebDir = relativePath(base, cfg.PortalWebDir)
next.SetupWebDir = relativePath(base, cfg.SetupWebDir)
next.LegacyUpdateDir = relativePath(base, cfg.LegacyUpdateDir)
next.LegacyFeedbackDir = relativePath(base, cfg.LegacyFeedbackDir)
next.LegacyUpdateNoticeDir = relativePath(base, cfg.LegacyUpdateNoticeDir)
next.Database.SQLitePath = relativePath(base, cfg.Database.SQLitePath)
return next
}
func relativePath(base, value string) string {
if strings.TrimSpace(value) == "" || strings.HasPrefix(strings.ToLower(value), "file:") {
return value
}
rel, err := filepath.Rel(base, value)
if err != nil || rel == "" {
return value
}
if strings.HasPrefix(rel, "..") {
return filepath.ToSlash(rel)
}
return filepath.ToSlash(rel)
}
func sanitizeNonPortablePaths(cfg *Config) {
if runtime.GOOS == "windows" {
return
}
for _, target := range []*string{
&cfg.StorageDir,
&cfg.DataDir,
&cfg.UpdatePublicDir,
&cfg.UpdateNoticeDir,
&cfg.DownloadsDir,
&cfg.AdminWebDir,
&cfg.PortalWebDir,
&cfg.SetupWebDir,
&cfg.LegacyUpdateDir,
&cfg.LegacyFeedbackDir,
&cfg.LegacyUpdateNoticeDir,
&cfg.Database.SQLitePath,
} {
if isWindowsAbsolutePath(*target) {
*target = ""
}
}
}
func shouldRewriteRelativeConfig(data []byte) bool {
var payload any
if len(data) == 0 || json.Unmarshal(data, &payload) != nil {
return false
}
return containsAbsolutePath(payload)
}
func containsAbsolutePath(value any) bool {
switch typed := value.(type) {
case map[string]any:
for _, item := range typed {
if containsAbsolutePath(item) {
return true
}
}
case []any:
for _, item := range typed {
if containsAbsolutePath(item) {
return true
}
}
case string:
return filepath.IsAbs(typed) || isWindowsAbsolutePath(typed)
}
return false
}
func isWindowsAbsolutePath(value string) bool {
value = strings.TrimSpace(value)
if len(value) >= 3 {
drive := value[0]
if ((drive >= 'A' && drive <= 'Z') || (drive >= 'a' && drive <= 'z')) && value[1] == ':' && (value[2] == '\\' || value[2] == '/') {
return true
}
}
return strings.HasPrefix(value, `\\`)
}
func absPath(base, value string) string {
if strings.TrimSpace(value) == "" {
return value