user_setting.go 4.5 KB

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