user_service_shortcuts.go 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. package v1
  2. import (
  3. "context"
  4. "github.com/pkg/errors"
  5. "google.golang.org/grpc/codes"
  6. "google.golang.org/grpc/status"
  7. "google.golang.org/protobuf/types/known/emptypb"
  8. "github.com/usememos/memos/internal/util"
  9. "github.com/usememos/memos/plugin/filter"
  10. v1pb "github.com/usememos/memos/proto/gen/api/v1"
  11. storepb "github.com/usememos/memos/proto/gen/store"
  12. "github.com/usememos/memos/store"
  13. )
  14. func (s *APIV1Service) ListShortcuts(ctx context.Context, request *v1pb.ListShortcutsRequest) (*v1pb.ListShortcutsResponse, error) {
  15. userID, err := ExtractUserIDFromName(request.Parent)
  16. if err != nil {
  17. return nil, status.Errorf(codes.InvalidArgument, "invalid user name: %v", err)
  18. }
  19. currentUser, err := s.GetCurrentUser(ctx)
  20. if err != nil {
  21. return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
  22. }
  23. if currentUser == nil || currentUser.ID != userID {
  24. return nil, status.Errorf(codes.PermissionDenied, "permission denied")
  25. }
  26. userSetting, err := s.Store.GetUserSetting(ctx, &store.FindUserSetting{
  27. UserID: &userID,
  28. Key: storepb.UserSettingKey_SHORTCUTS,
  29. })
  30. if err != nil {
  31. return nil, err
  32. }
  33. if userSetting == nil {
  34. return &v1pb.ListShortcutsResponse{
  35. Shortcuts: []*v1pb.Shortcut{},
  36. }, nil
  37. }
  38. shortcutsUserSetting := userSetting.GetShortcuts()
  39. shortcuts := []*v1pb.Shortcut{}
  40. for _, shortcut := range shortcutsUserSetting.GetShortcuts() {
  41. shortcuts = append(shortcuts, &v1pb.Shortcut{
  42. Id: shortcut.GetId(),
  43. Title: shortcut.GetTitle(),
  44. Filter: shortcut.GetFilter(),
  45. })
  46. }
  47. return &v1pb.ListShortcutsResponse{
  48. Shortcuts: shortcuts,
  49. }, nil
  50. }
  51. func (s *APIV1Service) CreateShortcut(ctx context.Context, request *v1pb.CreateShortcutRequest) (*v1pb.Shortcut, error) {
  52. userID, err := ExtractUserIDFromName(request.Parent)
  53. if err != nil {
  54. return nil, status.Errorf(codes.InvalidArgument, "invalid user name: %v", err)
  55. }
  56. currentUser, err := s.GetCurrentUser(ctx)
  57. if err != nil {
  58. return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
  59. }
  60. if currentUser == nil || currentUser.ID != userID {
  61. return nil, status.Errorf(codes.PermissionDenied, "permission denied")
  62. }
  63. newShortcut := &storepb.ShortcutsUserSetting_Shortcut{
  64. Id: util.GenUUID(),
  65. Title: request.Shortcut.GetTitle(),
  66. Filter: request.Shortcut.GetFilter(),
  67. }
  68. if newShortcut.Title == "" {
  69. return nil, status.Errorf(codes.InvalidArgument, "title is required")
  70. }
  71. if err := s.validateFilter(ctx, newShortcut.Filter); err != nil {
  72. return nil, status.Errorf(codes.InvalidArgument, "invalid filter: %v", err)
  73. }
  74. if request.ValidateOnly {
  75. return &v1pb.Shortcut{
  76. Id: newShortcut.GetId(),
  77. Title: newShortcut.GetTitle(),
  78. Filter: newShortcut.GetFilter(),
  79. }, nil
  80. }
  81. userSetting, err := s.Store.GetUserSetting(ctx, &store.FindUserSetting{
  82. UserID: &userID,
  83. Key: storepb.UserSettingKey_SHORTCUTS,
  84. })
  85. if err != nil {
  86. return nil, err
  87. }
  88. if userSetting == nil {
  89. userSetting = &storepb.UserSetting{
  90. UserId: userID,
  91. Key: storepb.UserSettingKey_SHORTCUTS,
  92. Value: &storepb.UserSetting_Shortcuts{
  93. Shortcuts: &storepb.ShortcutsUserSetting{
  94. Shortcuts: []*storepb.ShortcutsUserSetting_Shortcut{},
  95. },
  96. },
  97. }
  98. }
  99. shortcutsUserSetting := userSetting.GetShortcuts()
  100. shortcuts := shortcutsUserSetting.GetShortcuts()
  101. shortcuts = append(shortcuts, newShortcut)
  102. shortcutsUserSetting.Shortcuts = shortcuts
  103. userSetting.Value = &storepb.UserSetting_Shortcuts{
  104. Shortcuts: shortcutsUserSetting,
  105. }
  106. _, err = s.Store.UpsertUserSetting(ctx, userSetting)
  107. if err != nil {
  108. return nil, err
  109. }
  110. return &v1pb.Shortcut{
  111. Id: request.Shortcut.GetId(),
  112. Title: request.Shortcut.GetTitle(),
  113. Filter: request.Shortcut.GetFilter(),
  114. }, nil
  115. }
  116. func (s *APIV1Service) UpdateShortcut(ctx context.Context, request *v1pb.UpdateShortcutRequest) (*v1pb.Shortcut, error) {
  117. userID, err := ExtractUserIDFromName(request.Parent)
  118. if err != nil {
  119. return nil, status.Errorf(codes.InvalidArgument, "invalid user name: %v", err)
  120. }
  121. currentUser, err := s.GetCurrentUser(ctx)
  122. if err != nil {
  123. return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
  124. }
  125. if currentUser == nil || currentUser.ID != userID {
  126. return nil, status.Errorf(codes.PermissionDenied, "permission denied")
  127. }
  128. if request.UpdateMask == nil || len(request.UpdateMask.Paths) == 0 {
  129. return nil, status.Errorf(codes.InvalidArgument, "update mask is required")
  130. }
  131. userSetting, err := s.Store.GetUserSetting(ctx, &store.FindUserSetting{
  132. UserID: &userID,
  133. Key: storepb.UserSettingKey_SHORTCUTS,
  134. })
  135. if err != nil {
  136. return nil, err
  137. }
  138. if userSetting == nil {
  139. return nil, status.Errorf(codes.NotFound, "shortcut not found")
  140. }
  141. shortcutsUserSetting := userSetting.GetShortcuts()
  142. shortcuts := shortcutsUserSetting.GetShortcuts()
  143. newShortcuts := make([]*storepb.ShortcutsUserSetting_Shortcut, 0, len(shortcuts))
  144. for _, shortcut := range shortcuts {
  145. if shortcut.GetId() == request.Shortcut.GetId() {
  146. for _, field := range request.UpdateMask.Paths {
  147. if field == "title" {
  148. if request.Shortcut.GetTitle() == "" {
  149. return nil, status.Errorf(codes.InvalidArgument, "title is required")
  150. }
  151. shortcut.Title = request.Shortcut.GetTitle()
  152. } else if field == "filter" {
  153. if err := s.validateFilter(ctx, request.Shortcut.GetFilter()); err != nil {
  154. return nil, status.Errorf(codes.InvalidArgument, "invalid filter: %v", err)
  155. }
  156. shortcut.Filter = request.Shortcut.GetFilter()
  157. }
  158. }
  159. }
  160. newShortcuts = append(newShortcuts, shortcut)
  161. }
  162. shortcutsUserSetting.Shortcuts = newShortcuts
  163. userSetting.Value = &storepb.UserSetting_Shortcuts{
  164. Shortcuts: shortcutsUserSetting,
  165. }
  166. _, err = s.Store.UpsertUserSetting(ctx, userSetting)
  167. if err != nil {
  168. return nil, err
  169. }
  170. return &v1pb.Shortcut{
  171. Id: request.Shortcut.GetId(),
  172. Title: request.Shortcut.GetTitle(),
  173. Filter: request.Shortcut.GetFilter(),
  174. }, nil
  175. }
  176. func (s *APIV1Service) DeleteShortcut(ctx context.Context, request *v1pb.DeleteShortcutRequest) (*emptypb.Empty, error) {
  177. userID, err := ExtractUserIDFromName(request.Parent)
  178. if err != nil {
  179. return nil, status.Errorf(codes.InvalidArgument, "invalid user name: %v", err)
  180. }
  181. currentUser, err := s.GetCurrentUser(ctx)
  182. if err != nil {
  183. return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
  184. }
  185. if currentUser == nil || currentUser.ID != userID {
  186. return nil, status.Errorf(codes.PermissionDenied, "permission denied")
  187. }
  188. userSetting, err := s.Store.GetUserSetting(ctx, &store.FindUserSetting{
  189. UserID: &userID,
  190. Key: storepb.UserSettingKey_SHORTCUTS,
  191. })
  192. if err != nil {
  193. return nil, err
  194. }
  195. if userSetting == nil {
  196. return &emptypb.Empty{}, nil
  197. }
  198. shortcutsUserSetting := userSetting.GetShortcuts()
  199. shortcuts := shortcutsUserSetting.GetShortcuts()
  200. newShortcuts := make([]*storepb.ShortcutsUserSetting_Shortcut, 0, len(shortcuts))
  201. for _, shortcut := range shortcuts {
  202. if shortcut.GetId() != request.Id {
  203. newShortcuts = append(newShortcuts, shortcut)
  204. }
  205. }
  206. shortcutsUserSetting.Shortcuts = newShortcuts
  207. userSetting.Value = &storepb.UserSetting_Shortcuts{
  208. Shortcuts: shortcutsUserSetting,
  209. }
  210. _, err = s.Store.UpsertUserSetting(ctx, userSetting)
  211. if err != nil {
  212. return nil, err
  213. }
  214. return &emptypb.Empty{}, nil
  215. }
  216. func (s *APIV1Service) validateFilter(_ context.Context, filterStr string) error {
  217. if filterStr == "" {
  218. return errors.New("filter cannot be empty")
  219. }
  220. // Validate the filter.
  221. parsedExpr, err := filter.Parse(filterStr, filter.MemoFilterCELAttributes...)
  222. if err != nil {
  223. return errors.Wrap(err, "failed to parse filter")
  224. }
  225. convertCtx := filter.NewConvertContext()
  226. err = s.Store.GetDriver().ConvertExprToSQL(convertCtx, parsedExpr.GetExpr())
  227. if err != nil {
  228. return errors.Wrap(err, "failed to convert filter to SQL")
  229. }
  230. return nil
  231. }