system.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. package v1
  2. import (
  3. "encoding/json"
  4. "net/http"
  5. "github.com/labstack/echo/v4"
  6. "go.uber.org/zap"
  7. "github.com/usememos/memos/internal/log"
  8. "github.com/usememos/memos/server/profile"
  9. "github.com/usememos/memos/store"
  10. )
  11. type SystemStatus struct {
  12. Host *User `json:"host"`
  13. Profile profile.Profile `json:"profile"`
  14. DBSize int64 `json:"dbSize"`
  15. // System settings
  16. // Allow sign up.
  17. AllowSignUp bool `json:"allowSignUp"`
  18. // Disable password login.
  19. DisablePasswordLogin bool `json:"disablePasswordLogin"`
  20. // Disable public memos.
  21. DisablePublicMemos bool `json:"disablePublicMemos"`
  22. // Max upload size.
  23. MaxUploadSizeMiB int `json:"maxUploadSizeMiB"`
  24. // Additional style.
  25. AdditionalStyle string `json:"additionalStyle"`
  26. // Additional script.
  27. AdditionalScript string `json:"additionalScript"`
  28. // Customized server profile, including server name and external url.
  29. CustomizedProfile CustomizedProfile `json:"customizedProfile"`
  30. // Storage service ID.
  31. StorageServiceID int32 `json:"storageServiceId"`
  32. // Local storage path.
  33. LocalStoragePath string `json:"localStoragePath"`
  34. // Memo display with updated timestamp.
  35. MemoDisplayWithUpdatedTs bool `json:"memoDisplayWithUpdatedTs"`
  36. }
  37. func (s *APIV1Service) registerSystemRoutes(g *echo.Group) {
  38. g.GET("/ping", s.PingSystem)
  39. g.GET("/status", s.GetSystemStatus)
  40. g.POST("/system/vacuum", s.ExecVacuum)
  41. }
  42. // PingSystem godoc
  43. //
  44. // @Summary Ping the system
  45. // @Tags system
  46. // @Produce json
  47. // @Success 200 {boolean} true "If succeed to ping the system"
  48. // @Router /api/v1/ping [GET]
  49. func (*APIV1Service) PingSystem(c echo.Context) error {
  50. return c.JSON(http.StatusOK, true)
  51. }
  52. // GetSystemStatus godoc
  53. //
  54. // @Summary Get system GetSystemStatus
  55. // @Tags system
  56. // @Produce json
  57. // @Success 200 {object} SystemStatus "System GetSystemStatus"
  58. // @Failure 401 {object} nil "Missing user in session | Unauthorized"
  59. // @Failure 500 {object} nil "Failed to find host user | Failed to find system setting list | Failed to unmarshal system setting customized profile value"
  60. // @Router /api/v1/status [GET]
  61. func (s *APIV1Service) GetSystemStatus(c echo.Context) error {
  62. ctx := c.Request().Context()
  63. systemStatus := SystemStatus{
  64. Profile: profile.Profile{
  65. Mode: s.Profile.Mode,
  66. Version: s.Profile.Version,
  67. },
  68. // Allow sign up by default.
  69. AllowSignUp: true,
  70. MaxUploadSizeMiB: 32,
  71. CustomizedProfile: CustomizedProfile{
  72. Name: "Memos",
  73. Locale: "en",
  74. Appearance: "system",
  75. },
  76. StorageServiceID: DefaultStorage,
  77. LocalStoragePath: "assets/{timestamp}_{filename}",
  78. }
  79. hostUserType := store.RoleHost
  80. hostUser, err := s.Store.GetUser(ctx, &store.FindUser{
  81. Role: &hostUserType,
  82. })
  83. if err != nil {
  84. return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find host user").SetInternal(err)
  85. }
  86. if hostUser != nil {
  87. systemStatus.Host = &User{ID: hostUser.ID}
  88. }
  89. systemSettingList, err := s.Store.ListSystemSettings(ctx, &store.FindSystemSetting{})
  90. if err != nil {
  91. return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find system setting list").SetInternal(err)
  92. }
  93. for _, systemSetting := range systemSettingList {
  94. if systemSetting.Name == SystemSettingServerIDName.String() || systemSetting.Name == SystemSettingSecretSessionName.String() || systemSetting.Name == SystemSettingTelegramBotTokenName.String() || systemSetting.Name == SystemSettingInstanceURLName.String() {
  95. continue
  96. }
  97. var baseValue any
  98. err := json.Unmarshal([]byte(systemSetting.Value), &baseValue)
  99. if err != nil {
  100. // Skip invalid value.
  101. continue
  102. }
  103. switch systemSetting.Name {
  104. case SystemSettingAllowSignUpName.String():
  105. systemStatus.AllowSignUp = baseValue.(bool)
  106. case SystemSettingDisablePasswordLoginName.String():
  107. systemStatus.DisablePasswordLogin = baseValue.(bool)
  108. case SystemSettingDisablePublicMemosName.String():
  109. systemStatus.DisablePublicMemos = baseValue.(bool)
  110. case SystemSettingMaxUploadSizeMiBName.String():
  111. systemStatus.MaxUploadSizeMiB = int(baseValue.(float64))
  112. case SystemSettingAdditionalStyleName.String():
  113. systemStatus.AdditionalStyle = baseValue.(string)
  114. case SystemSettingAdditionalScriptName.String():
  115. systemStatus.AdditionalScript = baseValue.(string)
  116. case SystemSettingCustomizedProfileName.String():
  117. customizedProfile := CustomizedProfile{}
  118. if err := json.Unmarshal([]byte(systemSetting.Value), &customizedProfile); err != nil {
  119. return echo.NewHTTPError(http.StatusInternalServerError, "Failed to unmarshal system setting customized profile value").SetInternal(err)
  120. }
  121. systemStatus.CustomizedProfile = customizedProfile
  122. case SystemSettingStorageServiceIDName.String():
  123. systemStatus.StorageServiceID = int32(baseValue.(float64))
  124. case SystemSettingLocalStoragePathName.String():
  125. systemStatus.LocalStoragePath = baseValue.(string)
  126. case SystemSettingMemoDisplayWithUpdatedTsName.String():
  127. systemStatus.MemoDisplayWithUpdatedTs = baseValue.(bool)
  128. default:
  129. log.Warn("Unknown system setting name", zap.String("setting name", systemSetting.Name))
  130. }
  131. }
  132. return c.JSON(http.StatusOK, systemStatus)
  133. }
  134. // ExecVacuum godoc
  135. //
  136. // @Summary Vacuum the database
  137. // @Tags system
  138. // @Produce json
  139. // @Success 200 {boolean} true "Database vacuumed"
  140. // @Failure 401 {object} nil "Missing user in session | Unauthorized"
  141. // @Failure 500 {object} nil "Failed to find user | Failed to ExecVacuum database"
  142. // @Router /api/v1/system/vacuum [POST]
  143. func (s *APIV1Service) ExecVacuum(c echo.Context) error {
  144. ctx := c.Request().Context()
  145. userID, ok := c.Get(userIDContextKey).(int32)
  146. if !ok {
  147. return echo.NewHTTPError(http.StatusUnauthorized, "Missing user in session")
  148. }
  149. user, err := s.Store.GetUser(ctx, &store.FindUser{
  150. ID: &userID,
  151. })
  152. if err != nil {
  153. return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find user").SetInternal(err)
  154. }
  155. if user == nil || user.Role != store.RoleHost {
  156. return echo.NewHTTPError(http.StatusUnauthorized, "Unauthorized")
  157. }
  158. if err := s.Store.Vacuum(ctx); err != nil {
  159. return echo.NewHTTPError(http.StatusInternalServerError, "Failed to vacuum database").SetInternal(err)
  160. }
  161. return c.JSON(http.StatusOK, true)
  162. }