user_setting.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. package v1
  2. import (
  3. "encoding/json"
  4. "net/http"
  5. "github.com/labstack/echo/v4"
  6. "github.com/pkg/errors"
  7. "golang.org/x/exp/slices"
  8. "github.com/usememos/memos/store"
  9. )
  10. type UserSettingKey string
  11. const (
  12. // UserSettingLocaleKey is the key type for user locale.
  13. UserSettingLocaleKey UserSettingKey = "locale"
  14. // UserSettingAppearanceKey is the key type for user appearance.
  15. UserSettingAppearanceKey UserSettingKey = "appearance"
  16. // UserSettingMemoVisibilityKey is the key type for user preference memo default visibility.
  17. UserSettingMemoVisibilityKey UserSettingKey = "memo-visibility"
  18. // UserSettingTelegramUserIDKey is the key type for telegram UserID of memos user.
  19. UserSettingTelegramUserIDKey UserSettingKey = "telegram-user-id"
  20. )
  21. // String returns the string format of UserSettingKey type.
  22. func (key UserSettingKey) String() string {
  23. switch key {
  24. case UserSettingLocaleKey:
  25. return "locale"
  26. case UserSettingAppearanceKey:
  27. return "appearance"
  28. case UserSettingMemoVisibilityKey:
  29. return "memo-visibility"
  30. case UserSettingTelegramUserIDKey:
  31. return "telegram-user-id"
  32. }
  33. return ""
  34. }
  35. var (
  36. UserSettingLocaleValue = []string{
  37. "de",
  38. "en",
  39. "es",
  40. "fr",
  41. "hi",
  42. "hr",
  43. "it",
  44. "ja",
  45. "ko",
  46. "nl",
  47. "pl",
  48. "pt-BR",
  49. "ru",
  50. "sl",
  51. "sv",
  52. "tr",
  53. "uk",
  54. "vi",
  55. "zh-Hans",
  56. "zh-Hant",
  57. }
  58. UserSettingAppearanceValue = []string{"system", "light", "dark"}
  59. UserSettingMemoVisibilityValue = []Visibility{Private, Protected, Public}
  60. )
  61. type UserSetting struct {
  62. UserID int32 `json:"userId"`
  63. Key UserSettingKey `json:"key"`
  64. Value string `json:"value"`
  65. }
  66. type UpsertUserSettingRequest struct {
  67. UserID int32 `json:"-"`
  68. Key UserSettingKey `json:"key"`
  69. Value string `json:"value"`
  70. }
  71. func (s *APIV1Service) registerUserSettingRoutes(g *echo.Group) {
  72. g.POST("/user/setting", s.UpsertUserSetting)
  73. }
  74. // UpsertUserSetting godoc
  75. //
  76. // @Summary Upsert user setting
  77. // @Tags user-setting
  78. // @Accept json
  79. // @Produce json
  80. // @Param body body UpsertUserSettingRequest true "Request object."
  81. // @Success 200 {object} store.UserSetting "Created user setting"
  82. // @Failure 400 {object} nil "Malformatted post user setting upsert request | Invalid user setting format"
  83. // @Failure 401 {object} nil "Missing auth session"
  84. // @Failure 500 {object} nil "Failed to upsert user setting"
  85. // @Router /api/v1/user/setting [POST]
  86. func (s *APIV1Service) UpsertUserSetting(c echo.Context) error {
  87. ctx := c.Request().Context()
  88. userID, ok := c.Get(userIDContextKey).(int32)
  89. if !ok {
  90. return echo.NewHTTPError(http.StatusUnauthorized, "Missing auth session")
  91. }
  92. userSettingUpsert := &UpsertUserSettingRequest{}
  93. if err := json.NewDecoder(c.Request().Body).Decode(userSettingUpsert); err != nil {
  94. return echo.NewHTTPError(http.StatusBadRequest, "Malformatted post user setting upsert request").SetInternal(err)
  95. }
  96. if err := userSettingUpsert.Validate(); err != nil {
  97. return echo.NewHTTPError(http.StatusBadRequest, "Invalid user setting format").SetInternal(err)
  98. }
  99. userSettingUpsert.UserID = userID
  100. userSetting, err := s.Store.UpsertUserSetting(ctx, &store.UserSetting{
  101. UserID: userID,
  102. Key: userSettingUpsert.Key.String(),
  103. Value: userSettingUpsert.Value,
  104. })
  105. if err != nil {
  106. return echo.NewHTTPError(http.StatusInternalServerError, "Failed to upsert user setting").SetInternal(err)
  107. }
  108. userSettingMessage := convertUserSettingFromStore(userSetting)
  109. return c.JSON(http.StatusOK, userSettingMessage)
  110. }
  111. func (upsert UpsertUserSettingRequest) Validate() error {
  112. if upsert.Key == UserSettingLocaleKey {
  113. localeValue := "en"
  114. err := json.Unmarshal([]byte(upsert.Value), &localeValue)
  115. if err != nil {
  116. return errors.New("failed to unmarshal user setting locale value")
  117. }
  118. if !slices.Contains(UserSettingLocaleValue, localeValue) {
  119. return errors.New("invalid user setting locale value")
  120. }
  121. } else if upsert.Key == UserSettingAppearanceKey {
  122. appearanceValue := "system"
  123. err := json.Unmarshal([]byte(upsert.Value), &appearanceValue)
  124. if err != nil {
  125. return errors.New("failed to unmarshal user setting appearance value")
  126. }
  127. if !slices.Contains(UserSettingAppearanceValue, appearanceValue) {
  128. return errors.New("invalid user setting appearance value")
  129. }
  130. } else if upsert.Key == UserSettingMemoVisibilityKey {
  131. memoVisibilityValue := Private
  132. err := json.Unmarshal([]byte(upsert.Value), &memoVisibilityValue)
  133. if err != nil {
  134. return errors.New("failed to unmarshal user setting memo visibility value")
  135. }
  136. if !slices.Contains(UserSettingMemoVisibilityValue, memoVisibilityValue) {
  137. return errors.New("invalid user setting memo visibility value")
  138. }
  139. } else if upsert.Key == UserSettingTelegramUserIDKey {
  140. var key string
  141. err := json.Unmarshal([]byte(upsert.Value), &key)
  142. if err != nil {
  143. return errors.New("invalid user setting telegram user id value")
  144. }
  145. } else {
  146. return errors.New("invalid user setting key")
  147. }
  148. return nil
  149. }
  150. func convertUserSettingFromStore(userSetting *store.UserSetting) *UserSetting {
  151. return &UserSetting{
  152. UserID: userSetting.UserID,
  153. Key: UserSettingKey(userSetting.Key),
  154. Value: userSetting.Value,
  155. }
  156. }