A clean, Markdown-based publishing platform made for writers. Write together, and build a community. https://writefreely.org
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 
 
 

306 lignes
8.2 KiB

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