feat: add portable directory safeguards

This commit is contained in:
QiuSW
2026-07-28 00:12:29 +08:00
parent 92091a6690
commit 733954ddc8
15 changed files with 914 additions and 95 deletions
+33 -8
View File
@@ -29,8 +29,8 @@ import (
const version = "0.1.0-dev"
var (
openDefaultConfig = config.OpenDefault
defaultInstanceUserDataRoot = config.DefaultInstanceUserDataRoot
openDefaultConfig = config.OpenDefault
defaultPortableDirectories = config.DefaultPortableDirectories
)
func main() {
@@ -105,11 +105,11 @@ func runWindow(logger *slog.Logger) {
window.Option(app.Title("Chub"), app.Size(unit.Dp(1100), unit.Dp(720)))
theme := material.NewTheme()
shell := ui.NewShell(theme)
if root, err := defaultInstanceUserDataRoot(); err != nil {
logger.Error("resolve default instance user data root", "error", err)
shell.ReportStartupIssue("无法确定程序目录中的默认 User Data Dir;新建实例时请选择其他绝对路径。")
if directories, err := defaultPortableDirectories(); err != nil {
logger.Error("resolve portable directories", "error", err)
shell.ReportStartupIssue("无法确定程序目录中的默认 User Data Dir 和日志目录;请检查程序位置后重试。")
} else {
shell.SetDefaultInstanceUserDataRoot(root)
shell.SetPortableDirectoryDefaults(directories.DefaultDir, directories.LogDir)
}
directoryPicker := files.NewDirectoryPicker()
shell.OnChooseDirectory(directoryPicker.ChooseDirectory, window.Invalidate)
@@ -131,6 +131,7 @@ func runWindow(logger *slog.Logger) {
portAllocator: cdp,
proxyResolver: proxies,
monitor: exitMonitor,
dataPreparer: files.NewInstanceDataPreparer(),
}.Start, window.Invalidate)
shell.OnStopInstance(instanceStopper{launcher: launcher, managedProfiles: launcher, monitor: exitMonitor}.Stop, window.Invalidate)
dataRemover := files.NewInstanceDataRemover()
@@ -179,7 +180,24 @@ func runWindow(logger *slog.Logger) {
logger.Error("load default configuration", "error", err)
shell.ReportStartupIssue("程序目录中的 config.json 无法读取;请恢复有效配置后重试。")
} else {
shell.SetSettings(ui.SettingsState{ChromePath: saved.Settings.ChromePath, EdgePath: saved.Settings.EdgePath, DefaultDir: saved.Settings.DefaultDir, LogDir: saved.Settings.LogDir, RemoteDebugStartPort: saved.Settings.RemoteDebugStartPort, CloseOnExit: saved.Settings.CloseOnExit})
logPreparer := files.NewDirectoryPreparer()
if err := logPreparer.PrepareDirectory(context.Background(), saved.Settings.LogDir); err != nil {
logger.Error("prepare log directory", "error", err)
shell.ReportStartupIssue("日志目录不可写;请在设置中选择可写目录后重试。")
} else if fileLogger, closer, logErr := logging.OpenFile(saved.Settings.LogDir); logErr != nil {
logger.Error("open diagnostic log", "error", logErr)
shell.ReportStartupIssue("日志目录不可写;请在设置中选择可写目录后重试。")
} else {
logger = fileLogger
defer closer.Close()
logger.Info("diagnostic log opened", "version", version)
}
if directories, directoriesErr := store.PortableDirectories(); directoriesErr != nil {
logger.Error("resolve portable settings directories", "error", directoriesErr)
} else {
shell.SetPortableDirectoryDefaults(directories.DefaultDir, directories.LogDir)
}
shell.SetSettings(ui.SettingsState{ChromePath: saved.Settings.ChromePath, EdgePath: saved.Settings.EdgePath, DefaultDir: saved.Settings.DefaultDir, DefaultDirMode: string(saved.Settings.DefaultDirSource), LogDir: saved.Settings.LogDir, LogDirMode: string(saved.Settings.LogDirSource), RemoteDebugStartPort: saved.Settings.RemoteDebugStartPort, CloseOnExit: saved.Settings.CloseOnExit})
proxies.Set(saved.Proxies)
shell.SetProxies(proxyOptions(saved.Proxies))
rows := make([]ui.InstanceRow, 0, len(saved.Instances))
@@ -188,7 +206,7 @@ func runWindow(logger *slog.Logger) {
}
shell.SetInstances(rows)
shell.OnSave(func(value ui.SettingsState) {
saved.Settings = config.Settings{ChromePath: value.ChromePath, EdgePath: value.EdgePath, DefaultDir: value.DefaultDir, LogDir: value.LogDir, RemoteDebugStartPort: value.RemoteDebugStartPort, CloseOnExit: value.CloseOnExit}
saved.Settings = config.Settings{ChromePath: value.ChromePath, EdgePath: value.EdgePath, DefaultDir: value.DefaultDir, DefaultDirSource: config.DirectorySource(value.DefaultDirMode), LogDir: value.LogDir, LogDirSource: config.DirectorySource(value.LogDirMode), RemoteDebugStartPort: value.RemoteDebugStartPort, CloseOnExit: value.CloseOnExit}
go saveSettings(logger, store, configSnapshot(saved))
})
shell.OnInstancesChanged(func(rows []ui.InstanceRow) {
@@ -372,6 +390,7 @@ type instanceStarter struct {
portAllocator browser.RemoteDebugPortAllocator
proxyResolver proxyServerResolver
monitor managedExitObserver
dataPreparer files.InstanceDataPreparer
}
type instanceStatusRefresher struct {
@@ -561,6 +580,12 @@ func (s instanceStarter) Start(ctx context.Context, row ui.InstanceRow, settings
return ui.InstanceStartOutcome{PID: use.PID, Source: use.Source}, &browser.ProfileOccupiedError{UserDataDir: row.UserDataDir, PID: use.PID}
}
}
if s.dataPreparer == nil {
return ui.InstanceStartOutcome{}, errors.New("User Data Dir 预检服务尚未准备好")
}
if err := s.dataPreparer.PrepareInstanceDataDir(ctx, row.UserDataDir); err != nil {
return ui.InstanceStartOutcome{}, fmt.Errorf("User Data Dir 不可写,请选择其他目录:%w", err)
}
proxyServer := ""
if row.ProxyID != "" {
if s.proxyResolver == nil {