feat: add proxy labels and default profiles
This commit is contained in:
@@ -68,6 +68,27 @@ func DefaultPath() (string, error) {
|
||||
return defaultPathForExecutable(executable)
|
||||
}
|
||||
|
||||
// DefaultInstanceUserDataRoot returns the portable root for new browser
|
||||
// profiles. It resolves the actual executable location and never uses cwd.
|
||||
func DefaultInstanceUserDataRoot() (string, error) {
|
||||
executable, err := os.Executable()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("%w: resolve executable", ErrDefaultConfigUnavailable)
|
||||
}
|
||||
return defaultInstanceUserDataRootForExecutable(executable)
|
||||
}
|
||||
|
||||
// DefaultInstanceUserDataDir returns a unique-instance profile path below the
|
||||
// executable-side user_data_dirs root. It only computes a path; it does not
|
||||
// create directories or validate their write permissions.
|
||||
func DefaultInstanceUserDataDir(instanceID string) (string, error) {
|
||||
root, err := DefaultInstanceUserDataRoot()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return defaultInstanceUserDataDir(root, instanceID)
|
||||
}
|
||||
|
||||
// LegacyPath is the pre-portable configuration location. It is read only when
|
||||
// the executable-side config does not exist yet.
|
||||
func LegacyPath() (string, error) {
|
||||
@@ -107,6 +128,23 @@ func defaultPathForExecutable(executable string) (string, error) {
|
||||
return filepath.Join(filepath.Dir(filepath.Clean(path)), "config.json"), nil
|
||||
}
|
||||
|
||||
func defaultInstanceUserDataRootForExecutable(executable string) (string, error) {
|
||||
path := strings.TrimSpace(executable)
|
||||
if path == "" || !filepath.IsAbs(path) {
|
||||
return "", fmt.Errorf("%w: executable path must be absolute", ErrDefaultConfigUnavailable)
|
||||
}
|
||||
return filepath.Join(filepath.Dir(filepath.Clean(path)), "user_data_dirs"), nil
|
||||
}
|
||||
|
||||
func defaultInstanceUserDataDir(root, instanceID string) (string, error) {
|
||||
root = strings.TrimSpace(root)
|
||||
instanceID = strings.TrimSpace(instanceID)
|
||||
if root == "" || !filepath.IsAbs(root) || instanceID == "" || filepath.Base(instanceID) != instanceID || instanceID == "." {
|
||||
return "", fmt.Errorf("%w: invalid default instance directory", ErrDefaultConfigUnavailable)
|
||||
}
|
||||
return filepath.Join(filepath.Clean(root), instanceID), nil
|
||||
}
|
||||
|
||||
func openDefaultAt(target, legacy string) (*Store, error) {
|
||||
store, targetExists, err := defaultStore(target)
|
||||
if err != nil || targetExists {
|
||||
@@ -303,28 +341,35 @@ func normalizeProxyConfig(value *File) error {
|
||||
return errors.New("config is required")
|
||||
}
|
||||
byID := make(map[string]ProxyProfile, len(value.Proxies))
|
||||
names := make(map[string]struct{}, len(value.Proxies))
|
||||
names := make([]string, 0, len(value.Proxies))
|
||||
servers := make(map[string]struct{}, len(value.Proxies))
|
||||
for i := range value.Proxies {
|
||||
profile := &value.Proxies[i]
|
||||
profile.ID = strings.TrimSpace(profile.ID)
|
||||
profile.Name = strings.TrimSpace(profile.Name)
|
||||
if profile.ID == "" || profile.Name == "" {
|
||||
name, err := domain.NormalizeProxyName(profile.Name)
|
||||
if profile.ID == "" || err != nil {
|
||||
return errors.New("invalid proxy configuration")
|
||||
}
|
||||
profile.Name = name
|
||||
if _, exists := byID[profile.ID]; exists {
|
||||
return errors.New("duplicate proxy configuration")
|
||||
}
|
||||
nameKey := strings.ToLower(profile.Name)
|
||||
if _, exists := names[nameKey]; exists {
|
||||
return errors.New("duplicate proxy configuration")
|
||||
for _, existing := range names {
|
||||
if strings.EqualFold(existing, profile.Name) {
|
||||
return errors.New("duplicate proxy configuration")
|
||||
}
|
||||
}
|
||||
server, err := domain.NormalizeProxyServer(profile.Server)
|
||||
if err != nil || server == "" {
|
||||
return errors.New("invalid proxy configuration")
|
||||
}
|
||||
if _, exists := servers[server]; exists {
|
||||
return errors.New("duplicate proxy configuration")
|
||||
}
|
||||
profile.Server = server
|
||||
byID[profile.ID] = *profile
|
||||
names[nameKey] = struct{}{}
|
||||
names = append(names, profile.Name)
|
||||
servers[server] = struct{}{}
|
||||
}
|
||||
|
||||
legacyByServer := make(map[string]string, len(value.Proxies))
|
||||
@@ -351,10 +396,12 @@ func normalizeProxyConfig(value *File) error {
|
||||
proxyID := legacyByServer[server]
|
||||
if proxyID == "" {
|
||||
proxyID = nextLegacyProxyID(byID)
|
||||
profile := ProxyProfile{ID: proxyID, Name: "导入代理 " + strconv.Itoa(len(value.Proxies)+1), Server: server}
|
||||
profile := ProxyProfile{ID: proxyID, Name: nextLegacyProxyName(names), Server: server}
|
||||
value.Proxies = append(value.Proxies, profile)
|
||||
byID[proxyID] = profile
|
||||
legacyByServer[server] = proxyID
|
||||
names = append(names, profile.Name)
|
||||
servers[server] = struct{}{}
|
||||
}
|
||||
instance.ProxyID = proxyID
|
||||
instance.Launch.ProxyServer = ""
|
||||
@@ -371,6 +418,22 @@ func nextLegacyProxyID(existing map[string]ProxyProfile) string {
|
||||
}
|
||||
}
|
||||
|
||||
func nextLegacyProxyName(existing []string) string {
|
||||
for index := 1; ; index++ {
|
||||
candidate := "导入代理 " + strconv.Itoa(index)
|
||||
duplicate := false
|
||||
for _, name := range existing {
|
||||
if strings.EqualFold(name, candidate) {
|
||||
duplicate = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !duplicate {
|
||||
return candidate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func replace(target, temp string) error {
|
||||
backup := target + ".bak"
|
||||
_, statErr := os.Stat(target)
|
||||
|
||||
Reference in New Issue
Block a user