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.
 
 

150 lines
3.2 KiB

  1. package settings
  2. import (
  3. "crypto/rand"
  4. "encoding/base64"
  5. "encoding/json"
  6. "fmt"
  7. "io"
  8. "io/ioutil"
  9. "os"
  10. "path/filepath"
  11. "git.kirsle.net/apps/gophertype/pkg/console"
  12. "git.kirsle.net/apps/gophertype/pkg/session"
  13. )
  14. // Current holds the current app settings. When the app settings have never
  15. // been initialized, this struct holds the default values with a random secret
  16. // key. The config is not saved to DB until you call Save() on it.
  17. var Current = Load()
  18. // UserRoot is the folder path to the user web files.
  19. var UserRoot string
  20. // Spec singleton holds the app configuration.
  21. type Spec struct {
  22. // Sets to `true` when the site's initial setup has run and an admin created.
  23. Initialized bool
  24. // Site information
  25. Title string
  26. Description string
  27. Email string // primary email for notifications
  28. NSFW bool
  29. BaseURL string
  30. // Blog settings
  31. PostsPerPage int
  32. // Mail settings
  33. MailEnabled bool
  34. MailSender string
  35. MailHost string
  36. MailPort int
  37. MailUsername string
  38. MailPassword string
  39. // Security
  40. SecretKey string
  41. }
  42. // Filename is the path where the settings.json file is saved to.
  43. var (
  44. configFilename = ".settings.json"
  45. configPath string
  46. )
  47. // SetFilename sets the config file path, to be inside the user root.
  48. func SetFilename(userRoot string) error {
  49. path := filepath.Join(userRoot, configFilename)
  50. configPath = path
  51. // Initialize it for the first time?
  52. if _, err := os.Stat(path); os.IsNotExist(err) {
  53. console.Warn("No settings.json found; initializing a new settings file at %s", path)
  54. fh, err := os.Create(path)
  55. if err != nil {
  56. return fmt.Errorf("couldn't initialize settings.json at %s: %s", path, err)
  57. }
  58. Current.ToJSON(fh)
  59. fh.Close()
  60. return nil
  61. }
  62. // Read the stored file instead.
  63. fh, err := os.Open(path)
  64. if err != nil {
  65. return fmt.Errorf("couldn't read settings.json from %s: %s", path, err)
  66. }
  67. defer fh.Close()
  68. data, err := ioutil.ReadAll(fh)
  69. if err != nil {
  70. return fmt.Errorf("couldn't read data from settings.json at %s: %s", path, err)
  71. }
  72. spec, err := FromJSON(data)
  73. if err != nil {
  74. return fmt.Errorf("settings.json parse error from %s: %s", path, err)
  75. }
  76. Current = spec
  77. UserRoot = userRoot
  78. session.SetSecretKey([]byte(Current.SecretKey))
  79. return nil
  80. }
  81. // Load gets or creates the App Settings.
  82. func Load() Spec {
  83. var s = Spec{
  84. Title: "Untitled Site",
  85. Description: "Just another web blog.",
  86. SecretKey: MakeSecretKey(),
  87. PostsPerPage: 20,
  88. }
  89. session.SetSecretKey([]byte(s.SecretKey))
  90. return s
  91. }
  92. // FromJSON loads a settings JSON from bytes.
  93. func FromJSON(data []byte) (Spec, error) {
  94. var s Spec
  95. err := json.Unmarshal(data, &s)
  96. return s, err
  97. }
  98. // ToJSON converts the settings spec to JSON.
  99. func (s Spec) ToJSON(w io.Writer) error {
  100. enc := json.NewEncoder(w)
  101. enc.SetIndent("", "\t")
  102. err := enc.Encode(s)
  103. return err
  104. }
  105. // Save the settings to DB.
  106. func (s Spec) Save() error {
  107. Current = s
  108. session.SetSecretKey([]byte(s.SecretKey))
  109. fh, err := os.Create(configPath)
  110. if err != nil {
  111. return err
  112. }
  113. defer fh.Close()
  114. return s.ToJSON(fh)
  115. }
  116. // MakeSecretKey generates a secret key for signing HTTP cookies.
  117. func MakeSecretKey() string {
  118. keyLength := 32
  119. b := make([]byte, keyLength)
  120. rand.Read(b)
  121. return base64.URLEncoding.EncodeToString(b)
  122. }