package settings import ( "crypto/rand" "encoding/base64" "encoding/json" "fmt" "io" "io/ioutil" "os" "path/filepath" "git.kirsle.net/apps/gophertype/pkg/console" "git.kirsle.net/apps/gophertype/pkg/session" ) // Current holds the current app settings. When the app settings have never // been initialized, this struct holds the default values with a random secret // key. The config is not saved to DB until you call Save() on it. var Current = Load() // UserRoot is the folder path to the user web files. var UserRoot string // Spec singleton holds the app configuration. type Spec struct { // Sets to `true` when the site's initial setup has run and an admin created. Initialized bool // Site information Title string Description string Email string // primary email for notifications NSFW bool BaseURL string // Blog settings PostsPerPage int // Mail settings MailEnabled bool MailSender string MailHost string MailPort int MailUsername string MailPassword string // Redis settings RedisEnabled bool RedisHost string RedisPort int RedisDB int // Security SecretKey string } // Filename is the path where the settings.json file is saved to. var ( configFilename = ".settings.json" configPath string ) // SetFilename sets the config file path, to be inside the user root. func SetFilename(userRoot string) error { path := filepath.Join(userRoot, configFilename) configPath = path // Initialize it for the first time? if _, err := os.Stat(path); os.IsNotExist(err) { console.Warn("No settings.json found; initializing a new settings file at %s", path) fh, err := os.Create(path) if err != nil { return fmt.Errorf("couldn't initialize settings.json at %s: %s", path, err) } Current.ToJSON(fh) fh.Close() return nil } // Read the stored file instead. fh, err := os.Open(path) if err != nil { return fmt.Errorf("couldn't read settings.json from %s: %s", path, err) } defer fh.Close() data, err := ioutil.ReadAll(fh) if err != nil { return fmt.Errorf("couldn't read data from settings.json at %s: %s", path, err) } spec, err := FromJSON(data) if err != nil { return fmt.Errorf("settings.json parse error from %s: %s", path, err) } Current = spec UserRoot = userRoot session.SetSecretKey([]byte(Current.SecretKey)) return nil } // Load gets or creates the App Settings. func Load() Spec { var s = Spec{ Title: "Untitled Site", Description: "Just another web blog.", SecretKey: MakeSecretKey(), PostsPerPage: 20, RedisHost: "localhost", RedisPort: 6379, } session.SetSecretKey([]byte(s.SecretKey)) return s } // FromJSON loads a settings JSON from bytes. func FromJSON(data []byte) (Spec, error) { var s Spec err := json.Unmarshal(data, &s) return s, err } // ToJSON converts the settings spec to JSON. func (s Spec) ToJSON(w io.Writer) error { enc := json.NewEncoder(w) enc.SetIndent("", "\t") err := enc.Encode(s) return err } // Save the settings to DB. func (s Spec) Save() error { Current = s session.SetSecretKey([]byte(s.SecretKey)) fh, err := os.Create(configPath) if err != nil { return err } defer fh.Close() return s.ToJSON(fh) } // MakeSecretKey generates a secret key for signing HTTP cookies. func MakeSecretKey() string { keyLength := 32 b := make([]byte, keyLength) rand.Read(b) return base64.URLEncoding.EncodeToString(b) }