server.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. package server
  2. import (
  3. "context"
  4. "fmt"
  5. "net/http"
  6. "strings"
  7. "time"
  8. "github.com/google/uuid"
  9. "github.com/labstack/echo/v4"
  10. "github.com/labstack/echo/v4/middleware"
  11. "github.com/pkg/errors"
  12. echoSwagger "github.com/swaggo/echo-swagger"
  13. apiv1 "github.com/usememos/memos/api/v1"
  14. apiv2 "github.com/usememos/memos/api/v2"
  15. "github.com/usememos/memos/plugin/telegram"
  16. "github.com/usememos/memos/server/integration"
  17. "github.com/usememos/memos/server/profile"
  18. "github.com/usememos/memos/server/service/backup"
  19. "github.com/usememos/memos/server/service/metric"
  20. versionchecker "github.com/usememos/memos/server/service/version_checker"
  21. "github.com/usememos/memos/store"
  22. )
  23. type Server struct {
  24. e *echo.Echo
  25. ID string
  26. Secret string
  27. Profile *profile.Profile
  28. Store *store.Store
  29. // API services.
  30. apiV2Service *apiv2.APIV2Service
  31. // Asynchronous runners.
  32. backupRunner *backup.BackupRunner
  33. telegramBot *telegram.Bot
  34. }
  35. func NewServer(ctx context.Context, profile *profile.Profile, store *store.Store) (*Server, error) {
  36. e := echo.New()
  37. e.Debug = true
  38. e.HideBanner = true
  39. e.HidePort = true
  40. s := &Server{
  41. e: e,
  42. Store: store,
  43. Profile: profile,
  44. // Asynchronous runners.
  45. telegramBot: telegram.NewBotWithHandler(integration.NewTelegramHandler(store)),
  46. }
  47. if profile.Driver == "sqlite" {
  48. s.backupRunner = backup.NewBackupRunner(store)
  49. }
  50. e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
  51. Format: `{"time":"${time_rfc3339}","latency":"${latency_human}",` +
  52. `"method":"${method}","uri":"${uri}",` +
  53. `"status":${status},"error":"${error}"}` + "\n",
  54. }))
  55. e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
  56. Skipper: grpcRequestSkipper,
  57. AllowOrigins: []string{"*"},
  58. AllowMethods: []string{http.MethodGet, http.MethodHead, http.MethodPut, http.MethodPatch, http.MethodPost, http.MethodDelete},
  59. }))
  60. e.Use(middleware.TimeoutWithConfig(middleware.TimeoutConfig{
  61. Skipper: timeoutSkipper,
  62. Timeout: 30 * time.Second,
  63. }))
  64. serverID, err := s.getSystemServerID(ctx)
  65. if err != nil {
  66. return nil, errors.Wrap(err, "failed to retrieve system server ID")
  67. }
  68. s.ID = serverID
  69. // Serve frontend.
  70. embedFrontend(e)
  71. // Serve swagger in dev/demo mode.
  72. if profile.Mode == "dev" || profile.Mode == "demo" {
  73. e.GET("/api/*", echoSwagger.WrapHandler)
  74. }
  75. secret := "usememos"
  76. if profile.Mode == "prod" {
  77. secret, err = s.getSystemSecretSessionName(ctx)
  78. if err != nil {
  79. return nil, errors.Wrap(err, "failed to retrieve system secret session name")
  80. }
  81. }
  82. s.Secret = secret
  83. // Register healthz endpoint.
  84. e.GET("/healthz", func(c echo.Context) error {
  85. return c.String(http.StatusOK, "Service ready.")
  86. })
  87. // Register API v1 endpoints.
  88. rootGroup := e.Group("")
  89. apiV1Service := apiv1.NewAPIV1Service(s.Secret, profile, store, s.telegramBot)
  90. apiV1Service.Register(rootGroup)
  91. s.apiV2Service = apiv2.NewAPIV2Service(s.Secret, profile, store, s.Profile.Port+1)
  92. // Register gRPC gateway as api v2.
  93. if err := s.apiV2Service.RegisterGateway(ctx, e); err != nil {
  94. return nil, errors.Wrap(err, "failed to register gRPC gateway")
  95. }
  96. return s, nil
  97. }
  98. func (s *Server) Start(ctx context.Context) error {
  99. go versionchecker.NewVersionChecker(s.Store, s.Profile).Start(ctx)
  100. go s.telegramBot.Start(ctx)
  101. if s.backupRunner != nil {
  102. go s.backupRunner.Run(ctx)
  103. }
  104. metric.Enqueue("server start")
  105. return s.e.Start(fmt.Sprintf("%s:%d", s.Profile.Addr, s.Profile.Port))
  106. }
  107. func (s *Server) Shutdown(ctx context.Context) {
  108. ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
  109. defer cancel()
  110. // Shutdown echo server
  111. if err := s.e.Shutdown(ctx); err != nil {
  112. fmt.Printf("failed to shutdown server, error: %v\n", err)
  113. }
  114. // Close database connection
  115. if err := s.Store.Close(); err != nil {
  116. fmt.Printf("failed to close database, error: %v\n", err)
  117. }
  118. fmt.Printf("memos stopped properly\n")
  119. }
  120. func (s *Server) GetEcho() *echo.Echo {
  121. return s.e
  122. }
  123. func (s *Server) getSystemServerID(ctx context.Context) (string, error) {
  124. serverIDSetting, err := s.Store.GetSystemSetting(ctx, &store.FindSystemSetting{
  125. Name: apiv1.SystemSettingServerIDName.String(),
  126. })
  127. if err != nil {
  128. return "", err
  129. }
  130. if serverIDSetting == nil || serverIDSetting.Value == "" {
  131. serverIDSetting, err = s.Store.UpsertSystemSetting(ctx, &store.SystemSetting{
  132. Name: apiv1.SystemSettingServerIDName.String(),
  133. Value: uuid.NewString(),
  134. })
  135. if err != nil {
  136. return "", err
  137. }
  138. }
  139. return serverIDSetting.Value, nil
  140. }
  141. func (s *Server) getSystemSecretSessionName(ctx context.Context) (string, error) {
  142. secretSessionNameValue, err := s.Store.GetSystemSetting(ctx, &store.FindSystemSetting{
  143. Name: apiv1.SystemSettingSecretSessionName.String(),
  144. })
  145. if err != nil {
  146. return "", err
  147. }
  148. if secretSessionNameValue == nil || secretSessionNameValue.Value == "" {
  149. secretSessionNameValue, err = s.Store.UpsertSystemSetting(ctx, &store.SystemSetting{
  150. Name: apiv1.SystemSettingSecretSessionName.String(),
  151. Value: uuid.NewString(),
  152. })
  153. if err != nil {
  154. return "", err
  155. }
  156. }
  157. return secretSessionNameValue.Value, nil
  158. }
  159. func grpcRequestSkipper(c echo.Context) bool {
  160. return strings.HasPrefix(c.Request().URL.Path, "/memos.api.v2.")
  161. }
  162. func timeoutSkipper(c echo.Context) bool {
  163. if grpcRequestSkipper(c) {
  164. return true
  165. }
  166. // Skip timeout for blob upload which is frequently timed out.
  167. return c.Request().Method == http.MethodPost && c.Request().URL.Path == "/api/v1/resource/blob"
  168. }