Noah
383b5d7591
* Add the Age Gate middleware for NSFW sites. * Cache thumbnail images from blog entries. * Implement the user-root properly for loading web assets.
150 lines
3.2 KiB
Go
150 lines
3.2 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
|
|
|
|
// 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,
|
|
}
|
|
|
|
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)
|
|
}
|