server.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. package server
  2. import (
  3. "context"
  4. "database/sql"
  5. "encoding/json"
  6. "fmt"
  7. "time"
  8. "github.com/pkg/errors"
  9. "github.com/usememos/memos/api"
  10. "github.com/usememos/memos/server/profile"
  11. "github.com/usememos/memos/store"
  12. "github.com/usememos/memos/store/db"
  13. "github.com/labstack/echo/v4"
  14. "github.com/labstack/echo/v4/middleware"
  15. )
  16. type Server struct {
  17. e *echo.Echo
  18. db *sql.DB
  19. ID string
  20. Profile *profile.Profile
  21. Store *store.Store
  22. }
  23. func NewServer(ctx context.Context, profile *profile.Profile) (*Server, error) {
  24. e := echo.New()
  25. e.Debug = true
  26. e.HideBanner = true
  27. e.HidePort = true
  28. db := db.NewDB(profile)
  29. if err := db.Open(ctx); err != nil {
  30. return nil, errors.Wrap(err, "cannot open db")
  31. }
  32. s := &Server{
  33. e: e,
  34. db: db.DBInstance,
  35. Profile: profile,
  36. }
  37. storeInstance := store.New(db.DBInstance, profile)
  38. s.Store = storeInstance
  39. e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
  40. Format: `{"time":"${time_rfc3339}",` +
  41. `"method":"${method}","uri":"${uri}",` +
  42. `"status":${status},"error":"${error}"}` + "\n",
  43. }))
  44. e.Use(middleware.Gzip())
  45. e.Use(middleware.CSRFWithConfig(middleware.CSRFConfig{
  46. Skipper: s.defaultAuthSkipper,
  47. TokenLookup: "cookie:_csrf",
  48. }))
  49. e.Use(middleware.CORS())
  50. e.Use(middleware.SecureWithConfig(middleware.SecureConfig{
  51. Skipper: defaultGetRequestSkipper,
  52. XSSProtection: "1; mode=block",
  53. ContentTypeNosniff: "nosniff",
  54. XFrameOptions: "SAMEORIGIN",
  55. HSTSPreloadEnabled: false,
  56. }))
  57. e.Use(middleware.TimeoutWithConfig(middleware.TimeoutConfig{
  58. ErrorMessage: "Request timeout",
  59. Timeout: 30 * time.Second,
  60. }))
  61. serverID, err := s.getSystemServerID(ctx)
  62. if err != nil {
  63. return nil, err
  64. }
  65. s.ID = serverID
  66. embedFrontend(e)
  67. secret := "usememos"
  68. if profile.Mode == "prod" {
  69. secret, err = s.getSystemSecretSessionName(ctx)
  70. if err != nil {
  71. return nil, err
  72. }
  73. }
  74. rootGroup := e.Group("")
  75. s.registerRSSRoutes(rootGroup)
  76. publicGroup := e.Group("/o")
  77. publicGroup.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
  78. return JWTMiddleware(s, next, secret)
  79. })
  80. registerGetterPublicRoutes(publicGroup)
  81. s.registerResourcePublicRoutes(publicGroup)
  82. apiGroup := e.Group("/api")
  83. apiGroup.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
  84. return JWTMiddleware(s, next, secret)
  85. })
  86. s.registerSystemRoutes(apiGroup)
  87. s.registerAuthRoutes(apiGroup, secret)
  88. s.registerUserRoutes(apiGroup)
  89. s.registerMemoRoutes(apiGroup)
  90. s.registerShortcutRoutes(apiGroup)
  91. s.registerResourceRoutes(apiGroup)
  92. s.registerTagRoutes(apiGroup)
  93. s.registerStorageRoutes(apiGroup)
  94. s.registerIdentityProviderRoutes(apiGroup)
  95. s.registerOpenAIRoutes(apiGroup)
  96. return s, nil
  97. }
  98. func (s *Server) Start(ctx context.Context) error {
  99. if err := s.createServerStartActivity(ctx); err != nil {
  100. return errors.Wrap(err, "failed to create activity")
  101. }
  102. return s.e.Start(fmt.Sprintf(":%d", s.Profile.Port))
  103. }
  104. func (s *Server) Shutdown(ctx context.Context) {
  105. ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
  106. defer cancel()
  107. // Shutdown echo server
  108. if err := s.e.Shutdown(ctx); err != nil {
  109. fmt.Printf("failed to shutdown server, error: %v\n", err)
  110. }
  111. // Close database connection
  112. if err := s.db.Close(); err != nil {
  113. fmt.Printf("failed to close database, error: %v\n", err)
  114. }
  115. fmt.Printf("memos stopped properly\n")
  116. }
  117. func (s *Server) createServerStartActivity(ctx context.Context) error {
  118. payload := api.ActivityServerStartPayload{
  119. ServerID: s.ID,
  120. Profile: s.Profile,
  121. }
  122. payloadBytes, err := json.Marshal(payload)
  123. if err != nil {
  124. return errors.Wrap(err, "failed to marshal activity payload")
  125. }
  126. activity, err := s.Store.CreateActivity(ctx, &api.ActivityCreate{
  127. CreatorID: api.UnknownID,
  128. Type: api.ActivityServerStart,
  129. Level: api.ActivityInfo,
  130. Payload: string(payloadBytes),
  131. })
  132. if err != nil || activity == nil {
  133. return errors.Wrap(err, "failed to create activity")
  134. }
  135. return err
  136. }