A web blog and personal homepage engine written in Go.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

144 lines
3.1 KiB

package settings
import (
// 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()
// 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
// 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)
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
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(),
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
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)
return base64.URLEncoding.EncodeToString(b)