Files
gosentry/src/storage/store_config.go
T
mixeme 0e6b3bcecf refactor: split source files that exceeded the ~300-line ceiling
Mechanical moves only — operations, store, history_view, and settings_view
are now split along their existing seams so every file stays within the 250+20%
guideline. Document the new layout in ARCHITECTURE.md and close the ROADMAP item.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-07 22:22:08 +03:00

73 lines
2.6 KiB
Go

package storage
import (
"encoding/json"
"errors"
"os"
"path/filepath"
"strings"
"gitea.mixdep.ru/mix/gosentry/src/domain"
)
func loadOrCreateConfig(paths Paths) (domain.Config, error) {
// Defaults favor a portable installation: settings and jobs begin next to the
// executable, while logs are grouped under a dedicated subdirectory.
config := domain.DefaultConfig()
if _, err := os.Stat(paths.ConfigPath); errors.Is(err, os.ErrNotExist) {
return config, writeJSON(paths.ConfigPath, config)
}
data, err := os.ReadFile(paths.ConfigPath)
if err != nil {
return domain.Config{}, err
}
// Clearing the default first keeps "the file sets jobs_file" distinguishable
// from "the file omits it", which the jobs_dir migration below depends on.
// The fallbacks restore a value in either case.
config.JobsFile = ""
if err := json.Unmarshal(data, &config); err != nil {
return domain.Config{}, err
}
// A config written before the setting named a file carries jobs_dir instead
// of jobs_file. Keep its meaning by appending the fixed name that version
// used, then drop the old key so the file is rewritten in the current shape.
if strings.TrimSpace(config.JobsFile) == "" && strings.TrimSpace(config.JobsDir) != "" {
config.JobsFile = filepath.Join(config.JobsDir, JobsFileName)
}
config.JobsDir = ""
if strings.TrimSpace(config.JobsFile) == "" {
// Empty paths are treated as missing values rather than intentional root
// directories. This avoids accidentally writing jobs to unexpected places.
config.JobsFile = JobsFileName
}
if strings.TrimSpace(config.LogsDir) == "" {
config.LogsDir = "logs"
}
// MaxLogFiles and MaxLogAgeDays are deliberately not normalized: 0 means
// "keep everything" (see runner.CleanupLogs), not a missing value, so
// backfilling it here would make that choice impossible to persist. A config
// written before either field existed already carries 0 from json.Unmarshal
// leaving the DefaultConfig() value in config untouched, so old files still
// pick up 100 / 30 without an explicit backfill.
if config.ExecutionMode == "" {
config.ExecutionMode = domain.ExecutionModeParallel
}
if config.OverlapPolicy == "" {
config.OverlapPolicy = domain.OverlapPolicySkip
}
// DefaultTimeoutSeconds is deliberately not normalized: 0 is a meaningful
// value ("no timeout"), not a missing one, so backfilling it here would make
// the setting impossible to persist. Negative values are rejected by
// app.validateConfig before they can be saved.
if config.Theme == "" {
config.Theme = domain.ThemeGoSentry
}
if config.Theme == "default" {
config.Theme = domain.ThemeSystem
}
return config, nil
}