123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180 |
- package v2
- import (
- "context"
- "github.com/google/cel-go/cel"
- "github.com/pkg/errors"
- apiv2pb "github.com/usememos/memos/proto/gen/api/v2"
- "github.com/usememos/memos/store"
- v1alpha1 "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
- "google.golang.org/grpc/codes"
- "google.golang.org/grpc/status"
- )
- type MemoService struct {
- apiv2pb.UnimplementedMemoServiceServer
- Store *store.Store
- }
- // NewMemoService creates a new MemoService.
- func NewMemoService(store *store.Store) *MemoService {
- return &MemoService{
- Store: store,
- }
- }
- func (s *MemoService) ListMemos(ctx context.Context, request *apiv2pb.ListMemosRequest) (*apiv2pb.ListMemosResponse, error) {
- memoFind := &store.FindMemo{}
- if request.Filter != "" {
- filter, err := parseListMemosFilter(request.Filter)
- if err != nil {
- return nil, status.Errorf(codes.InvalidArgument, "invalid filter: %v", err)
- }
- if filter.Visibility != nil {
- memoFind.VisibilityList = []store.Visibility{*filter.Visibility}
- }
- if filter.CreatedTsBefore != nil {
- memoFind.CreatedTsBefore = filter.CreatedTsBefore
- }
- if filter.CreatedTsAfter != nil {
- memoFind.CreatedTsAfter = filter.CreatedTsAfter
- }
- }
- user, _ := getCurrentUser(ctx, s.Store)
- // If the user is not authenticated, only public memos are visible.
- if user == nil {
- memoFind.VisibilityList = []store.Visibility{store.Public}
- }
- if request.PageSize != 0 {
- offset := int(request.Page * request.PageSize)
- limit := int(request.PageSize)
- memoFind.Offset = &offset
- memoFind.Limit = &limit
- }
- memos, err := s.Store.ListMemos(ctx, memoFind)
- if err != nil {
- return nil, err
- }
- memoMessages := make([]*apiv2pb.Memo, len(memos))
- for i, memo := range memos {
- memoMessages[i] = convertMemoFromStore(memo)
- }
- response := &apiv2pb.ListMemosResponse{
- Memos: memoMessages,
- }
- return response, nil
- }
- func (s *MemoService) GetMemo(ctx context.Context, request *apiv2pb.GetMemoRequest) (*apiv2pb.GetMemoResponse, error) {
- memo, err := s.Store.GetMemo(ctx, &store.FindMemo{
- ID: &request.Id,
- })
- if err != nil {
- return nil, err
- }
- if memo == nil {
- return nil, status.Errorf(codes.NotFound, "memo not found")
- }
- if memo.Visibility != store.Public {
- user, err := getCurrentUser(ctx, s.Store)
- if err != nil {
- return nil, status.Errorf(codes.Internal, "failed to get user")
- }
- if user == nil {
- return nil, status.Errorf(codes.PermissionDenied, "permission denied")
- }
- if memo.Visibility == store.Private && memo.CreatorID != user.ID {
- return nil, status.Errorf(codes.PermissionDenied, "permission denied")
- }
- }
- response := &apiv2pb.GetMemoResponse{
- Memo: convertMemoFromStore(memo),
- }
- return response, nil
- }
- // ListMemosFilterCELAttributes are the CEL attributes for ListMemosFilter.
- var ListMemosFilterCELAttributes = []cel.EnvOption{
- cel.Variable("visibility", cel.StringType),
- cel.Variable("created_ts_before", cel.IntType),
- cel.Variable("created_ts_after", cel.IntType),
- }
- type ListMemosFilter struct {
- Visibility *store.Visibility
- CreatedTsBefore *int64
- CreatedTsAfter *int64
- }
- func parseListMemosFilter(expression string) (*ListMemosFilter, error) {
- e, err := cel.NewEnv(ListMemosFilterCELAttributes...)
- if err != nil {
- return nil, err
- }
- ast, issues := e.Compile(expression)
- if issues != nil {
- return nil, errors.Errorf("found issue %v", issues)
- }
- filter := &ListMemosFilter{}
- callExpr := ast.Expr().GetCallExpr()
- findField(callExpr, filter)
- return filter, nil
- }
- func findField(callExpr *v1alpha1.Expr_Call, filter *ListMemosFilter) {
- if len(callExpr.Args) == 2 {
- idExpr := callExpr.Args[0].GetIdentExpr()
- if idExpr != nil {
- if idExpr.Name == "visibility" {
- visibility := store.Visibility(callExpr.Args[1].GetConstExpr().GetStringValue())
- filter.Visibility = &visibility
- }
- if idExpr.Name == "created_ts_before" {
- createdTsBefore := callExpr.Args[1].GetConstExpr().GetInt64Value()
- filter.CreatedTsBefore = &createdTsBefore
- }
- if idExpr.Name == "created_ts_after" {
- createdTsAfter := callExpr.Args[1].GetConstExpr().GetInt64Value()
- filter.CreatedTsAfter = &createdTsAfter
- }
- return
- }
- }
- for _, arg := range callExpr.Args {
- callExpr := arg.GetCallExpr()
- if callExpr != nil {
- findField(callExpr, filter)
- }
- }
- }
- func convertMemoFromStore(memo *store.Memo) *apiv2pb.Memo {
- return &apiv2pb.Memo{
- Id: int32(memo.ID),
- RowStatus: convertRowStatusFromStore(memo.RowStatus),
- CreatedTs: memo.CreatedTs,
- UpdatedTs: memo.UpdatedTs,
- CreatorId: int32(memo.CreatorID),
- Content: memo.Content,
- Visibility: convertVisibilityFromStore(memo.Visibility),
- Pinned: memo.Pinned,
- }
- }
- func convertVisibilityFromStore(visibility store.Visibility) apiv2pb.Visibility {
- switch visibility {
- case store.Private:
- return apiv2pb.Visibility_PRIVATE
- case store.Protected:
- return apiv2pb.Visibility_PROTECTED
- case store.Public:
- return apiv2pb.Visibility_PUBLIC
- default:
- return apiv2pb.Visibility_VISIBILITY_UNSPECIFIED
- }
- }
|