A clean, Markdown-based publishing platform made for writers. Write together, and build a community. https://writefreely.org
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.
 
 
 
 
 

277 lines
7.6 KiB

  1. /*
  2. * Copyright © 2018-2021 A Bunch Tell LLC.
  3. *
  4. * This file is part of WriteFreely.
  5. *
  6. * WriteFreely is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU Affero General Public License, included
  8. * in the LICENSE file in this source code package.
  9. */
  10. // Package config holds and assists in the configuration of a writefreely instance.
  11. package config
  12. import (
  13. "strings"
  14. "gopkg.in/ini.v1"
  15. )
  16. const (
  17. // FileName is the default configuration file name
  18. FileName = "config.ini"
  19. UserNormal UserType = "user"
  20. UserAdmin = "admin"
  21. )
  22. type (
  23. UserType string
  24. // ServerCfg holds values that affect how the HTTP server runs
  25. ServerCfg struct {
  26. HiddenHost string `ini:"hidden_host"`
  27. Port int `ini:"port"`
  28. Bind string `ini:"bind"`
  29. TLSCertPath string `ini:"tls_cert_path"`
  30. TLSKeyPath string `ini:"tls_key_path"`
  31. Autocert bool `ini:"autocert"`
  32. TemplatesParentDir string `ini:"templates_parent_dir"`
  33. StaticParentDir string `ini:"static_parent_dir"`
  34. PagesParentDir string `ini:"pages_parent_dir"`
  35. KeysParentDir string `ini:"keys_parent_dir"`
  36. HashSeed string `ini:"hash_seed"`
  37. GopherPort int `ini:"gopher_port"`
  38. Dev bool `ini:"-"`
  39. }
  40. // DatabaseCfg holds values that determine how the application connects to a datastore
  41. DatabaseCfg struct {
  42. Type string `ini:"type"`
  43. FileName string `ini:"filename"`
  44. User string `ini:"username"`
  45. Password string `ini:"password"`
  46. Database string `ini:"database"`
  47. Host string `ini:"host"`
  48. Port int `ini:"port"`
  49. TLS bool `ini:"tls"`
  50. }
  51. WriteAsOauthCfg struct {
  52. ClientID string `ini:"client_id"`
  53. ClientSecret string `ini:"client_secret"`
  54. AuthLocation string `ini:"auth_location"`
  55. TokenLocation string `ini:"token_location"`
  56. InspectLocation string `ini:"inspect_location"`
  57. CallbackProxy string `ini:"callback_proxy"`
  58. CallbackProxyAPI string `ini:"callback_proxy_api"`
  59. }
  60. GitlabOauthCfg struct {
  61. ClientID string `ini:"client_id"`
  62. ClientSecret string `ini:"client_secret"`
  63. Host string `ini:"host"`
  64. DisplayName string `ini:"display_name"`
  65. CallbackProxy string `ini:"callback_proxy"`
  66. CallbackProxyAPI string `ini:"callback_proxy_api"`
  67. }
  68. GiteaOauthCfg struct {
  69. ClientID string `ini:"client_id"`
  70. ClientSecret string `ini:"client_secret"`
  71. Host string `ini:"host"`
  72. DisplayName string `ini:"display_name"`
  73. CallbackProxy string `ini:"callback_proxy"`
  74. CallbackProxyAPI string `ini:"callback_proxy_api"`
  75. }
  76. SlackOauthCfg struct {
  77. ClientID string `ini:"client_id"`
  78. ClientSecret string `ini:"client_secret"`
  79. TeamID string `ini:"team_id"`
  80. CallbackProxy string `ini:"callback_proxy"`
  81. CallbackProxyAPI string `ini:"callback_proxy_api"`
  82. }
  83. GenericOauthCfg struct {
  84. ClientID string `ini:"client_id"`
  85. ClientSecret string `ini:"client_secret"`
  86. Host string `ini:"host"`
  87. DisplayName string `ini:"display_name"`
  88. CallbackProxy string `ini:"callback_proxy"`
  89. CallbackProxyAPI string `ini:"callback_proxy_api"`
  90. TokenEndpoint string `ini:"token_endpoint"`
  91. InspectEndpoint string `ini:"inspect_endpoint"`
  92. AuthEndpoint string `ini:"auth_endpoint"`
  93. Scope string `ini:"scope"`
  94. AllowDisconnect bool `ini:"allow_disconnect"`
  95. MapUserID string `ini:"map_user_id"`
  96. MapUsername string `ini:"map_username"`
  97. MapDisplayName string `ini:"map_display_name"`
  98. MapEmail string `ini:"map_email"`
  99. }
  100. // AppCfg holds values that affect how the application functions
  101. AppCfg struct {
  102. SiteName string `ini:"site_name"`
  103. SiteDesc string `ini:"site_description"`
  104. Host string `ini:"host"`
  105. // Site appearance
  106. Theme string `ini:"theme"`
  107. Editor string `ini:"editor"`
  108. JSDisabled bool `ini:"disable_js"`
  109. WebFonts bool `ini:"webfonts"`
  110. Landing string `ini:"landing"`
  111. SimpleNav bool `ini:"simple_nav"`
  112. WFModesty bool `ini:"wf_modesty"`
  113. // Site functionality
  114. Chorus bool `ini:"chorus"`
  115. Forest bool `ini:"forest"` // The admin cares about the forest, not the trees. Hide unnecessary technical info.
  116. DisableDrafts bool `ini:"disable_drafts"`
  117. // Users
  118. SingleUser bool `ini:"single_user"`
  119. OpenRegistration bool `ini:"open_registration"`
  120. OpenDeletion bool `ini:"open_deletion"`
  121. MinUsernameLen int `ini:"min_username_len"`
  122. MaxBlogs int `ini:"max_blogs"`
  123. // Options for public instances
  124. // Federation
  125. Federation bool `ini:"federation"`
  126. PublicStats bool `ini:"public_stats"`
  127. Monetization bool `ini:"monetization"`
  128. NotesOnly bool `ini:"notes_only"`
  129. // Access
  130. Private bool `ini:"private"`
  131. // Additional functions
  132. LocalTimeline bool `ini:"local_timeline"`
  133. UserInvites string `ini:"user_invites"`
  134. // Defaults
  135. DefaultVisibility string `ini:"default_visibility"`
  136. // Check for Updates
  137. UpdateChecks bool `ini:"update_checks"`
  138. // Disable password authentication if use only Oauth
  139. DisablePasswordAuth bool `ini:"disable_password_auth"`
  140. }
  141. // Config holds the complete configuration for running a writefreely instance
  142. Config struct {
  143. Server ServerCfg `ini:"server"`
  144. Database DatabaseCfg `ini:"database"`
  145. App AppCfg `ini:"app"`
  146. SlackOauth SlackOauthCfg `ini:"oauth.slack"`
  147. WriteAsOauth WriteAsOauthCfg `ini:"oauth.writeas"`
  148. GitlabOauth GitlabOauthCfg `ini:"oauth.gitlab"`
  149. GiteaOauth GiteaOauthCfg `ini:"oauth.gitea"`
  150. GenericOauth GenericOauthCfg `ini:"oauth.generic"`
  151. }
  152. )
  153. // New creates a new Config with sane defaults
  154. func New() *Config {
  155. c := &Config{
  156. Server: ServerCfg{
  157. Port: 8080,
  158. Bind: "localhost", /* IPV6 support when not using localhost? */
  159. },
  160. App: AppCfg{
  161. Host: "http://localhost:8080",
  162. Theme: "write",
  163. WebFonts: true,
  164. SingleUser: true,
  165. MinUsernameLen: 3,
  166. MaxBlogs: 1,
  167. Federation: true,
  168. PublicStats: true,
  169. },
  170. }
  171. c.UseMySQL(true)
  172. return c
  173. }
  174. // UseMySQL resets the Config's Database to use default values for a MySQL setup.
  175. func (cfg *Config) UseMySQL(fresh bool) {
  176. cfg.Database.Type = "mysql"
  177. if fresh {
  178. cfg.Database.Host = "localhost"
  179. cfg.Database.Port = 3306
  180. }
  181. }
  182. // UseSQLite resets the Config's Database to use default values for a SQLite setup.
  183. func (cfg *Config) UseSQLite(fresh bool) {
  184. cfg.Database.Type = "sqlite3"
  185. if fresh {
  186. cfg.Database.FileName = "writefreely.db"
  187. }
  188. }
  189. // IsSecureStandalone returns whether or not the application is running as a
  190. // standalone server with TLS enabled.
  191. func (cfg *Config) IsSecureStandalone() bool {
  192. return cfg.Server.Port == 443 && cfg.Server.TLSCertPath != "" && cfg.Server.TLSKeyPath != ""
  193. }
  194. func (ac *AppCfg) LandingPath() string {
  195. if !strings.HasPrefix(ac.Landing, "/") {
  196. return "/" + ac.Landing
  197. }
  198. return ac.Landing
  199. }
  200. func (ac AppCfg) SignupPath() string {
  201. if !ac.OpenRegistration {
  202. return ""
  203. }
  204. if ac.Chorus || ac.Private || (ac.Landing != "" && ac.Landing != "/") {
  205. return "/signup"
  206. }
  207. return "/"
  208. }
  209. // Load reads the given configuration file, then parses and returns it as a Config.
  210. func Load(fname string) (*Config, error) {
  211. if fname == "" {
  212. fname = FileName
  213. }
  214. cfg, err := ini.Load(fname)
  215. if err != nil {
  216. return nil, err
  217. }
  218. // Parse INI file
  219. uc := &Config{}
  220. err = cfg.MapTo(uc)
  221. if err != nil {
  222. return nil, err
  223. }
  224. return uc, nil
  225. }
  226. // Save writes the given Config to the given file.
  227. func Save(uc *Config, fname string) error {
  228. cfg := ini.Empty()
  229. err := ini.ReflectFrom(cfg, uc)
  230. if err != nil {
  231. return err
  232. }
  233. if fname == "" {
  234. fname = FileName
  235. }
  236. return cfg.SaveTo(fname)
  237. }