verstak/internal/core/config/appconfig.go

140 lines
3.6 KiB
Go

package config
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
)
const (
AppConfigVersion = 1
ConfigDirName = "verstak"
AppConfigFileName = "config.json"
)
// AppConfig is the global application config stored at ~/.config/verstak/config.json.
type AppConfig struct {
Version int `json:"version"`
VaultPath string `json:"vault_path"`
Theme string `json:"theme"`
Language string `json:"language"`
EnabledTemplates []string `json:"enabled_templates"`
EnabledPlugins []string `json:"enabled_plugins"`
FirstRunCompleted bool `json:"first_run_completed"`
Window WindowConfig `json:"window,omitempty"`
Vault VaultAppConfig `json:"vault,omitempty"`
}
type WindowConfig struct {
Width int `json:"width,omitempty"`
Height int `json:"height,omitempty"`
}
// VaultAppConfig holds per-vault settings in the global config.
type VaultAppConfig struct {
VaultID string `json:"vault_id,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
Sync SyncSettings `json:"sync,omitempty"`
}
// SyncSettings holds sync configuration for the current vault.
type SyncSettings struct {
Enabled bool `json:"enabled"`
ServerURL string `json:"server_url"`
DeviceID string `json:"device_id"`
DeviceName string `json:"device_name"`
SyncInterval int `json:"sync_interval"`
LastStatus string `json:"last_status"`
LastSyncAt string `json:"last_sync_at"`
LastError string `json:"last_error,omitempty"`
}
func DefaultAppConfig() *AppConfig {
return &AppConfig{
Version: AppConfigVersion,
Theme: "system",
Language: "ru",
EnabledTemplates: []string{"folder", "project", "client", "document", "recipe"},
EnabledPlugins: []string{},
}
}
// ConfigDir returns ~/.config/verstak (or platform equivalent).
func ConfigDir() (string, error) {
cfgDir, err := os.UserConfigDir()
if err != nil {
return "", err
}
return filepath.Join(cfgDir, ConfigDirName), nil
}
// AppConfigPath returns the path to config.json.
func AppConfigPath() (string, error) {
dir, err := ConfigDir()
if err != nil {
return "", err
}
return filepath.Join(dir, AppConfigFileName), nil
}
// EnsureConfigDir creates the config directory if it doesn't exist.
func EnsureConfigDir() (string, error) {
dir, err := ConfigDir()
if err != nil {
return "", err
}
if err := os.MkdirAll(dir, 0o750); err != nil {
return "", err
}
return dir, nil
}
// LoadAppConfig reads the global config from disk. Returns nil if the file doesn't exist.
func LoadAppConfig() (*AppConfig, error) {
path, err := AppConfigPath()
if err != nil {
return nil, err
}
data, err := os.ReadFile(path)
if err != nil {
if os.IsNotExist(err) {
return nil, nil
}
return nil, fmt.Errorf("read config: %w", err)
}
if len(data) == 0 {
return nil, nil
}
var cfg AppConfig
if err := json.Unmarshal(data, &cfg); err != nil {
return nil, fmt.Errorf("parse config: %w", err)
}
return &cfg, nil
}
// SaveAppConfig writes the global config to disk.
func SaveAppConfig(cfg *AppConfig) error {
path, err := AppConfigPath()
if err != nil {
return err
}
if _, err := EnsureConfigDir(); err != nil {
return err
}
data, err := json.MarshalIndent(cfg, "", " ")
if err != nil {
return err
}
return os.WriteFile(path, data, 0o640)
}
// DefaultVaultPath returns the default vault location.
func DefaultVaultPath() (string, error) {
dir, err := ConfigDir()
if err != nil {
return "", err
}
return filepath.Join(dir, "vault"), nil
}