Add standalone desktop workflow
This commit is contained in:
@@ -26,15 +26,17 @@ const (
|
||||
type SourceFolder struct {
|
||||
Path string `json:"path"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Root bool `json:"root,omitempty"`
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
ReserveFreeGB float64 `json:"reserve_free_gb"`
|
||||
DestFolder string `json:"dest_folder"`
|
||||
Sources []SourceFolder `json:"sources"`
|
||||
OverwriteMode OverwriteMode `json:"overwrite_mode"`
|
||||
FileSelectMode FileSelectMode `json:"file_select_mode"`
|
||||
AutoCopy bool `json:"auto_copy"`
|
||||
MediaPath string `json:"media_path"`
|
||||
ReserveFreeGB float64 `json:"reserve_free_gb"`
|
||||
DestFolder string `json:"dest_folder"`
|
||||
Sources []SourceFolder `json:"sources"`
|
||||
OverwriteMode OverwriteMode `json:"overwrite_mode"`
|
||||
FileSelectMode FileSelectMode `json:"file_select_mode"`
|
||||
AutoCopy bool `json:"auto_copy"`
|
||||
FileReplicaCounts map[string]int `json:"file_replica_counts,omitempty"`
|
||||
DiskReplicaFiles map[string][]string `json:"disk_replica_files,omitempty"`
|
||||
}
|
||||
@@ -67,6 +69,8 @@ func Load(path string) (*Config, error) {
|
||||
} else {
|
||||
cfg.DestFolder = defaults().DestFolder
|
||||
}
|
||||
cfg.MediaPath = NormalizeMediaPath(cfg.MediaPath)
|
||||
cfg.Sources = NormalizeSources(cfg.Sources, cfg.MediaPath)
|
||||
return &cfg, nil
|
||||
}
|
||||
|
||||
@@ -86,6 +90,26 @@ func Save(path string, cfg *Config) error {
|
||||
}
|
||||
|
||||
func (c *Config) Validate() error {
|
||||
c.MediaPath = NormalizeMediaPath(c.MediaPath)
|
||||
if c.MediaPath != "" {
|
||||
info, err := os.Stat(c.MediaPath)
|
||||
if err != nil {
|
||||
return errors.New("media_path is not accessible")
|
||||
}
|
||||
if !info.IsDir() {
|
||||
return errors.New("media_path must be a directory")
|
||||
}
|
||||
}
|
||||
c.Sources = NormalizeSources(c.Sources, c.MediaPath)
|
||||
for _, source := range c.Sources {
|
||||
info, err := os.Stat(source.Path)
|
||||
if err != nil {
|
||||
return errors.New("source path is not accessible: " + source.Path)
|
||||
}
|
||||
if !info.IsDir() {
|
||||
return errors.New("source path must be a directory: " + source.Path)
|
||||
}
|
||||
}
|
||||
if c.ReserveFreeGB < 0 {
|
||||
return errors.New("reserve_free_gb must be >= 0")
|
||||
}
|
||||
@@ -105,6 +129,46 @@ func (c *Config) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NormalizeMediaPath(value string) string {
|
||||
value = strings.TrimSpace(value)
|
||||
if value == "" {
|
||||
return ""
|
||||
}
|
||||
return filepath.Clean(value)
|
||||
}
|
||||
|
||||
func NormalizeSources(items []SourceFolder, mediaPath string) []SourceFolder {
|
||||
seen := make(map[string]struct{}, len(items))
|
||||
result := make([]SourceFolder, 0, len(items))
|
||||
for _, item := range items {
|
||||
path := normalizeSourcePath(item.Path, mediaPath)
|
||||
if path == "" {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[path]; ok {
|
||||
continue
|
||||
}
|
||||
seen[path] = struct{}{}
|
||||
result = append(result, SourceFolder{
|
||||
Path: path,
|
||||
Enabled: item.Enabled,
|
||||
Root: item.Root,
|
||||
})
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func normalizeSourcePath(value string, mediaPath string) string {
|
||||
value = strings.TrimSpace(value)
|
||||
if value == "" {
|
||||
return ""
|
||||
}
|
||||
if !filepath.IsAbs(value) && mediaPath != "" {
|
||||
value = filepath.Join(mediaPath, value)
|
||||
}
|
||||
return filepath.Clean(value)
|
||||
}
|
||||
|
||||
func NormalizeDestFolder(value string) (string, error) {
|
||||
value = strings.TrimSpace(value)
|
||||
if value == "" {
|
||||
|
||||
Reference in New Issue
Block a user