memo_service.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. package v2
  2. import (
  3. "context"
  4. "github.com/google/cel-go/cel"
  5. "github.com/pkg/errors"
  6. apiv2pb "github.com/usememos/memos/proto/gen/api/v2"
  7. "github.com/usememos/memos/store"
  8. v1alpha1 "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
  9. "google.golang.org/grpc/codes"
  10. "google.golang.org/grpc/status"
  11. )
  12. type MemoService struct {
  13. apiv2pb.UnimplementedMemoServiceServer
  14. Store *store.Store
  15. }
  16. // NewMemoService creates a new MemoService.
  17. func NewMemoService(store *store.Store) *MemoService {
  18. return &MemoService{
  19. Store: store,
  20. }
  21. }
  22. func (s *MemoService) ListMemos(ctx context.Context, request *apiv2pb.ListMemosRequest) (*apiv2pb.ListMemosResponse, error) {
  23. memoFind := &store.FindMemo{}
  24. if request.Filter != "" {
  25. filter, err := parseListMemosFilter(request.Filter)
  26. if err != nil {
  27. return nil, status.Errorf(codes.InvalidArgument, "invalid filter: %v", err)
  28. }
  29. if filter.Visibility != nil {
  30. memoFind.VisibilityList = []store.Visibility{*filter.Visibility}
  31. }
  32. if filter.CreatedTsBefore != nil {
  33. memoFind.CreatedTsBefore = filter.CreatedTsBefore
  34. }
  35. if filter.CreatedTsAfter != nil {
  36. memoFind.CreatedTsAfter = filter.CreatedTsAfter
  37. }
  38. }
  39. user, _ := getCurrentUser(ctx, s.Store)
  40. // If the user is not authenticated, only public memos are visible.
  41. if user == nil {
  42. memoFind.VisibilityList = []store.Visibility{store.Public}
  43. }
  44. if request.PageSize != 0 {
  45. offset := int(request.Page * request.PageSize)
  46. limit := int(request.PageSize)
  47. memoFind.Offset = &offset
  48. memoFind.Limit = &limit
  49. }
  50. memos, err := s.Store.ListMemos(ctx, memoFind)
  51. if err != nil {
  52. return nil, err
  53. }
  54. memoMessages := make([]*apiv2pb.Memo, len(memos))
  55. for i, memo := range memos {
  56. memoMessages[i] = convertMemoFromStore(memo)
  57. }
  58. response := &apiv2pb.ListMemosResponse{
  59. Memos: memoMessages,
  60. }
  61. return response, nil
  62. }
  63. func (s *MemoService) GetMemo(ctx context.Context, request *apiv2pb.GetMemoRequest) (*apiv2pb.GetMemoResponse, error) {
  64. memo, err := s.Store.GetMemo(ctx, &store.FindMemo{
  65. ID: &request.Id,
  66. })
  67. if err != nil {
  68. return nil, err
  69. }
  70. if memo == nil {
  71. return nil, status.Errorf(codes.NotFound, "memo not found")
  72. }
  73. if memo.Visibility != store.Public {
  74. user, err := getCurrentUser(ctx, s.Store)
  75. if err != nil {
  76. return nil, status.Errorf(codes.Internal, "failed to get user")
  77. }
  78. if user == nil {
  79. return nil, status.Errorf(codes.PermissionDenied, "permission denied")
  80. }
  81. if memo.Visibility == store.Private && memo.CreatorID != user.ID {
  82. return nil, status.Errorf(codes.PermissionDenied, "permission denied")
  83. }
  84. }
  85. response := &apiv2pb.GetMemoResponse{
  86. Memo: convertMemoFromStore(memo),
  87. }
  88. return response, nil
  89. }
  90. // ListMemosFilterCELAttributes are the CEL attributes for ListMemosFilter.
  91. var ListMemosFilterCELAttributes = []cel.EnvOption{
  92. cel.Variable("visibility", cel.StringType),
  93. cel.Variable("created_ts_before", cel.IntType),
  94. cel.Variable("created_ts_after", cel.IntType),
  95. }
  96. type ListMemosFilter struct {
  97. Visibility *store.Visibility
  98. CreatedTsBefore *int64
  99. CreatedTsAfter *int64
  100. }
  101. func parseListMemosFilter(expression string) (*ListMemosFilter, error) {
  102. e, err := cel.NewEnv(ListMemosFilterCELAttributes...)
  103. if err != nil {
  104. return nil, err
  105. }
  106. ast, issues := e.Compile(expression)
  107. if issues != nil {
  108. return nil, errors.Errorf("found issue %v", issues)
  109. }
  110. filter := &ListMemosFilter{}
  111. callExpr := ast.Expr().GetCallExpr()
  112. findField(callExpr, filter)
  113. return filter, nil
  114. }
  115. func findField(callExpr *v1alpha1.Expr_Call, filter *ListMemosFilter) {
  116. if len(callExpr.Args) == 2 {
  117. idExpr := callExpr.Args[0].GetIdentExpr()
  118. if idExpr != nil {
  119. if idExpr.Name == "visibility" {
  120. visibility := store.Visibility(callExpr.Args[1].GetConstExpr().GetStringValue())
  121. filter.Visibility = &visibility
  122. }
  123. if idExpr.Name == "created_ts_before" {
  124. createdTsBefore := callExpr.Args[1].GetConstExpr().GetInt64Value()
  125. filter.CreatedTsBefore = &createdTsBefore
  126. }
  127. if idExpr.Name == "created_ts_after" {
  128. createdTsAfter := callExpr.Args[1].GetConstExpr().GetInt64Value()
  129. filter.CreatedTsAfter = &createdTsAfter
  130. }
  131. return
  132. }
  133. }
  134. for _, arg := range callExpr.Args {
  135. callExpr := arg.GetCallExpr()
  136. if callExpr != nil {
  137. findField(callExpr, filter)
  138. }
  139. }
  140. }
  141. func convertMemoFromStore(memo *store.Memo) *apiv2pb.Memo {
  142. return &apiv2pb.Memo{
  143. Id: int32(memo.ID),
  144. RowStatus: convertRowStatusFromStore(memo.RowStatus),
  145. CreatedTs: memo.CreatedTs,
  146. UpdatedTs: memo.UpdatedTs,
  147. CreatorId: int32(memo.CreatorID),
  148. Content: memo.Content,
  149. Visibility: convertVisibilityFromStore(memo.Visibility),
  150. Pinned: memo.Pinned,
  151. }
  152. }
  153. func convertVisibilityFromStore(visibility store.Visibility) apiv2pb.Visibility {
  154. switch visibility {
  155. case store.Private:
  156. return apiv2pb.Visibility_PRIVATE
  157. case store.Protected:
  158. return apiv2pb.Visibility_PROTECTED
  159. case store.Public:
  160. return apiv2pb.Visibility_PUBLIC
  161. default:
  162. return apiv2pb.Visibility_VISIBILITY_UNSPECIFIED
  163. }
  164. }