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.
 
 
 
 
 

204 lines
6.2 KiB

  1. /*
  2. * Copyright © 2018 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 writefreely
  11. import (
  12. "fmt"
  13. "github.com/gogits/gogs/pkg/tool"
  14. "github.com/gorilla/mux"
  15. "github.com/writeas/impart"
  16. "github.com/writeas/web-core/auth"
  17. "github.com/writeas/web-core/log"
  18. "github.com/writeas/writefreely/config"
  19. "net/http"
  20. "runtime"
  21. "strconv"
  22. "time"
  23. )
  24. var (
  25. appStartTime = time.Now()
  26. sysStatus systemStatus
  27. )
  28. type systemStatus struct {
  29. Uptime string
  30. NumGoroutine int
  31. // General statistics.
  32. MemAllocated string // bytes allocated and still in use
  33. MemTotal string // bytes allocated (even if freed)
  34. MemSys string // bytes obtained from system (sum of XxxSys below)
  35. Lookups uint64 // number of pointer lookups
  36. MemMallocs uint64 // number of mallocs
  37. MemFrees uint64 // number of frees
  38. // Main allocation heap statistics.
  39. HeapAlloc string // bytes allocated and still in use
  40. HeapSys string // bytes obtained from system
  41. HeapIdle string // bytes in idle spans
  42. HeapInuse string // bytes in non-idle span
  43. HeapReleased string // bytes released to the OS
  44. HeapObjects uint64 // total number of allocated objects
  45. // Low-level fixed-size structure allocator statistics.
  46. // Inuse is bytes used now.
  47. // Sys is bytes obtained from system.
  48. StackInuse string // bootstrap stacks
  49. StackSys string
  50. MSpanInuse string // mspan structures
  51. MSpanSys string
  52. MCacheInuse string // mcache structures
  53. MCacheSys string
  54. BuckHashSys string // profiling bucket hash table
  55. GCSys string // GC metadata
  56. OtherSys string // other system allocations
  57. // Garbage collector statistics.
  58. NextGC string // next run in HeapAlloc time (bytes)
  59. LastGC string // last run in absolute time (ns)
  60. PauseTotalNs string
  61. PauseNs string // circular buffer of recent GC pause times, most recent at [(NumGC+255)%256]
  62. NumGC uint32
  63. }
  64. func handleViewAdminDash(app *app, u *User, w http.ResponseWriter, r *http.Request) error {
  65. updateAppStats()
  66. p := struct {
  67. *UserPage
  68. SysStatus systemStatus
  69. Config config.AppCfg
  70. Message, ConfigMessage string
  71. AboutPage, PrivacyPage string
  72. }{
  73. UserPage: NewUserPage(app, r, u, "Admin", nil),
  74. SysStatus: sysStatus,
  75. Config: app.cfg.App,
  76. Message: r.FormValue("m"),
  77. ConfigMessage: r.FormValue("cm"),
  78. }
  79. var err error
  80. p.AboutPage, err = getAboutPage(app)
  81. if err != nil {
  82. return err
  83. }
  84. p.PrivacyPage, _, err = getPrivacyPage(app)
  85. if err != nil {
  86. return err
  87. }
  88. showUserPage(w, "admin", p)
  89. return nil
  90. }
  91. func handleAdminUpdateSite(app *app, u *User, w http.ResponseWriter, r *http.Request) error {
  92. vars := mux.Vars(r)
  93. id := vars["page"]
  94. // Validate
  95. if id != "about" && id != "privacy" {
  96. return impart.HTTPError{http.StatusNotFound, "No such page."}
  97. }
  98. // Update page
  99. m := ""
  100. err := app.db.UpdateDynamicContent(id, r.FormValue("content"))
  101. if err != nil {
  102. m = "?m=" + err.Error()
  103. }
  104. return impart.HTTPError{http.StatusFound, "/admin" + m + "#page-" + id}
  105. }
  106. func handleAdminUpdateConfig(app *app, u *User, w http.ResponseWriter, r *http.Request) error {
  107. app.cfg.App.SiteName = r.FormValue("site_name")
  108. app.cfg.App.SiteDesc = r.FormValue("site_desc")
  109. app.cfg.App.OpenRegistration = r.FormValue("open_registration") == "on"
  110. mul, err := strconv.Atoi(r.FormValue("min_username_len"))
  111. if err == nil {
  112. app.cfg.App.MinUsernameLen = mul
  113. }
  114. mb, err := strconv.Atoi(r.FormValue("max_blogs"))
  115. if err == nil {
  116. app.cfg.App.MaxBlogs = mb
  117. }
  118. app.cfg.App.Federation = r.FormValue("federation") == "on"
  119. app.cfg.App.PublicStats = r.FormValue("public_stats") == "on"
  120. app.cfg.App.Private = r.FormValue("private") == "on"
  121. app.cfg.App.LocalTimeline = r.FormValue("local_timeline") == "on"
  122. if app.cfg.App.LocalTimeline && app.timeline == nil {
  123. log.Info("Initializing local timeline...")
  124. initLocalTimeline(app)
  125. }
  126. m := "?cm=Configuration+saved."
  127. err = config.Save(app.cfg, app.cfgFile)
  128. if err != nil {
  129. m = "?cm=" + err.Error()
  130. }
  131. return impart.HTTPError{http.StatusFound, "/admin" + m + "#config"}
  132. }
  133. func updateAppStats() {
  134. sysStatus.Uptime = tool.TimeSincePro(appStartTime)
  135. m := new(runtime.MemStats)
  136. runtime.ReadMemStats(m)
  137. sysStatus.NumGoroutine = runtime.NumGoroutine()
  138. sysStatus.MemAllocated = tool.FileSize(int64(m.Alloc))
  139. sysStatus.MemTotal = tool.FileSize(int64(m.TotalAlloc))
  140. sysStatus.MemSys = tool.FileSize(int64(m.Sys))
  141. sysStatus.Lookups = m.Lookups
  142. sysStatus.MemMallocs = m.Mallocs
  143. sysStatus.MemFrees = m.Frees
  144. sysStatus.HeapAlloc = tool.FileSize(int64(m.HeapAlloc))
  145. sysStatus.HeapSys = tool.FileSize(int64(m.HeapSys))
  146. sysStatus.HeapIdle = tool.FileSize(int64(m.HeapIdle))
  147. sysStatus.HeapInuse = tool.FileSize(int64(m.HeapInuse))
  148. sysStatus.HeapReleased = tool.FileSize(int64(m.HeapReleased))
  149. sysStatus.HeapObjects = m.HeapObjects
  150. sysStatus.StackInuse = tool.FileSize(int64(m.StackInuse))
  151. sysStatus.StackSys = tool.FileSize(int64(m.StackSys))
  152. sysStatus.MSpanInuse = tool.FileSize(int64(m.MSpanInuse))
  153. sysStatus.MSpanSys = tool.FileSize(int64(m.MSpanSys))
  154. sysStatus.MCacheInuse = tool.FileSize(int64(m.MCacheInuse))
  155. sysStatus.MCacheSys = tool.FileSize(int64(m.MCacheSys))
  156. sysStatus.BuckHashSys = tool.FileSize(int64(m.BuckHashSys))
  157. sysStatus.GCSys = tool.FileSize(int64(m.GCSys))
  158. sysStatus.OtherSys = tool.FileSize(int64(m.OtherSys))
  159. sysStatus.NextGC = tool.FileSize(int64(m.NextGC))
  160. sysStatus.LastGC = fmt.Sprintf("%.1fs", float64(time.Now().UnixNano()-int64(m.LastGC))/1000/1000/1000)
  161. sysStatus.PauseTotalNs = fmt.Sprintf("%.1fs", float64(m.PauseTotalNs)/1000/1000/1000)
  162. sysStatus.PauseNs = fmt.Sprintf("%.3fs", float64(m.PauseNs[(m.NumGC+255)%256])/1000/1000/1000)
  163. sysStatus.NumGC = m.NumGC
  164. }
  165. func adminResetPassword(app *app, u *User, newPass string) error {
  166. hashedPass, err := auth.HashPass([]byte(newPass))
  167. if err != nil {
  168. return impart.HTTPError{http.StatusInternalServerError, fmt.Sprintf("Could not create password hash: %v", err)}
  169. }
  170. err = app.db.ChangePassphrase(u.ID, true, "", hashedPass)
  171. if err != nil {
  172. return impart.HTTPError{http.StatusInternalServerError, fmt.Sprintf("Could not update passphrase: %v", err)}
  173. }
  174. return nil
  175. }