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.
 
 
 
 
 

185 lines
3.9 KiB

  1. package writefreely
  2. import (
  3. "database/sql"
  4. "flag"
  5. "fmt"
  6. _ "github.com/go-sql-driver/mysql"
  7. "net/http"
  8. "os"
  9. "os/signal"
  10. "syscall"
  11. "github.com/gorilla/mux"
  12. "github.com/gorilla/sessions"
  13. "github.com/writeas/web-core/log"
  14. "github.com/writeas/writefreely/config"
  15. "github.com/writeas/writefreely/page"
  16. )
  17. const (
  18. staticDir = "static/"
  19. serverSoftware = "Write Freely"
  20. softwareURL = "https://writefreely.org"
  21. softwareVer = "0.1"
  22. )
  23. var (
  24. debugging bool
  25. )
  26. type app struct {
  27. router *mux.Router
  28. db *datastore
  29. cfg *config.Config
  30. keys *keychain
  31. sessionStore *sessions.CookieStore
  32. }
  33. func pageForReq(app *app, r *http.Request) page.StaticPage {
  34. p := page.StaticPage{
  35. AppCfg: app.cfg.App,
  36. Path: r.URL.Path,
  37. Version: "v" + softwareVer,
  38. }
  39. // Add user information, if given
  40. var u *User
  41. accessToken := r.FormValue("t")
  42. if accessToken != "" {
  43. userID := app.db.GetUserID(accessToken)
  44. if userID != -1 {
  45. var err error
  46. u, err = app.db.GetUserByID(userID)
  47. if err == nil {
  48. p.Username = u.Username
  49. }
  50. }
  51. } else {
  52. u = getUserSession(app, r)
  53. if u != nil {
  54. p.Username = u.Username
  55. }
  56. }
  57. return p
  58. }
  59. var shttp = http.NewServeMux()
  60. func Serve() {
  61. debugPtr := flag.Bool("debug", false, "Enables debug logging.")
  62. createConfig := flag.Bool("create-config", false, "Creates a basic configuration and exits")
  63. doConfig := flag.Bool("config", false, "Run the configuration process")
  64. flag.Parse()
  65. debugging = *debugPtr
  66. if *createConfig {
  67. log.Info("Creating configuration...")
  68. c := config.New()
  69. log.Info("Saving configuration...")
  70. err := config.Save(c)
  71. if err != nil {
  72. log.Error("Unable to save configuration: %v", err)
  73. os.Exit(1)
  74. }
  75. os.Exit(0)
  76. } else if *doConfig {
  77. err := config.Configure()
  78. if err != nil {
  79. log.Error("Unable to configure: %v", err)
  80. os.Exit(1)
  81. }
  82. os.Exit(0)
  83. }
  84. log.Info("Initializing...")
  85. log.Info("Loading configuration...")
  86. cfg, err := config.Load()
  87. if err != nil {
  88. log.Error("Unable to load configuration: %v", err)
  89. os.Exit(1)
  90. }
  91. app := &app{
  92. cfg: cfg,
  93. }
  94. app.cfg.Server.Dev = *debugPtr
  95. initTemplates()
  96. // Load keys
  97. log.Info("Loading encryption keys...")
  98. err = initKeys(app)
  99. if err != nil {
  100. log.Error("\n%s\n", err)
  101. }
  102. // Initialize modules
  103. app.sessionStore = initSession(app)
  104. // Check database configuration
  105. if app.cfg.Database.User == "" || app.cfg.Database.Password == "" {
  106. log.Error("Database user or password not set.")
  107. os.Exit(1)
  108. }
  109. if app.cfg.Database.Host == "" {
  110. app.cfg.Database.Host = "localhost"
  111. }
  112. if app.cfg.Database.Database == "" {
  113. app.cfg.Database.Database = "writeas"
  114. }
  115. log.Info("Connecting to database...")
  116. db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=true", app.cfg.Database.User, app.cfg.Database.Password, app.cfg.Database.Host, app.cfg.Database.Port, app.cfg.Database.Database))
  117. if err != nil {
  118. log.Error("\n%s\n", err)
  119. os.Exit(1)
  120. }
  121. app.db = &datastore{db}
  122. defer shutdown(app)
  123. app.db.SetMaxOpenConns(50)
  124. r := mux.NewRouter()
  125. handler := NewHandler(app)
  126. handler.SetErrorPages(&ErrorPages{
  127. NotFound: pages["404-general.tmpl"],
  128. Gone: pages["410.tmpl"],
  129. InternalServerError: pages["500.tmpl"],
  130. Blank: pages["blank.tmpl"],
  131. })
  132. // Handle app routes
  133. initRoutes(handler, r, app.cfg, app.db)
  134. // Handle static files
  135. fs := http.FileServer(http.Dir(staticDir))
  136. shttp.Handle("/", fs)
  137. r.PathPrefix("/").Handler(fs)
  138. // Handle shutdown
  139. c := make(chan os.Signal, 2)
  140. signal.Notify(c, os.Interrupt, syscall.SIGTERM)
  141. go func() {
  142. <-c
  143. log.Info("Shutting down...")
  144. shutdown(app)
  145. log.Info("Done.")
  146. os.Exit(0)
  147. }()
  148. // Start web application server
  149. http.Handle("/", r)
  150. log.Info("Serving on http://localhost:%d\n", app.cfg.Server.Port)
  151. log.Info("---")
  152. http.ListenAndServe(fmt.Sprintf(":%d", app.cfg.Server.Port), nil)
  153. }
  154. func shutdown(app *app) {
  155. log.Info("Closing database connection...")
  156. app.db.Close()
  157. }