server.go 5.8 KB

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