gophertype/pkg/settings/settings.go

159 lines
3.3 KiB
Go

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)
}