From 7aa3207f6172cb99b5fc8d36e850996f87a1fab5 Mon Sep 17 00:00:00 2001 From: Noah Alderton <noahlouisalderton@gmail.com> Date: Sun, 28 Jan 2024 19:27:02 -0800 Subject: [PATCH 1/5] Add gRPC Memos Export --- api/v2/acl.go | 73 +++ api/v2/memo_service.go | 221 ++++++--- api/v2/v2.go | 3 + proto/api/v2/memo_service.proto | 13 + proto/gen/api/v2/README.md | 33 ++ proto/gen/api/v2/memo_service.pb.go | 446 ++++++++++++------ proto/gen/api/v2/memo_service.pb.gw.go | 61 +++ proto/gen/api/v2/memo_service_grpc.pb.go | 67 ++- .../components/Settings/MyAccountSection.tsx | 15 + web/src/components/ShareMemoDialog.tsx | 10 +- web/src/helpers/utils.ts | 8 + web/src/locales/en.json | 1 + 12 files changed, 710 insertions(+), 241 deletions(-) diff --git a/api/v2/acl.go b/api/v2/acl.go index 9c6498c1c33fc..4636ee889f37a 100644 --- a/api/v2/acl.go +++ b/api/v2/acl.go @@ -27,6 +27,40 @@ const ( usernameContextKey ContextKey = iota ) +// Used to set modified context of ServerStream +type WrappedStream struct { + stream grpc.ServerStream + ctx context.Context +} + +func (w *WrappedStream) RecvMsg(m any) error { + return w.stream.RecvMsg(m) +} + +func (w *WrappedStream) SendMsg(m any) error { + return w.stream.SendMsg(m) +} + +func (w *WrappedStream) SendHeader(md metadata.MD) error { + return w.stream.SendHeader(md) +} + +func (w *WrappedStream) SetHeader(md metadata.MD) error { + return w.stream.SetHeader(md) +} + +func (w *WrappedStream) SetTrailer(md metadata.MD) { + w.stream.SetTrailer(md) +} + +func (w *WrappedStream) Context() context.Context { + return w.ctx +} + +func newWrappedStream(stream grpc.ServerStream, ctx context.Context) grpc.ServerStream { + return &WrappedStream{stream, ctx} +} + // GRPCAuthInterceptor is the auth interceptor for gRPC server. type GRPCAuthInterceptor struct { Store *store.Store @@ -80,6 +114,45 @@ func (in *GRPCAuthInterceptor) AuthenticationInterceptor(ctx context.Context, re return handler(childCtx, request) } +func (in *GRPCAuthInterceptor) StreamAuthenticationInterceptor(srv any, stream grpc.ServerStream, serverInfo *grpc.StreamServerInfo, handler grpc.StreamHandler) error { + md, ok := metadata.FromIncomingContext(stream.Context()) + if !ok { + return status.Errorf(codes.Unauthenticated, "failed to parse metadata from incoming context") + } + accessToken, err := getTokenFromMetadata(md) + if err != nil { + return status.Errorf(codes.Unauthenticated, err.Error()) + } + + username, err := in.authenticate(stream.Context(), accessToken) + if err != nil { + if isUnauthorizeAllowedMethod(serverInfo.FullMethod) { + return handler(stream.Context(), stream) + } + return err + } + user, err := in.Store.GetUser(stream.Context(), &store.FindUser{ + Username: &username, + }) + if err != nil { + return errors.Wrap(err, "failed to get user") + } + if user == nil { + return errors.Errorf("user %q not exists", username) + } + if user.RowStatus == store.Archived { + return errors.Errorf("user %q is archived", username) + } + if isOnlyForAdminAllowedMethod(serverInfo.FullMethod) && user.Role != store.RoleHost && user.Role != store.RoleAdmin { + return errors.Errorf("user %q is not admin", username) + } + + // Stores userID into context. + childCtx := context.WithValue(stream.Context(), usernameContextKey, username) + + return handler(srv, newWrappedStream(stream, childCtx)) +} + func (in *GRPCAuthInterceptor) authenticate(ctx context.Context, accessToken string) (string, error) { if accessToken == "" { return "", status.Errorf(codes.Unauthenticated, "access token not found") diff --git a/api/v2/memo_service.go b/api/v2/memo_service.go index 057d5e1a9c458..6b221ce074423 100644 --- a/api/v2/memo_service.go +++ b/api/v2/memo_service.go @@ -1,6 +1,8 @@ package v2 import ( + "archive/zip" + "bytes" "context" "encoding/json" "fmt" @@ -32,6 +34,7 @@ import ( const ( DefaultPageSize = 10 MaxContentLength = 8 * 1024 + ChunkSize = 64 * 1024 // 64 KiB ) func (s *APIV2Service) CreateMemo(ctx context.Context, request *apiv2pb.CreateMemoRequest) (*apiv2pb.CreateMemoResponse, error) { @@ -100,84 +103,9 @@ func (s *APIV2Service) CreateMemo(ctx context.Context, request *apiv2pb.CreateMe } func (s *APIV2Service) ListMemos(ctx context.Context, request *apiv2pb.ListMemosRequest) (*apiv2pb.ListMemosResponse, error) { - memoFind := &store.FindMemo{ - // Exclude comments by default. - ExcludeComments: true, - } - if request.Filter != "" { - filter, err := parseListMemosFilter(request.Filter) - if err != nil { - return nil, status.Errorf(codes.InvalidArgument, "invalid filter: %v", err) - } - if len(filter.ContentSearch) > 0 { - memoFind.ContentSearch = filter.ContentSearch - } - if len(filter.Visibilities) > 0 { - memoFind.VisibilityList = filter.Visibilities - } - if filter.OrderByPinned { - memoFind.OrderByPinned = filter.OrderByPinned - } - if filter.DisplayTimeAfter != nil { - displayWithUpdatedTs, err := s.getMemoDisplayWithUpdatedTsSettingValue(ctx) - if err != nil { - return nil, status.Errorf(codes.Internal, "failed to get memo display with updated ts setting value") - } - if displayWithUpdatedTs { - memoFind.UpdatedTsAfter = filter.DisplayTimeAfter - } else { - memoFind.CreatedTsAfter = filter.DisplayTimeAfter - } - } - if filter.DisplayTimeBefore != nil { - displayWithUpdatedTs, err := s.getMemoDisplayWithUpdatedTsSettingValue(ctx) - if err != nil { - return nil, status.Errorf(codes.Internal, "failed to get memo display with updated ts setting value") - } - if displayWithUpdatedTs { - memoFind.UpdatedTsBefore = filter.DisplayTimeBefore - } else { - memoFind.CreatedTsBefore = filter.DisplayTimeBefore - } - } - if filter.Creator != nil { - username, err := ExtractUsernameFromName(*filter.Creator) - if err != nil { - return nil, status.Errorf(codes.InvalidArgument, "invalid creator name") - } - user, err := s.Store.GetUser(ctx, &store.FindUser{ - Username: &username, - }) - if err != nil { - return nil, status.Errorf(codes.Internal, "failed to get user") - } - if user == nil { - return nil, status.Errorf(codes.NotFound, "user not found") - } - memoFind.CreatorID = &user.ID - } - if filter.RowStatus != nil { - memoFind.RowStatus = filter.RowStatus - } - } else { - return nil, status.Errorf(codes.InvalidArgument, "filter is required") - } - - 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 user != nil && memoFind.CreatorID != nil && *memoFind.CreatorID != user.ID { - memoFind.VisibilityList = []store.Visibility{store.Public, store.Protected} - } - - displayWithUpdatedTs, err := s.getMemoDisplayWithUpdatedTsSettingValue(ctx) + memoFind, err := s.buildFindMemosWithFilter(ctx, request.Filter, true) if err != nil { - return nil, status.Errorf(codes.Internal, "failed to get memo display with updated ts setting value") - } - if displayWithUpdatedTs { - memoFind.OrderByUpdatedTs = true + return nil, err } var limit, offset int @@ -621,6 +549,61 @@ func (s *APIV2Service) GetUserMemosStats(ctx context.Context, request *apiv2pb.G return response, nil } +func (s *APIV2Service) MemosExport(request *apiv2pb.ExportMemosRequest, srv apiv2pb.MemoService_MemosExportServer) error { + ctx := srv.Context() + fmt.Printf("%+v\n", ctx) + memoFind, err := s.buildFindMemosWithFilter(ctx, request.Filter, true) + if err != nil { + return err + } + + memos, err := s.Store.ListMemos(ctx, memoFind) + if err != nil { + return err + } + + buf := new(bytes.Buffer) + writer := zip.NewWriter(buf) + + for _, memo := range memos { + memoMessage, err := s.convertMemoFromStore(ctx, memo) + log.Info(memoMessage.Content) + if err != nil { + return errors.Wrap(err, "failed to convert memo") + } + file, err := writer.Create(time.Unix(memo.CreatedTs, 0).Format(time.RFC3339) + ".md") + if err != nil { + return status.Errorf(codes.Internal, "Failed to create memo file") + } + _, err = file.Write([]byte(memoMessage.Content)) + if err != nil { + return status.Errorf(codes.Internal, "Failed to write to memo file") + } + } + + err = writer.Close() + if err != nil { + return status.Errorf(codes.Internal, "Failed to close zip file writer") + } + + exportChunk := &apiv2pb.ExportMemosResponse{} + sizeOfFile := len(buf.Bytes()) + for currentByte := 0; currentByte < sizeOfFile; currentByte += ChunkSize { + if currentByte+ChunkSize > sizeOfFile { + exportChunk.File = buf.Bytes()[currentByte:sizeOfFile] + } else { + exportChunk.File = buf.Bytes()[currentByte : currentByte+ChunkSize] + } + + err := srv.Send(exportChunk) + if err != nil { + return status.Error(codes.Internal, "Unable to stream ExportMemosResponse chunk") + } + } + + return nil +} + func (s *APIV2Service) convertMemoFromStore(ctx context.Context, memo *store.Memo) (*apiv2pb.Memo, error) { rawNodes, err := parser.Parse(tokenizer.Tokenize(memo.Content)) if err != nil { @@ -847,6 +830,90 @@ func (s *APIV2Service) dispatchMemoRelatedWebhook(ctx context.Context, memo *api return nil } +func (s *APIV2Service) buildFindMemosWithFilter(ctx context.Context, filter string, excludeComments bool) (*store.FindMemo, error) { + memoFind := &store.FindMemo{ + // Exclude comments by default. + ExcludeComments: excludeComments, + } + if filter != "" { + filter, err := parseListMemosFilter(filter) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "invalid filter: %v", err) + } + if len(filter.ContentSearch) > 0 { + memoFind.ContentSearch = filter.ContentSearch + } + if len(filter.Visibilities) > 0 { + memoFind.VisibilityList = filter.Visibilities + } + if filter.OrderByPinned { + memoFind.OrderByPinned = filter.OrderByPinned + } + if filter.DisplayTimeAfter != nil { + displayWithUpdatedTs, err := s.getMemoDisplayWithUpdatedTsSettingValue(ctx) + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to get memo display with updated ts setting value") + } + if displayWithUpdatedTs { + memoFind.UpdatedTsAfter = filter.DisplayTimeAfter + } else { + memoFind.CreatedTsAfter = filter.DisplayTimeAfter + } + } + if filter.DisplayTimeBefore != nil { + displayWithUpdatedTs, err := s.getMemoDisplayWithUpdatedTsSettingValue(ctx) + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to get memo display with updated ts setting value") + } + if displayWithUpdatedTs { + memoFind.UpdatedTsBefore = filter.DisplayTimeBefore + } else { + memoFind.CreatedTsBefore = filter.DisplayTimeBefore + } + } + if filter.Creator != nil { + username, err := ExtractUsernameFromName(*filter.Creator) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "invalid creator name") + } + user, err := s.Store.GetUser(ctx, &store.FindUser{ + Username: &username, + }) + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to get user") + } + if user == nil { + return nil, status.Errorf(codes.NotFound, "user not found") + } + memoFind.CreatorID = &user.ID + } + if filter.RowStatus != nil { + memoFind.RowStatus = filter.RowStatus + } + } else { + return nil, status.Errorf(codes.InvalidArgument, "filter is required") + } + + 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 user != nil && memoFind.CreatorID != nil && *memoFind.CreatorID != user.ID { + memoFind.VisibilityList = []store.Visibility{store.Public, store.Protected} + } + + displayWithUpdatedTs, err := s.getMemoDisplayWithUpdatedTsSettingValue(ctx) + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to get memo display with updated ts setting value") + } + if displayWithUpdatedTs { + memoFind.OrderByUpdatedTs = true + } + + return memoFind, nil +} + func convertMemoToWebhookPayload(memo *apiv2pb.Memo) *webhook.WebhookPayload { return &webhook.WebhookPayload{ CreatorID: memo.CreatorId, diff --git a/api/v2/v2.go b/api/v2/v2.go index 9a54a72bdf0ba..b4ec41607af82 100644 --- a/api/v2/v2.go +++ b/api/v2/v2.go @@ -47,6 +47,9 @@ func NewAPIV2Service(secret string, profile *profile.Profile, store *store.Store grpc.ChainUnaryInterceptor( authProvider.AuthenticationInterceptor, ), + grpc.ChainStreamInterceptor( + authProvider.StreamAuthenticationInterceptor, + ), ) apiv2Service := &APIV2Service{ Secret: secret, diff --git a/proto/api/v2/memo_service.proto b/proto/api/v2/memo_service.proto index dfda692d1ca83..17069f66aadad 100644 --- a/proto/api/v2/memo_service.proto +++ b/proto/api/v2/memo_service.proto @@ -90,6 +90,10 @@ service MemoService { option (google.api.http) = {get: "/api/v2/memos/stats"}; option (google.api.method_signature) = "username"; } + + rpc MemosExport(ExportMemosRequest) returns (stream ExportMemosResponse) { + option (google.api.http) = {get: "/api/v2/memos/export"}; + } } enum Visibility { @@ -273,3 +277,12 @@ message GetUserMemosStatsResponse { // key is the year-month-day string. e.g. "2020-01-01". map<string, int32> stats = 1; } + +message ExportMemosRequest { + // Same as ListMemosRequest.filter + string filter = 1; +} + +message ExportMemosResponse { + bytes file = 1; +} diff --git a/proto/gen/api/v2/README.md b/proto/gen/api/v2/README.md index 2207704115662..dc615f320d348 100644 --- a/proto/gen/api/v2/README.md +++ b/proto/gen/api/v2/README.md @@ -134,6 +134,8 @@ - [CreateMemoResponse](#memos-api-v2-CreateMemoResponse) - [DeleteMemoRequest](#memos-api-v2-DeleteMemoRequest) - [DeleteMemoResponse](#memos-api-v2-DeleteMemoResponse) + - [ExportMemosRequest](#memos-api-v2-ExportMemosRequest) + - [ExportMemosResponse](#memos-api-v2-ExportMemosResponse) - [GetMemoByNameRequest](#memos-api-v2-GetMemoByNameRequest) - [GetMemoByNameResponse](#memos-api-v2-GetMemoByNameResponse) - [GetMemoRequest](#memos-api-v2-GetMemoRequest) @@ -1939,6 +1941,36 @@ Used internally for obfuscating the page token. +<a name="memos-api-v2-ExportMemosRequest"></a> + +### ExportMemosRequest + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| filter | [string](#string) | | Same as ListMemosRequest.filter | + + + + + + +<a name="memos-api-v2-ExportMemosResponse"></a> + +### ExportMemosResponse + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| file | [bytes](#bytes) | | | + + + + + + <a name="memos-api-v2-GetMemoByNameRequest"></a> ### GetMemoByNameRequest @@ -2323,6 +2355,7 @@ Used internally for obfuscating the page token. | CreateMemoComment | [CreateMemoCommentRequest](#memos-api-v2-CreateMemoCommentRequest) | [CreateMemoCommentResponse](#memos-api-v2-CreateMemoCommentResponse) | CreateMemoComment creates a comment for a memo. | | ListMemoComments | [ListMemoCommentsRequest](#memos-api-v2-ListMemoCommentsRequest) | [ListMemoCommentsResponse](#memos-api-v2-ListMemoCommentsResponse) | ListMemoComments lists comments for a memo. | | GetUserMemosStats | [GetUserMemosStatsRequest](#memos-api-v2-GetUserMemosStatsRequest) | [GetUserMemosStatsResponse](#memos-api-v2-GetUserMemosStatsResponse) | GetUserMemosStats gets stats of memos for a user. | +| MemosExport | [ExportMemosRequest](#memos-api-v2-ExportMemosRequest) | [ExportMemosResponse](#memos-api-v2-ExportMemosResponse) stream | | diff --git a/proto/gen/api/v2/memo_service.pb.go b/proto/gen/api/v2/memo_service.pb.go index cd3e5cc7bcfef..ec083663b9716 100644 --- a/proto/gen/api/v2/memo_service.pb.go +++ b/proto/gen/api/v2/memo_service.pb.go @@ -1537,6 +1537,101 @@ func (x *GetUserMemosStatsResponse) GetStats() map[string]int32 { return nil } +type ExportMemosRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Same as ListMemosRequest.filter + Filter string `protobuf:"bytes,1,opt,name=filter,proto3" json:"filter,omitempty"` +} + +func (x *ExportMemosRequest) Reset() { + *x = ExportMemosRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v2_memo_service_proto_msgTypes[27] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ExportMemosRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ExportMemosRequest) ProtoMessage() {} + +func (x *ExportMemosRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_v2_memo_service_proto_msgTypes[27] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ExportMemosRequest.ProtoReflect.Descriptor instead. +func (*ExportMemosRequest) Descriptor() ([]byte, []int) { + return file_api_v2_memo_service_proto_rawDescGZIP(), []int{27} +} + +func (x *ExportMemosRequest) GetFilter() string { + if x != nil { + return x.Filter + } + return "" +} + +type ExportMemosResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + File []byte `protobuf:"bytes,1,opt,name=file,proto3" json:"file,omitempty"` +} + +func (x *ExportMemosResponse) Reset() { + *x = ExportMemosResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_api_v2_memo_service_proto_msgTypes[28] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ExportMemosResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ExportMemosResponse) ProtoMessage() {} + +func (x *ExportMemosResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_v2_memo_service_proto_msgTypes[28] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ExportMemosResponse.ProtoReflect.Descriptor instead. +func (*ExportMemosResponse) Descriptor() ([]byte, []int) { + return file_api_v2_memo_service_proto_rawDescGZIP(), []int{28} +} + +func (x *ExportMemosResponse) GetFile() []byte { + if x != nil { + return x.File + } + return nil +} + var File_api_v2_memo_service_proto protoreflect.FileDescriptor var file_api_v2_memo_service_proto_rawDesc = []byte{ @@ -1719,131 +1814,144 @@ var file_api_v2_memo_service_proto_rawDesc = []byte{ 0x74, 0x73, 0x1a, 0x38, 0x0a, 0x0a, 0x53, 0x74, 0x61, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x2a, 0x50, 0x0a, 0x0a, - 0x56, 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x1a, 0x0a, 0x16, 0x56, 0x49, - 0x53, 0x49, 0x42, 0x49, 0x4c, 0x49, 0x54, 0x59, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, - 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x50, 0x52, 0x49, 0x56, 0x41, 0x54, - 0x45, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x50, 0x52, 0x4f, 0x54, 0x45, 0x43, 0x54, 0x45, 0x44, - 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x10, 0x03, 0x32, 0xb0, - 0x0d, 0x0a, 0x0b, 0x4d, 0x65, 0x6d, 0x6f, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x69, - 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x6f, 0x12, 0x1f, 0x2e, 0x6d, - 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, - 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x3a, 0x01, 0x2a, 0x22, 0x0d, 0x2f, 0x61, 0x70, 0x69, - 0x2f, 0x76, 0x32, 0x2f, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x12, 0x63, 0x0a, 0x09, 0x4c, 0x69, 0x73, - 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x12, 0x1e, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x12, - 0x0d, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x12, 0x67, - 0x0a, 0x07, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x12, 0x1c, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, - 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x6f, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0xda, 0x41, 0x02, 0x69, 0x64, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x6d, 0x65, 0x6d, - 0x6f, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0x7d, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4d, 0x65, - 0x6d, 0x6f, 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x42, - 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6d, - 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x4d, - 0x65, 0x6d, 0x6f, 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x23, 0xda, 0x41, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, - 0x12, 0x14, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, - 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x12, 0x80, 0x01, 0x0a, 0x0a, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x05, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x2c, 0x0a, 0x12, + 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0x29, 0x0a, 0x13, 0x45, 0x78, + 0x70, 0x6f, 0x72, 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x04, 0x66, 0x69, 0x6c, 0x65, 0x2a, 0x50, 0x0a, 0x0a, 0x56, 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, + 0x69, 0x74, 0x79, 0x12, 0x1a, 0x0a, 0x16, 0x56, 0x49, 0x53, 0x49, 0x42, 0x49, 0x4c, 0x49, 0x54, + 0x59, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, + 0x0b, 0x0a, 0x07, 0x50, 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, + 0x50, 0x52, 0x4f, 0x54, 0x45, 0x43, 0x54, 0x45, 0x44, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x50, + 0x55, 0x42, 0x4c, 0x49, 0x43, 0x10, 0x03, 0x32, 0xa4, 0x0e, 0x0a, 0x0b, 0x4d, 0x65, 0x6d, 0x6f, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x69, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x6f, 0x12, 0x1f, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x6f, 0x52, + 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x6f, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2f, 0xda, 0x41, 0x0f, 0x69, 0x64, 0x2c, - 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x17, 0x3a, 0x01, 0x2a, 0x32, 0x12, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x6d, - 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0x70, 0x0a, 0x0a, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x6f, 0x12, 0x1f, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x65, 0x6d, - 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x65, - 0x6d, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0xda, 0x41, 0x02, 0x69, - 0x64, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x2a, 0x12, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, - 0x2f, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0x8f, 0x01, 0x0a, 0x10, - 0x53, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, - 0x12, 0x25, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, + 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x6f, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, + 0x3a, 0x01, 0x2a, 0x22, 0x0d, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x6d, 0x65, 0x6d, + 0x6f, 0x73, 0x12, 0x63, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x12, + 0x1e, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4c, + 0x69, 0x73, 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1f, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4c, + 0x69, 0x73, 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x12, 0x0d, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, + 0x32, 0x2f, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x12, 0x67, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x4d, 0x65, + 0x6d, 0x6f, 0x12, 0x1c, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x32, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1d, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, + 0x47, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x1f, 0xda, 0x41, 0x02, 0x69, 0x64, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x61, + 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, + 0x12, 0x7d, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x42, 0x79, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x22, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, + 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x42, 0x79, 0x4e, 0x61, + 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0xda, 0x41, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x61, 0x70, 0x69, 0x2f, + 0x76, 0x32, 0x2f, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x12, + 0x80, 0x01, 0x0a, 0x0a, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x6f, 0x12, 0x1f, + 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x20, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x2f, 0xda, 0x41, 0x0f, 0x69, 0x64, 0x2c, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x3a, 0x01, 0x2a, 0x32, 0x12, + 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x7b, 0x69, + 0x64, 0x7d, 0x12, 0x70, 0x0a, 0x0a, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x6f, + 0x12, 0x1f, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x20, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, + 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1f, 0xda, 0x41, 0x02, 0x69, 0x64, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, + 0x2a, 0x12, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, + 0x7b, 0x69, 0x64, 0x7d, 0x12, 0x8f, 0x01, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x6f, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x25, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, + 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x6f, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x26, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x2c, 0xda, 0x41, 0x02, 0x69, 0x64, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x21, 0x3a, 0x01, 0x2a, 0x22, - 0x1c, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x7b, - 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x8f, 0x01, - 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x73, 0x12, 0x26, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x32, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x6d, 0x65, - 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, - 0x65, 0x6d, 0x6f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x29, 0xda, 0x41, 0x02, 0x69, 0x64, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x1e, 0x12, 0x1c, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x6d, 0x65, 0x6d, 0x6f, 0x73, - 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, - 0x8f, 0x01, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x52, 0x65, 0x6c, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x25, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2c, 0xda, 0x41, 0x02, 0x69, 0x64, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x21, 0x3a, 0x01, 0x2a, 0x22, 0x1c, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, + 0x32, 0x2f, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x8f, 0x01, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x4d, + 0x65, 0x6d, 0x6f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x26, 0x2e, 0x6d, + 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x4d, 0x65, 0x6d, 0x6f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x32, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x29, 0xda, + 0x41, 0x02, 0x69, 0x64, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x61, 0x70, 0x69, + 0x2f, 0x76, 0x32, 0x2f, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x2f, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x8f, 0x01, 0x0a, 0x10, 0x53, 0x65, 0x74, + 0x4d, 0x65, 0x6d, 0x6f, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x25, 0x2e, + 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x65, 0x74, + 0x4d, 0x65, 0x6d, 0x6f, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x52, 0x65, 0x6c, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x6d, 0x65, - 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x65, 0x74, 0x4d, 0x65, - 0x6d, 0x6f, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x2c, 0xda, 0x41, 0x02, 0x69, 0x64, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x21, - 0x3a, 0x01, 0x2a, 0x22, 0x1c, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x6d, 0x65, 0x6d, - 0x6f, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x12, 0x8f, 0x01, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x52, 0x65, - 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x26, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x52, - 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x27, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4c, + 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2c, 0xda, 0x41, + 0x02, 0x69, 0x64, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x21, 0x3a, 0x01, 0x2a, 0x22, 0x1c, 0x2f, 0x61, + 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, + 0x2f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x8f, 0x01, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x29, 0xda, 0x41, 0x02, 0x69, 0x64, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x6d, - 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x12, 0x8e, 0x01, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x65, - 0x6d, 0x6f, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x26, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, - 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, - 0x65, 0x6d, 0x6f, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x27, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, - 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x6f, 0x43, 0x6f, 0x6d, 0x6d, 0x65, - 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0xda, 0x41, 0x02, 0x69, - 0x64, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x22, 0x1b, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, - 0x2f, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, - 0x65, 0x6e, 0x74, 0x73, 0x12, 0x8b, 0x01, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x65, 0x6d, - 0x6f, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x25, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, + 0x12, 0x26, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, + 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x65, 0x6d, 0x6f, + 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x29, 0xda, 0x41, 0x02, 0x69, 0x64, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, + 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x7b, 0x69, + 0x64, 0x7d, 0x2f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x8e, 0x01, 0x0a, + 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x6f, 0x43, 0x6f, 0x6d, 0x6d, 0x65, + 0x6e, 0x74, 0x12, 0x26, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x32, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x6f, 0x43, 0x6f, 0x6d, 0x6d, + 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x6d, 0x65, 0x6d, + 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x4d, 0x65, 0x6d, 0x6f, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x28, 0xda, 0x41, 0x02, 0x69, 0x64, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, + 0x22, 0x1b, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, + 0x7b, 0x69, 0x64, 0x7d, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x8b, 0x01, + 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, + 0x74, 0x73, 0x12, 0x25, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x32, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, + 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x65, 0x6d, - 0x6f, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x26, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, - 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0xda, 0x41, 0x02, 0x69, 0x64, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x12, 0x1b, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x6d, - 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, - 0x74, 0x73, 0x12, 0x8c, 0x01, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x4d, 0x65, - 0x6d, 0x6f, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x26, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x4d, - 0x65, 0x6d, 0x6f, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x27, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, + 0x6f, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x28, 0xda, 0x41, 0x02, 0x69, 0x64, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x12, 0x1b, + 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x7b, 0x69, + 0x64, 0x7d, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x8c, 0x01, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x53, 0x74, 0x61, 0x74, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0xda, 0x41, 0x08, 0x75, 0x73, - 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x61, - 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x73, 0x74, 0x61, 0x74, - 0x73, 0x42, 0xa8, 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x42, 0x10, 0x4d, 0x65, 0x6d, 0x6f, 0x53, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x75, 0x73, 0x65, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, - 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x65, 0x6e, 0x2f, - 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x3b, 0x61, 0x70, 0x69, 0x76, 0x32, 0xa2, 0x02, 0x03, 0x4d, - 0x41, 0x58, 0xaa, 0x02, 0x0c, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x41, 0x70, 0x69, 0x2e, 0x56, - 0x32, 0xca, 0x02, 0x0c, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x5c, 0x41, 0x70, 0x69, 0x5c, 0x56, 0x32, - 0xe2, 0x02, 0x18, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x5c, 0x41, 0x70, 0x69, 0x5c, 0x56, 0x32, 0x5c, - 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0e, 0x4d, 0x65, - 0x6d, 0x6f, 0x73, 0x3a, 0x3a, 0x41, 0x70, 0x69, 0x3a, 0x3a, 0x56, 0x32, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x73, 0x12, 0x26, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, + 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x53, 0x74, 0x61, + 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, + 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, + 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x26, 0xda, 0x41, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x6d, + 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x12, 0x72, 0x0a, 0x0b, 0x4d, 0x65, + 0x6d, 0x6f, 0x73, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x20, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, + 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x4d, + 0x65, 0x6d, 0x6f, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6d, 0x65, + 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x45, 0x78, 0x70, 0x6f, 0x72, + 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, + 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x30, 0x01, 0x42, 0xa8, + 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x32, 0x42, 0x10, 0x4d, 0x65, 0x6d, 0x6f, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x75, 0x73, 0x65, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x6d, 0x65, 0x6d, + 0x6f, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x61, 0x70, 0x69, + 0x2f, 0x76, 0x32, 0x3b, 0x61, 0x70, 0x69, 0x76, 0x32, 0xa2, 0x02, 0x03, 0x4d, 0x41, 0x58, 0xaa, + 0x02, 0x0c, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x41, 0x70, 0x69, 0x2e, 0x56, 0x32, 0xca, 0x02, + 0x0c, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x5c, 0x41, 0x70, 0x69, 0x5c, 0x56, 0x32, 0xe2, 0x02, 0x18, + 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x5c, 0x41, 0x70, 0x69, 0x5c, 0x56, 0x32, 0x5c, 0x47, 0x50, 0x42, + 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0e, 0x4d, 0x65, 0x6d, 0x6f, 0x73, + 0x3a, 0x3a, 0x41, 0x70, 0x69, 0x3a, 0x3a, 0x56, 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( @@ -1859,7 +1967,7 @@ func file_api_v2_memo_service_proto_rawDescGZIP() []byte { } var file_api_v2_memo_service_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_api_v2_memo_service_proto_msgTypes = make([]protoimpl.MessageInfo, 28) +var file_api_v2_memo_service_proto_msgTypes = make([]protoimpl.MessageInfo, 30) var file_api_v2_memo_service_proto_goTypes = []interface{}{ (Visibility)(0), // 0: memos.api.v2.Visibility (*Memo)(nil), // 1: memos.api.v2.Memo @@ -1889,39 +1997,41 @@ var file_api_v2_memo_service_proto_goTypes = []interface{}{ (*ListMemoCommentsResponse)(nil), // 25: memos.api.v2.ListMemoCommentsResponse (*GetUserMemosStatsRequest)(nil), // 26: memos.api.v2.GetUserMemosStatsRequest (*GetUserMemosStatsResponse)(nil), // 27: memos.api.v2.GetUserMemosStatsResponse - nil, // 28: memos.api.v2.GetUserMemosStatsResponse.StatsEntry - (RowStatus)(0), // 29: memos.api.v2.RowStatus - (*timestamppb.Timestamp)(nil), // 30: google.protobuf.Timestamp - (*Node)(nil), // 31: memos.api.v2.Node - (*Resource)(nil), // 32: memos.api.v2.Resource - (*MemoRelation)(nil), // 33: memos.api.v2.MemoRelation - (*fieldmaskpb.FieldMask)(nil), // 34: google.protobuf.FieldMask + (*ExportMemosRequest)(nil), // 28: memos.api.v2.ExportMemosRequest + (*ExportMemosResponse)(nil), // 29: memos.api.v2.ExportMemosResponse + nil, // 30: memos.api.v2.GetUserMemosStatsResponse.StatsEntry + (RowStatus)(0), // 31: memos.api.v2.RowStatus + (*timestamppb.Timestamp)(nil), // 32: google.protobuf.Timestamp + (*Node)(nil), // 33: memos.api.v2.Node + (*Resource)(nil), // 34: memos.api.v2.Resource + (*MemoRelation)(nil), // 35: memos.api.v2.MemoRelation + (*fieldmaskpb.FieldMask)(nil), // 36: google.protobuf.FieldMask } var file_api_v2_memo_service_proto_depIdxs = []int32{ - 29, // 0: memos.api.v2.Memo.row_status:type_name -> memos.api.v2.RowStatus - 30, // 1: memos.api.v2.Memo.create_time:type_name -> google.protobuf.Timestamp - 30, // 2: memos.api.v2.Memo.update_time:type_name -> google.protobuf.Timestamp - 30, // 3: memos.api.v2.Memo.display_time:type_name -> google.protobuf.Timestamp - 31, // 4: memos.api.v2.Memo.nodes:type_name -> memos.api.v2.Node + 31, // 0: memos.api.v2.Memo.row_status:type_name -> memos.api.v2.RowStatus + 32, // 1: memos.api.v2.Memo.create_time:type_name -> google.protobuf.Timestamp + 32, // 2: memos.api.v2.Memo.update_time:type_name -> google.protobuf.Timestamp + 32, // 3: memos.api.v2.Memo.display_time:type_name -> google.protobuf.Timestamp + 33, // 4: memos.api.v2.Memo.nodes:type_name -> memos.api.v2.Node 0, // 5: memos.api.v2.Memo.visibility:type_name -> memos.api.v2.Visibility - 32, // 6: memos.api.v2.Memo.resources:type_name -> memos.api.v2.Resource - 33, // 7: memos.api.v2.Memo.relations:type_name -> memos.api.v2.MemoRelation + 34, // 6: memos.api.v2.Memo.resources:type_name -> memos.api.v2.Resource + 35, // 7: memos.api.v2.Memo.relations:type_name -> memos.api.v2.MemoRelation 0, // 8: memos.api.v2.CreateMemoRequest.visibility:type_name -> memos.api.v2.Visibility 1, // 9: memos.api.v2.CreateMemoResponse.memo:type_name -> memos.api.v2.Memo 1, // 10: memos.api.v2.ListMemosResponse.memos:type_name -> memos.api.v2.Memo 1, // 11: memos.api.v2.GetMemoResponse.memo:type_name -> memos.api.v2.Memo 1, // 12: memos.api.v2.GetMemoByNameResponse.memo:type_name -> memos.api.v2.Memo 1, // 13: memos.api.v2.UpdateMemoRequest.memo:type_name -> memos.api.v2.Memo - 34, // 14: memos.api.v2.UpdateMemoRequest.update_mask:type_name -> google.protobuf.FieldMask + 36, // 14: memos.api.v2.UpdateMemoRequest.update_mask:type_name -> google.protobuf.FieldMask 1, // 15: memos.api.v2.UpdateMemoResponse.memo:type_name -> memos.api.v2.Memo - 32, // 16: memos.api.v2.SetMemoResourcesRequest.resources:type_name -> memos.api.v2.Resource - 32, // 17: memos.api.v2.ListMemoResourcesResponse.resources:type_name -> memos.api.v2.Resource - 33, // 18: memos.api.v2.SetMemoRelationsRequest.relations:type_name -> memos.api.v2.MemoRelation - 33, // 19: memos.api.v2.ListMemoRelationsResponse.relations:type_name -> memos.api.v2.MemoRelation + 34, // 16: memos.api.v2.SetMemoResourcesRequest.resources:type_name -> memos.api.v2.Resource + 34, // 17: memos.api.v2.ListMemoResourcesResponse.resources:type_name -> memos.api.v2.Resource + 35, // 18: memos.api.v2.SetMemoRelationsRequest.relations:type_name -> memos.api.v2.MemoRelation + 35, // 19: memos.api.v2.ListMemoRelationsResponse.relations:type_name -> memos.api.v2.MemoRelation 2, // 20: memos.api.v2.CreateMemoCommentRequest.create:type_name -> memos.api.v2.CreateMemoRequest 1, // 21: memos.api.v2.CreateMemoCommentResponse.memo:type_name -> memos.api.v2.Memo 1, // 22: memos.api.v2.ListMemoCommentsResponse.memos:type_name -> memos.api.v2.Memo - 28, // 23: memos.api.v2.GetUserMemosStatsResponse.stats:type_name -> memos.api.v2.GetUserMemosStatsResponse.StatsEntry + 30, // 23: memos.api.v2.GetUserMemosStatsResponse.stats:type_name -> memos.api.v2.GetUserMemosStatsResponse.StatsEntry 2, // 24: memos.api.v2.MemoService.CreateMemo:input_type -> memos.api.v2.CreateMemoRequest 4, // 25: memos.api.v2.MemoService.ListMemos:input_type -> memos.api.v2.ListMemosRequest 6, // 26: memos.api.v2.MemoService.GetMemo:input_type -> memos.api.v2.GetMemoRequest @@ -1935,21 +2045,23 @@ var file_api_v2_memo_service_proto_depIdxs = []int32{ 22, // 34: memos.api.v2.MemoService.CreateMemoComment:input_type -> memos.api.v2.CreateMemoCommentRequest 24, // 35: memos.api.v2.MemoService.ListMemoComments:input_type -> memos.api.v2.ListMemoCommentsRequest 26, // 36: memos.api.v2.MemoService.GetUserMemosStats:input_type -> memos.api.v2.GetUserMemosStatsRequest - 3, // 37: memos.api.v2.MemoService.CreateMemo:output_type -> memos.api.v2.CreateMemoResponse - 5, // 38: memos.api.v2.MemoService.ListMemos:output_type -> memos.api.v2.ListMemosResponse - 7, // 39: memos.api.v2.MemoService.GetMemo:output_type -> memos.api.v2.GetMemoResponse - 9, // 40: memos.api.v2.MemoService.GetMemoByName:output_type -> memos.api.v2.GetMemoByNameResponse - 11, // 41: memos.api.v2.MemoService.UpdateMemo:output_type -> memos.api.v2.UpdateMemoResponse - 13, // 42: memos.api.v2.MemoService.DeleteMemo:output_type -> memos.api.v2.DeleteMemoResponse - 15, // 43: memos.api.v2.MemoService.SetMemoResources:output_type -> memos.api.v2.SetMemoResourcesResponse - 17, // 44: memos.api.v2.MemoService.ListMemoResources:output_type -> memos.api.v2.ListMemoResourcesResponse - 19, // 45: memos.api.v2.MemoService.SetMemoRelations:output_type -> memos.api.v2.SetMemoRelationsResponse - 21, // 46: memos.api.v2.MemoService.ListMemoRelations:output_type -> memos.api.v2.ListMemoRelationsResponse - 23, // 47: memos.api.v2.MemoService.CreateMemoComment:output_type -> memos.api.v2.CreateMemoCommentResponse - 25, // 48: memos.api.v2.MemoService.ListMemoComments:output_type -> memos.api.v2.ListMemoCommentsResponse - 27, // 49: memos.api.v2.MemoService.GetUserMemosStats:output_type -> memos.api.v2.GetUserMemosStatsResponse - 37, // [37:50] is the sub-list for method output_type - 24, // [24:37] is the sub-list for method input_type + 28, // 37: memos.api.v2.MemoService.MemosExport:input_type -> memos.api.v2.ExportMemosRequest + 3, // 38: memos.api.v2.MemoService.CreateMemo:output_type -> memos.api.v2.CreateMemoResponse + 5, // 39: memos.api.v2.MemoService.ListMemos:output_type -> memos.api.v2.ListMemosResponse + 7, // 40: memos.api.v2.MemoService.GetMemo:output_type -> memos.api.v2.GetMemoResponse + 9, // 41: memos.api.v2.MemoService.GetMemoByName:output_type -> memos.api.v2.GetMemoByNameResponse + 11, // 42: memos.api.v2.MemoService.UpdateMemo:output_type -> memos.api.v2.UpdateMemoResponse + 13, // 43: memos.api.v2.MemoService.DeleteMemo:output_type -> memos.api.v2.DeleteMemoResponse + 15, // 44: memos.api.v2.MemoService.SetMemoResources:output_type -> memos.api.v2.SetMemoResourcesResponse + 17, // 45: memos.api.v2.MemoService.ListMemoResources:output_type -> memos.api.v2.ListMemoResourcesResponse + 19, // 46: memos.api.v2.MemoService.SetMemoRelations:output_type -> memos.api.v2.SetMemoRelationsResponse + 21, // 47: memos.api.v2.MemoService.ListMemoRelations:output_type -> memos.api.v2.ListMemoRelationsResponse + 23, // 48: memos.api.v2.MemoService.CreateMemoComment:output_type -> memos.api.v2.CreateMemoCommentResponse + 25, // 49: memos.api.v2.MemoService.ListMemoComments:output_type -> memos.api.v2.ListMemoCommentsResponse + 27, // 50: memos.api.v2.MemoService.GetUserMemosStats:output_type -> memos.api.v2.GetUserMemosStatsResponse + 29, // 51: memos.api.v2.MemoService.MemosExport:output_type -> memos.api.v2.ExportMemosResponse + 38, // [38:52] is the sub-list for method output_type + 24, // [24:38] is the sub-list for method input_type 24, // [24:24] is the sub-list for extension type_name 24, // [24:24] is the sub-list for extension extendee 0, // [0:24] is the sub-list for field type_name @@ -2289,6 +2401,30 @@ func file_api_v2_memo_service_proto_init() { return nil } } + file_api_v2_memo_service_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ExportMemosRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_v2_memo_service_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ExportMemosResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } file_api_v2_memo_service_proto_msgTypes[0].OneofWrappers = []interface{}{} type x struct{} @@ -2297,7 +2433,7 @@ func file_api_v2_memo_service_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_api_v2_memo_service_proto_rawDesc, NumEnums: 1, - NumMessages: 28, + NumMessages: 30, NumExtensions: 0, NumServices: 1, }, diff --git a/proto/gen/api/v2/memo_service.pb.gw.go b/proto/gen/api/v2/memo_service.pb.gw.go index 0940a8f5ee061..dcf9780ce02d1 100644 --- a/proto/gen/api/v2/memo_service.pb.gw.go +++ b/proto/gen/api/v2/memo_service.pb.gw.go @@ -723,6 +723,34 @@ func local_request_MemoService_GetUserMemosStats_0(ctx context.Context, marshale } +var ( + filter_MemoService_MemosExport_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_MemoService_MemosExport_0(ctx context.Context, marshaler runtime.Marshaler, client MemoServiceClient, req *http.Request, pathParams map[string]string) (MemoService_MemosExportClient, runtime.ServerMetadata, error) { + var protoReq ExportMemosRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_MemoService_MemosExport_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + stream, err := client.MemosExport(ctx, &protoReq) + if err != nil { + return nil, metadata, err + } + header, err := stream.Header() + if err != nil { + return nil, metadata, err + } + metadata.HeaderMD = header + return stream, metadata, nil + +} + // RegisterMemoServiceHandlerServer registers the http handlers for service MemoService to "mux". // UnaryRPC :call MemoServiceServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -1054,6 +1082,13 @@ func RegisterMemoServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux }) + mux.Handle("GET", pattern_MemoService_MemosExport_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + err := status.Error(codes.Unimplemented, "streaming calls are not yet supported in the in-process transport") + _, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + }) + return nil } @@ -1381,6 +1416,28 @@ func RegisterMemoServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux }) + mux.Handle("GET", pattern_MemoService_MemosExport_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/memos.api.v2.MemoService/MemosExport", runtime.WithHTTPPathPattern("/api/v2/memos/export")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_MemoService_MemosExport_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_MemoService_MemosExport_0(annotatedContext, mux, outboundMarshaler, w, req, func() (proto.Message, error) { return resp.Recv() }, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -1410,6 +1467,8 @@ var ( pattern_MemoService_ListMemoComments_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4}, []string{"api", "v2", "memos", "id", "comments"}, "")) pattern_MemoService_GetUserMemosStats_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"api", "v2", "memos", "stats"}, "")) + + pattern_MemoService_MemosExport_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"api", "v2", "memos", "export"}, "")) ) var ( @@ -1438,4 +1497,6 @@ var ( forward_MemoService_ListMemoComments_0 = runtime.ForwardResponseMessage forward_MemoService_GetUserMemosStats_0 = runtime.ForwardResponseMessage + + forward_MemoService_MemosExport_0 = runtime.ForwardResponseStream ) diff --git a/proto/gen/api/v2/memo_service_grpc.pb.go b/proto/gen/api/v2/memo_service_grpc.pb.go index a9639bfd36968..eb4a93d9ff41b 100644 --- a/proto/gen/api/v2/memo_service_grpc.pb.go +++ b/proto/gen/api/v2/memo_service_grpc.pb.go @@ -32,6 +32,7 @@ const ( MemoService_CreateMemoComment_FullMethodName = "/memos.api.v2.MemoService/CreateMemoComment" MemoService_ListMemoComments_FullMethodName = "/memos.api.v2.MemoService/ListMemoComments" MemoService_GetUserMemosStats_FullMethodName = "/memos.api.v2.MemoService/GetUserMemosStats" + MemoService_MemosExport_FullMethodName = "/memos.api.v2.MemoService/MemosExport" ) // MemoServiceClient is the client API for MemoService service. @@ -64,6 +65,7 @@ type MemoServiceClient interface { ListMemoComments(ctx context.Context, in *ListMemoCommentsRequest, opts ...grpc.CallOption) (*ListMemoCommentsResponse, error) // GetUserMemosStats gets stats of memos for a user. GetUserMemosStats(ctx context.Context, in *GetUserMemosStatsRequest, opts ...grpc.CallOption) (*GetUserMemosStatsResponse, error) + MemosExport(ctx context.Context, in *ExportMemosRequest, opts ...grpc.CallOption) (MemoService_MemosExportClient, error) } type memoServiceClient struct { @@ -191,6 +193,38 @@ func (c *memoServiceClient) GetUserMemosStats(ctx context.Context, in *GetUserMe return out, nil } +func (c *memoServiceClient) MemosExport(ctx context.Context, in *ExportMemosRequest, opts ...grpc.CallOption) (MemoService_MemosExportClient, error) { + stream, err := c.cc.NewStream(ctx, &MemoService_ServiceDesc.Streams[0], MemoService_MemosExport_FullMethodName, opts...) + if err != nil { + return nil, err + } + x := &memoServiceMemosExportClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type MemoService_MemosExportClient interface { + Recv() (*ExportMemosResponse, error) + grpc.ClientStream +} + +type memoServiceMemosExportClient struct { + grpc.ClientStream +} + +func (x *memoServiceMemosExportClient) Recv() (*ExportMemosResponse, error) { + m := new(ExportMemosResponse) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + // MemoServiceServer is the server API for MemoService service. // All implementations must embed UnimplementedMemoServiceServer // for forward compatibility @@ -221,6 +255,7 @@ type MemoServiceServer interface { ListMemoComments(context.Context, *ListMemoCommentsRequest) (*ListMemoCommentsResponse, error) // GetUserMemosStats gets stats of memos for a user. GetUserMemosStats(context.Context, *GetUserMemosStatsRequest) (*GetUserMemosStatsResponse, error) + MemosExport(*ExportMemosRequest, MemoService_MemosExportServer) error mustEmbedUnimplementedMemoServiceServer() } @@ -267,6 +302,9 @@ func (UnimplementedMemoServiceServer) ListMemoComments(context.Context, *ListMem func (UnimplementedMemoServiceServer) GetUserMemosStats(context.Context, *GetUserMemosStatsRequest) (*GetUserMemosStatsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetUserMemosStats not implemented") } +func (UnimplementedMemoServiceServer) MemosExport(*ExportMemosRequest, MemoService_MemosExportServer) error { + return status.Errorf(codes.Unimplemented, "method MemosExport not implemented") +} func (UnimplementedMemoServiceServer) mustEmbedUnimplementedMemoServiceServer() {} // UnsafeMemoServiceServer may be embedded to opt out of forward compatibility for this service. @@ -514,6 +552,27 @@ func _MemoService_GetUserMemosStats_Handler(srv interface{}, ctx context.Context return interceptor(ctx, in, info, handler) } +func _MemoService_MemosExport_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(ExportMemosRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(MemoServiceServer).MemosExport(m, &memoServiceMemosExportServer{stream}) +} + +type MemoService_MemosExportServer interface { + Send(*ExportMemosResponse) error + grpc.ServerStream +} + +type memoServiceMemosExportServer struct { + grpc.ServerStream +} + +func (x *memoServiceMemosExportServer) Send(m *ExportMemosResponse) error { + return x.ServerStream.SendMsg(m) +} + // MemoService_ServiceDesc is the grpc.ServiceDesc for MemoService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -574,6 +633,12 @@ var MemoService_ServiceDesc = grpc.ServiceDesc{ Handler: _MemoService_GetUserMemosStats_Handler, }, }, - Streams: []grpc.StreamDesc{}, + Streams: []grpc.StreamDesc{ + { + StreamName: "MemosExport", + Handler: _MemoService_MemosExport_Handler, + ServerStreams: true, + }, + }, Metadata: "api/v2/memo_service.proto", } diff --git a/web/src/components/Settings/MyAccountSection.tsx b/web/src/components/Settings/MyAccountSection.tsx index 53c4ae34e94e2..9885bd8f1fbaa 100644 --- a/web/src/components/Settings/MyAccountSection.tsx +++ b/web/src/components/Settings/MyAccountSection.tsx @@ -1,4 +1,6 @@ import { Button } from "@mui/joy"; +import { memoServiceClient } from "@/grpcweb"; +import { downloadFileFromUrl } from "@/helpers/utils"; import useCurrentUser from "@/hooks/useCurrentUser"; import { useTranslate } from "@/utils/i18n"; import showChangePasswordDialog from "../ChangePasswordDialog"; @@ -10,6 +12,16 @@ const MyAccountSection = () => { const t = useTranslate(); const user = useCurrentUser(); + const downloadExportedMemos = async (user: any) => { + const chunks = []; + for await (const response of memoServiceClient.memosExport({ filter: `creator == "${user.name}"` })) { + chunks.push(response.file.buffer); + } + const blob = new Blob(chunks); + const downloadUrl = window.URL.createObjectURL(blob); + downloadFileFromUrl(downloadUrl, "memos-export.zip"); + }; + return ( <div className="w-full gap-2 pt-2 pb-4"> <p className="font-medium text-gray-700 dark:text-gray-500">{t("setting.account-section.title")}</p> @@ -27,6 +39,9 @@ const MyAccountSection = () => { <Button variant="outlined" onClick={showChangePasswordDialog}> {t("setting.account-section.change-password")} </Button> + <Button variant="outlined" onClick={() => downloadExportedMemos(user)}> + {t("setting.account-section.export-memos")} + </Button> </div> <AccessTokenSection /> diff --git a/web/src/components/ShareMemoDialog.tsx b/web/src/components/ShareMemoDialog.tsx index e3aa1a44f70b8..6ae7e1f62b461 100644 --- a/web/src/components/ShareMemoDialog.tsx +++ b/web/src/components/ShareMemoDialog.tsx @@ -3,6 +3,7 @@ import copy from "copy-to-clipboard"; import React, { useEffect, useRef } from "react"; import { toast } from "react-hot-toast"; import { getDateTimeString } from "@/helpers/datetime"; +import { downloadFileFromUrl } from "@/helpers/utils"; import useLoading from "@/hooks/useLoading"; import toImage from "@/labs/html2image"; import { useUserStore, extractUsernameFromName } from "@/store/v1"; @@ -62,13 +63,6 @@ const ShareMemoDialog: React.FC<Props> = (props: Props) => { downloadFileFromUrl(URL.createObjectURL(blob), `memos-${getDateTimeString(Date.now())}.md`); }; - const downloadFileFromUrl = (url: string, filename: string) => { - const a = document.createElement("a"); - a.href = url; - a.download = filename; - a.click(); - }; - const handleCopyLinkBtnClick = () => { copy(`${window.location.origin}/m/${memo.name}`); toast.success(t("message.succeed-copy-link")); @@ -140,6 +134,6 @@ export default function showShareMemoDialog(memo: Memo): void { dialogName: "share-memo-dialog", }, ShareMemoDialog, - { memo }, + { memo } ); } diff --git a/web/src/helpers/utils.ts b/web/src/helpers/utils.ts index 8ed11e6da6eb8..5679538280334 100644 --- a/web/src/helpers/utils.ts +++ b/web/src/helpers/utils.ts @@ -92,3 +92,11 @@ export const isValidUrl = (url: string): boolean => { return false; } }; + +export const downloadFileFromUrl = (url: string, filename: string) => { + const a = document.createElement("a"); + a.href = url; + a.download = filename; + a.click(); + a.remove(); +}; diff --git a/web/src/locales/en.json b/web/src/locales/en.json index f35d054594020..93524a75fbcaf 100644 --- a/web/src/locales/en.json +++ b/web/src/locales/en.json @@ -177,6 +177,7 @@ "email-note": "Optional", "update-information": "Update Information", "change-password": "Change password", + "export-memos": "Export Memos", "reset-api": "Reset API", "openapi-title": "OpenAPI", "openapi-reset": "Reset OpenAPI Key", From 7ec5ec8a198c6fb7dcf749eb4750471d995e9a9a Mon Sep 17 00:00:00 2001 From: Noah Alderton <noahlouisalderton@gmail.com> Date: Sun, 28 Jan 2024 22:22:29 -0800 Subject: [PATCH 2/5] Update code style --- api/v2/acl.go | 10 +++++----- web/src/locales/es.json | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/api/v2/acl.go b/api/v2/acl.go index 4636ee889f37a..e8a5b0538b13b 100644 --- a/api/v2/acl.go +++ b/api/v2/acl.go @@ -27,10 +27,10 @@ const ( usernameContextKey ContextKey = iota ) -// Used to set modified context of ServerStream +// Used to set modified context of ServerStream. type WrappedStream struct { - stream grpc.ServerStream ctx context.Context + stream grpc.ServerStream } func (w *WrappedStream) RecvMsg(m any) error { @@ -57,8 +57,8 @@ func (w *WrappedStream) Context() context.Context { return w.ctx } -func newWrappedStream(stream grpc.ServerStream, ctx context.Context) grpc.ServerStream { - return &WrappedStream{stream, ctx} +func newWrappedStream(ctx context.Context, stream grpc.ServerStream) grpc.ServerStream { + return &WrappedStream{ctx, stream} } // GRPCAuthInterceptor is the auth interceptor for gRPC server. @@ -150,7 +150,7 @@ func (in *GRPCAuthInterceptor) StreamAuthenticationInterceptor(srv any, stream g // Stores userID into context. childCtx := context.WithValue(stream.Context(), usernameContextKey, username) - return handler(srv, newWrappedStream(stream, childCtx)) + return handler(srv, newWrappedStream(childCtx, stream)) } func (in *GRPCAuthInterceptor) authenticate(ctx context.Context, accessToken string) (string, error) { diff --git a/web/src/locales/es.json b/web/src/locales/es.json index 82c6e02711072..c1cbc0a6e1190 100644 --- a/web/src/locales/es.json +++ b/web/src/locales/es.json @@ -92,7 +92,8 @@ "account-section": { "title": "Información de la Cuenta", "update-information": "Actualizar Información", - "change-password": "Cambiar Contraseña" + "change-password": "Cambiar Contraseña", + "export-memos": "Exportar Notas" }, "preference-section": { "theme": "Tema", From bccdcd6fe8adf73574ad0ab2f348d4cb1e24cc74 Mon Sep 17 00:00:00 2001 From: Noah Alderton <noahlouisalderton@gmail.com> Date: Sun, 28 Jan 2024 22:31:02 -0800 Subject: [PATCH 3/5] Add URL.revokeObjectURL --- web/src/components/Settings/MyAccountSection.tsx | 1 + web/src/components/ShareMemoDialog.tsx | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/web/src/components/Settings/MyAccountSection.tsx b/web/src/components/Settings/MyAccountSection.tsx index 9885bd8f1fbaa..fdbeda2e2b58d 100644 --- a/web/src/components/Settings/MyAccountSection.tsx +++ b/web/src/components/Settings/MyAccountSection.tsx @@ -20,6 +20,7 @@ const MyAccountSection = () => { const blob = new Blob(chunks); const downloadUrl = window.URL.createObjectURL(blob); downloadFileFromUrl(downloadUrl, "memos-export.zip"); + URL.revokeObjectURL(downloadUrl); }; return ( diff --git a/web/src/components/ShareMemoDialog.tsx b/web/src/components/ShareMemoDialog.tsx index 6ae7e1f62b461..8093998869f2a 100644 --- a/web/src/components/ShareMemoDialog.tsx +++ b/web/src/components/ShareMemoDialog.tsx @@ -52,6 +52,7 @@ const ShareMemoDialog: React.FC<Props> = (props: Props) => { .then((url) => { downloadFileFromUrl(url, `memos-${getDateTimeString(Date.now())}.png`); downloadingImageState.setFinish(); + URL.revokeObjectURL(url); }) .catch((err) => { console.error(err); @@ -60,7 +61,9 @@ const ShareMemoDialog: React.FC<Props> = (props: Props) => { const handleDownloadTextFileBtnClick = () => { const blob = new Blob([memo.content], { type: "text/plain;charset=utf-8" }); - downloadFileFromUrl(URL.createObjectURL(blob), `memos-${getDateTimeString(Date.now())}.md`); + const url = URL.createObjectURL(blob); + downloadFileFromUrl(url, `memos-${getDateTimeString(Date.now())}.md`); + URL.revokeObjectURL(url); }; const handleCopyLinkBtnClick = () => { From 4410b03b27b65834e79986faaa0d0704390bea56 Mon Sep 17 00:00:00 2001 From: Noah Alderton <noahlouisalderton@gmail.com> Date: Sun, 28 Jan 2024 22:44:56 -0800 Subject: [PATCH 4/5] Rename protobuf and ESLint fix --- api/v2/memo_service.go | 6 +-- proto/api/v2/memo_service.proto | 6 +-- proto/gen/api/v2/README.md | 66 ++++++++++++------------ proto/gen/api/v2/memo_service.pb.go | 62 +++++++++++----------- proto/gen/api/v2/memo_service.pb.gw.go | 2 +- proto/gen/api/v2/memo_service_grpc.pb.go | 20 +++---- web/src/components/ShareMemoDialog.tsx | 2 +- 7 files changed, 82 insertions(+), 82 deletions(-) diff --git a/api/v2/memo_service.go b/api/v2/memo_service.go index 6b221ce074423..75e426695e680 100644 --- a/api/v2/memo_service.go +++ b/api/v2/memo_service.go @@ -549,7 +549,7 @@ func (s *APIV2Service) GetUserMemosStats(ctx context.Context, request *apiv2pb.G return response, nil } -func (s *APIV2Service) MemosExport(request *apiv2pb.ExportMemosRequest, srv apiv2pb.MemoService_MemosExportServer) error { +func (s *APIV2Service) MemosExport(request *apiv2pb.MemosExportRequest, srv apiv2pb.MemoService_MemosExportServer) error { ctx := srv.Context() fmt.Printf("%+v\n", ctx) memoFind, err := s.buildFindMemosWithFilter(ctx, request.Filter, true) @@ -586,7 +586,7 @@ func (s *APIV2Service) MemosExport(request *apiv2pb.ExportMemosRequest, srv apiv return status.Errorf(codes.Internal, "Failed to close zip file writer") } - exportChunk := &apiv2pb.ExportMemosResponse{} + exportChunk := &apiv2pb.MemosExportResponse{} sizeOfFile := len(buf.Bytes()) for currentByte := 0; currentByte < sizeOfFile; currentByte += ChunkSize { if currentByte+ChunkSize > sizeOfFile { @@ -597,7 +597,7 @@ func (s *APIV2Service) MemosExport(request *apiv2pb.ExportMemosRequest, srv apiv err := srv.Send(exportChunk) if err != nil { - return status.Error(codes.Internal, "Unable to stream ExportMemosResponse chunk") + return status.Error(codes.Internal, "Unable to stream MemosExportResponse chunk") } } diff --git a/proto/api/v2/memo_service.proto b/proto/api/v2/memo_service.proto index 17069f66aadad..3e8377c11ba95 100644 --- a/proto/api/v2/memo_service.proto +++ b/proto/api/v2/memo_service.proto @@ -91,7 +91,7 @@ service MemoService { option (google.api.method_signature) = "username"; } - rpc MemosExport(ExportMemosRequest) returns (stream ExportMemosResponse) { + rpc MemosExport(MemosExportRequest) returns (stream MemosExportResponse) { option (google.api.http) = {get: "/api/v2/memos/export"}; } } @@ -278,11 +278,11 @@ message GetUserMemosStatsResponse { map<string, int32> stats = 1; } -message ExportMemosRequest { +message MemosExportRequest { // Same as ListMemosRequest.filter string filter = 1; } -message ExportMemosResponse { +message MemosExportResponse { bytes file = 1; } diff --git a/proto/gen/api/v2/README.md b/proto/gen/api/v2/README.md index dc615f320d348..cd67434d14740 100644 --- a/proto/gen/api/v2/README.md +++ b/proto/gen/api/v2/README.md @@ -134,8 +134,6 @@ - [CreateMemoResponse](#memos-api-v2-CreateMemoResponse) - [DeleteMemoRequest](#memos-api-v2-DeleteMemoRequest) - [DeleteMemoResponse](#memos-api-v2-DeleteMemoResponse) - - [ExportMemosRequest](#memos-api-v2-ExportMemosRequest) - - [ExportMemosResponse](#memos-api-v2-ExportMemosResponse) - [GetMemoByNameRequest](#memos-api-v2-GetMemoByNameRequest) - [GetMemoByNameResponse](#memos-api-v2-GetMemoByNameResponse) - [GetMemoRequest](#memos-api-v2-GetMemoRequest) @@ -152,6 +150,8 @@ - [ListMemosRequest](#memos-api-v2-ListMemosRequest) - [ListMemosResponse](#memos-api-v2-ListMemosResponse) - [Memo](#memos-api-v2-Memo) + - [MemosExportRequest](#memos-api-v2-MemosExportRequest) + - [MemosExportResponse](#memos-api-v2-MemosExportResponse) - [SetMemoRelationsRequest](#memos-api-v2-SetMemoRelationsRequest) - [SetMemoRelationsResponse](#memos-api-v2-SetMemoRelationsResponse) - [SetMemoResourcesRequest](#memos-api-v2-SetMemoResourcesRequest) @@ -1941,36 +1941,6 @@ Used internally for obfuscating the page token. -<a name="memos-api-v2-ExportMemosRequest"></a> - -### ExportMemosRequest - - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| filter | [string](#string) | | Same as ListMemosRequest.filter | - - - - - - -<a name="memos-api-v2-ExportMemosResponse"></a> - -### ExportMemosResponse - - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| file | [bytes](#bytes) | | | - - - - - - <a name="memos-api-v2-GetMemoByNameRequest"></a> ### GetMemoByNameRequest @@ -2231,6 +2201,36 @@ Used internally for obfuscating the page token. +<a name="memos-api-v2-MemosExportRequest"></a> + +### MemosExportRequest + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| filter | [string](#string) | | Same as ListMemosRequest.filter | + + + + + + +<a name="memos-api-v2-MemosExportResponse"></a> + +### MemosExportResponse + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| file | [bytes](#bytes) | | | + + + + + + <a name="memos-api-v2-SetMemoRelationsRequest"></a> ### SetMemoRelationsRequest @@ -2355,7 +2355,7 @@ Used internally for obfuscating the page token. | CreateMemoComment | [CreateMemoCommentRequest](#memos-api-v2-CreateMemoCommentRequest) | [CreateMemoCommentResponse](#memos-api-v2-CreateMemoCommentResponse) | CreateMemoComment creates a comment for a memo. | | ListMemoComments | [ListMemoCommentsRequest](#memos-api-v2-ListMemoCommentsRequest) | [ListMemoCommentsResponse](#memos-api-v2-ListMemoCommentsResponse) | ListMemoComments lists comments for a memo. | | GetUserMemosStats | [GetUserMemosStatsRequest](#memos-api-v2-GetUserMemosStatsRequest) | [GetUserMemosStatsResponse](#memos-api-v2-GetUserMemosStatsResponse) | GetUserMemosStats gets stats of memos for a user. | -| MemosExport | [ExportMemosRequest](#memos-api-v2-ExportMemosRequest) | [ExportMemosResponse](#memos-api-v2-ExportMemosResponse) stream | | +| MemosExport | [MemosExportRequest](#memos-api-v2-MemosExportRequest) | [MemosExportResponse](#memos-api-v2-MemosExportResponse) stream | | diff --git a/proto/gen/api/v2/memo_service.pb.go b/proto/gen/api/v2/memo_service.pb.go index ec083663b9716..d3b1c76faa9c6 100644 --- a/proto/gen/api/v2/memo_service.pb.go +++ b/proto/gen/api/v2/memo_service.pb.go @@ -1537,7 +1537,7 @@ func (x *GetUserMemosStatsResponse) GetStats() map[string]int32 { return nil } -type ExportMemosRequest struct { +type MemosExportRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields @@ -1546,8 +1546,8 @@ type ExportMemosRequest struct { Filter string `protobuf:"bytes,1,opt,name=filter,proto3" json:"filter,omitempty"` } -func (x *ExportMemosRequest) Reset() { - *x = ExportMemosRequest{} +func (x *MemosExportRequest) Reset() { + *x = MemosExportRequest{} if protoimpl.UnsafeEnabled { mi := &file_api_v2_memo_service_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1555,13 +1555,13 @@ func (x *ExportMemosRequest) Reset() { } } -func (x *ExportMemosRequest) String() string { +func (x *MemosExportRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ExportMemosRequest) ProtoMessage() {} +func (*MemosExportRequest) ProtoMessage() {} -func (x *ExportMemosRequest) ProtoReflect() protoreflect.Message { +func (x *MemosExportRequest) ProtoReflect() protoreflect.Message { mi := &file_api_v2_memo_service_proto_msgTypes[27] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1573,19 +1573,19 @@ func (x *ExportMemosRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ExportMemosRequest.ProtoReflect.Descriptor instead. -func (*ExportMemosRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use MemosExportRequest.ProtoReflect.Descriptor instead. +func (*MemosExportRequest) Descriptor() ([]byte, []int) { return file_api_v2_memo_service_proto_rawDescGZIP(), []int{27} } -func (x *ExportMemosRequest) GetFilter() string { +func (x *MemosExportRequest) GetFilter() string { if x != nil { return x.Filter } return "" } -type ExportMemosResponse struct { +type MemosExportResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields @@ -1593,8 +1593,8 @@ type ExportMemosResponse struct { File []byte `protobuf:"bytes,1,opt,name=file,proto3" json:"file,omitempty"` } -func (x *ExportMemosResponse) Reset() { - *x = ExportMemosResponse{} +func (x *MemosExportResponse) Reset() { + *x = MemosExportResponse{} if protoimpl.UnsafeEnabled { mi := &file_api_v2_memo_service_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1602,13 +1602,13 @@ func (x *ExportMemosResponse) Reset() { } } -func (x *ExportMemosResponse) String() string { +func (x *MemosExportResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ExportMemosResponse) ProtoMessage() {} +func (*MemosExportResponse) ProtoMessage() {} -func (x *ExportMemosResponse) ProtoReflect() protoreflect.Message { +func (x *MemosExportResponse) ProtoReflect() protoreflect.Message { mi := &file_api_v2_memo_service_proto_msgTypes[28] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1620,12 +1620,12 @@ func (x *ExportMemosResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ExportMemosResponse.ProtoReflect.Descriptor instead. -func (*ExportMemosResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use MemosExportResponse.ProtoReflect.Descriptor instead. +func (*MemosExportResponse) Descriptor() ([]byte, []int) { return file_api_v2_memo_service_proto_rawDescGZIP(), []int{28} } -func (x *ExportMemosResponse) GetFile() []byte { +func (x *MemosExportResponse) GetFile() []byte { if x != nil { return x.File } @@ -1815,10 +1815,10 @@ var file_api_v2_memo_service_proto_rawDesc = []byte{ 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x2c, 0x0a, 0x12, - 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0x29, 0x0a, 0x13, 0x45, 0x78, - 0x70, 0x6f, 0x72, 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x28, 0x09, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0x29, 0x0a, 0x13, 0x4d, 0x65, + 0x6d, 0x6f, 0x73, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x2a, 0x50, 0x0a, 0x0a, 0x56, 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x1a, 0x0a, 0x16, 0x56, 0x49, 0x53, 0x49, 0x42, 0x49, 0x4c, 0x49, 0x54, @@ -1934,10 +1934,10 @@ var file_api_v2_memo_service_proto_rawDesc = []byte{ 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x12, 0x72, 0x0a, 0x0b, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x20, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, - 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x4d, - 0x65, 0x6d, 0x6f, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6d, 0x65, - 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x45, 0x78, 0x70, 0x6f, 0x72, - 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, + 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x45, 0x78, + 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6d, 0x65, + 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4d, 0x65, 0x6d, 0x6f, 0x73, + 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x30, 0x01, 0x42, 0xa8, 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, @@ -1997,8 +1997,8 @@ var file_api_v2_memo_service_proto_goTypes = []interface{}{ (*ListMemoCommentsResponse)(nil), // 25: memos.api.v2.ListMemoCommentsResponse (*GetUserMemosStatsRequest)(nil), // 26: memos.api.v2.GetUserMemosStatsRequest (*GetUserMemosStatsResponse)(nil), // 27: memos.api.v2.GetUserMemosStatsResponse - (*ExportMemosRequest)(nil), // 28: memos.api.v2.ExportMemosRequest - (*ExportMemosResponse)(nil), // 29: memos.api.v2.ExportMemosResponse + (*MemosExportRequest)(nil), // 28: memos.api.v2.MemosExportRequest + (*MemosExportResponse)(nil), // 29: memos.api.v2.MemosExportResponse nil, // 30: memos.api.v2.GetUserMemosStatsResponse.StatsEntry (RowStatus)(0), // 31: memos.api.v2.RowStatus (*timestamppb.Timestamp)(nil), // 32: google.protobuf.Timestamp @@ -2045,7 +2045,7 @@ var file_api_v2_memo_service_proto_depIdxs = []int32{ 22, // 34: memos.api.v2.MemoService.CreateMemoComment:input_type -> memos.api.v2.CreateMemoCommentRequest 24, // 35: memos.api.v2.MemoService.ListMemoComments:input_type -> memos.api.v2.ListMemoCommentsRequest 26, // 36: memos.api.v2.MemoService.GetUserMemosStats:input_type -> memos.api.v2.GetUserMemosStatsRequest - 28, // 37: memos.api.v2.MemoService.MemosExport:input_type -> memos.api.v2.ExportMemosRequest + 28, // 37: memos.api.v2.MemoService.MemosExport:input_type -> memos.api.v2.MemosExportRequest 3, // 38: memos.api.v2.MemoService.CreateMemo:output_type -> memos.api.v2.CreateMemoResponse 5, // 39: memos.api.v2.MemoService.ListMemos:output_type -> memos.api.v2.ListMemosResponse 7, // 40: memos.api.v2.MemoService.GetMemo:output_type -> memos.api.v2.GetMemoResponse @@ -2059,7 +2059,7 @@ var file_api_v2_memo_service_proto_depIdxs = []int32{ 23, // 48: memos.api.v2.MemoService.CreateMemoComment:output_type -> memos.api.v2.CreateMemoCommentResponse 25, // 49: memos.api.v2.MemoService.ListMemoComments:output_type -> memos.api.v2.ListMemoCommentsResponse 27, // 50: memos.api.v2.MemoService.GetUserMemosStats:output_type -> memos.api.v2.GetUserMemosStatsResponse - 29, // 51: memos.api.v2.MemoService.MemosExport:output_type -> memos.api.v2.ExportMemosResponse + 29, // 51: memos.api.v2.MemoService.MemosExport:output_type -> memos.api.v2.MemosExportResponse 38, // [38:52] is the sub-list for method output_type 24, // [24:38] is the sub-list for method input_type 24, // [24:24] is the sub-list for extension type_name @@ -2402,7 +2402,7 @@ func file_api_v2_memo_service_proto_init() { } } file_api_v2_memo_service_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ExportMemosRequest); i { + switch v := v.(*MemosExportRequest); i { case 0: return &v.state case 1: @@ -2414,7 +2414,7 @@ func file_api_v2_memo_service_proto_init() { } } file_api_v2_memo_service_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ExportMemosResponse); i { + switch v := v.(*MemosExportResponse); i { case 0: return &v.state case 1: diff --git a/proto/gen/api/v2/memo_service.pb.gw.go b/proto/gen/api/v2/memo_service.pb.gw.go index dcf9780ce02d1..0a4686f55b4ec 100644 --- a/proto/gen/api/v2/memo_service.pb.gw.go +++ b/proto/gen/api/v2/memo_service.pb.gw.go @@ -728,7 +728,7 @@ var ( ) func request_MemoService_MemosExport_0(ctx context.Context, marshaler runtime.Marshaler, client MemoServiceClient, req *http.Request, pathParams map[string]string) (MemoService_MemosExportClient, runtime.ServerMetadata, error) { - var protoReq ExportMemosRequest + var protoReq MemosExportRequest var metadata runtime.ServerMetadata if err := req.ParseForm(); err != nil { diff --git a/proto/gen/api/v2/memo_service_grpc.pb.go b/proto/gen/api/v2/memo_service_grpc.pb.go index eb4a93d9ff41b..b8cb63718c21d 100644 --- a/proto/gen/api/v2/memo_service_grpc.pb.go +++ b/proto/gen/api/v2/memo_service_grpc.pb.go @@ -65,7 +65,7 @@ type MemoServiceClient interface { ListMemoComments(ctx context.Context, in *ListMemoCommentsRequest, opts ...grpc.CallOption) (*ListMemoCommentsResponse, error) // GetUserMemosStats gets stats of memos for a user. GetUserMemosStats(ctx context.Context, in *GetUserMemosStatsRequest, opts ...grpc.CallOption) (*GetUserMemosStatsResponse, error) - MemosExport(ctx context.Context, in *ExportMemosRequest, opts ...grpc.CallOption) (MemoService_MemosExportClient, error) + MemosExport(ctx context.Context, in *MemosExportRequest, opts ...grpc.CallOption) (MemoService_MemosExportClient, error) } type memoServiceClient struct { @@ -193,7 +193,7 @@ func (c *memoServiceClient) GetUserMemosStats(ctx context.Context, in *GetUserMe return out, nil } -func (c *memoServiceClient) MemosExport(ctx context.Context, in *ExportMemosRequest, opts ...grpc.CallOption) (MemoService_MemosExportClient, error) { +func (c *memoServiceClient) MemosExport(ctx context.Context, in *MemosExportRequest, opts ...grpc.CallOption) (MemoService_MemosExportClient, error) { stream, err := c.cc.NewStream(ctx, &MemoService_ServiceDesc.Streams[0], MemoService_MemosExport_FullMethodName, opts...) if err != nil { return nil, err @@ -209,7 +209,7 @@ func (c *memoServiceClient) MemosExport(ctx context.Context, in *ExportMemosRequ } type MemoService_MemosExportClient interface { - Recv() (*ExportMemosResponse, error) + Recv() (*MemosExportResponse, error) grpc.ClientStream } @@ -217,8 +217,8 @@ type memoServiceMemosExportClient struct { grpc.ClientStream } -func (x *memoServiceMemosExportClient) Recv() (*ExportMemosResponse, error) { - m := new(ExportMemosResponse) +func (x *memoServiceMemosExportClient) Recv() (*MemosExportResponse, error) { + m := new(MemosExportResponse) if err := x.ClientStream.RecvMsg(m); err != nil { return nil, err } @@ -255,7 +255,7 @@ type MemoServiceServer interface { ListMemoComments(context.Context, *ListMemoCommentsRequest) (*ListMemoCommentsResponse, error) // GetUserMemosStats gets stats of memos for a user. GetUserMemosStats(context.Context, *GetUserMemosStatsRequest) (*GetUserMemosStatsResponse, error) - MemosExport(*ExportMemosRequest, MemoService_MemosExportServer) error + MemosExport(*MemosExportRequest, MemoService_MemosExportServer) error mustEmbedUnimplementedMemoServiceServer() } @@ -302,7 +302,7 @@ func (UnimplementedMemoServiceServer) ListMemoComments(context.Context, *ListMem func (UnimplementedMemoServiceServer) GetUserMemosStats(context.Context, *GetUserMemosStatsRequest) (*GetUserMemosStatsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetUserMemosStats not implemented") } -func (UnimplementedMemoServiceServer) MemosExport(*ExportMemosRequest, MemoService_MemosExportServer) error { +func (UnimplementedMemoServiceServer) MemosExport(*MemosExportRequest, MemoService_MemosExportServer) error { return status.Errorf(codes.Unimplemented, "method MemosExport not implemented") } func (UnimplementedMemoServiceServer) mustEmbedUnimplementedMemoServiceServer() {} @@ -553,7 +553,7 @@ func _MemoService_GetUserMemosStats_Handler(srv interface{}, ctx context.Context } func _MemoService_MemosExport_Handler(srv interface{}, stream grpc.ServerStream) error { - m := new(ExportMemosRequest) + m := new(MemosExportRequest) if err := stream.RecvMsg(m); err != nil { return err } @@ -561,7 +561,7 @@ func _MemoService_MemosExport_Handler(srv interface{}, stream grpc.ServerStream) } type MemoService_MemosExportServer interface { - Send(*ExportMemosResponse) error + Send(*MemosExportResponse) error grpc.ServerStream } @@ -569,7 +569,7 @@ type memoServiceMemosExportServer struct { grpc.ServerStream } -func (x *memoServiceMemosExportServer) Send(m *ExportMemosResponse) error { +func (x *memoServiceMemosExportServer) Send(m *MemosExportResponse) error { return x.ServerStream.SendMsg(m) } diff --git a/web/src/components/ShareMemoDialog.tsx b/web/src/components/ShareMemoDialog.tsx index 8093998869f2a..45a267087ad1b 100644 --- a/web/src/components/ShareMemoDialog.tsx +++ b/web/src/components/ShareMemoDialog.tsx @@ -137,6 +137,6 @@ export default function showShareMemoDialog(memo: Memo): void { dialogName: "share-memo-dialog", }, ShareMemoDialog, - { memo } + { memo }, ); } From b200a3c94eed29379c6d1fa818b61edb50e0471f Mon Sep 17 00:00:00 2001 From: Noah Alderton <noahlouisalderton@gmail.com> Date: Tue, 30 Jan 2024 00:36:07 -0800 Subject: [PATCH 5/5] Change MemosExport to ExportMemos --- api/v2/memo_service.go | 6 +- proto/api/v2/memo_service.proto | 6 +- proto/gen/api/v2/README.md | 66 +++++++++---------- proto/gen/api/v2/memo_service.pb.go | 66 +++++++++---------- proto/gen/api/v2/memo_service.pb.gw.go | 24 +++---- proto/gen/api/v2/memo_service_grpc.pb.go | 44 ++++++------- .../components/Settings/MyAccountSection.tsx | 2 +- 7 files changed, 107 insertions(+), 107 deletions(-) diff --git a/api/v2/memo_service.go b/api/v2/memo_service.go index 75e426695e680..18e3e710ac8ea 100644 --- a/api/v2/memo_service.go +++ b/api/v2/memo_service.go @@ -549,7 +549,7 @@ func (s *APIV2Service) GetUserMemosStats(ctx context.Context, request *apiv2pb.G return response, nil } -func (s *APIV2Service) MemosExport(request *apiv2pb.MemosExportRequest, srv apiv2pb.MemoService_MemosExportServer) error { +func (s *APIV2Service) ExportMemos(request *apiv2pb.ExportMemosRequest, srv apiv2pb.MemoService_ExportMemosServer) error { ctx := srv.Context() fmt.Printf("%+v\n", ctx) memoFind, err := s.buildFindMemosWithFilter(ctx, request.Filter, true) @@ -586,7 +586,7 @@ func (s *APIV2Service) MemosExport(request *apiv2pb.MemosExportRequest, srv apiv return status.Errorf(codes.Internal, "Failed to close zip file writer") } - exportChunk := &apiv2pb.MemosExportResponse{} + exportChunk := &apiv2pb.ExportMemosResponse{} sizeOfFile := len(buf.Bytes()) for currentByte := 0; currentByte < sizeOfFile; currentByte += ChunkSize { if currentByte+ChunkSize > sizeOfFile { @@ -597,7 +597,7 @@ func (s *APIV2Service) MemosExport(request *apiv2pb.MemosExportRequest, srv apiv err := srv.Send(exportChunk) if err != nil { - return status.Error(codes.Internal, "Unable to stream MemosExportResponse chunk") + return status.Error(codes.Internal, "Unable to stream ExportMemosResponse chunk") } } diff --git a/proto/api/v2/memo_service.proto b/proto/api/v2/memo_service.proto index 3e8377c11ba95..988fd369e89e4 100644 --- a/proto/api/v2/memo_service.proto +++ b/proto/api/v2/memo_service.proto @@ -91,7 +91,7 @@ service MemoService { option (google.api.method_signature) = "username"; } - rpc MemosExport(MemosExportRequest) returns (stream MemosExportResponse) { + rpc ExportMemos(ExportMemosRequest) returns (stream ExportMemosResponse) { option (google.api.http) = {get: "/api/v2/memos/export"}; } } @@ -278,11 +278,11 @@ message GetUserMemosStatsResponse { map<string, int32> stats = 1; } -message MemosExportRequest { +message ExportMemosRequest { // Same as ListMemosRequest.filter string filter = 1; } -message MemosExportResponse { +message ExportMemosResponse { bytes file = 1; } diff --git a/proto/gen/api/v2/README.md b/proto/gen/api/v2/README.md index cd67434d14740..f2fee3e4f1205 100644 --- a/proto/gen/api/v2/README.md +++ b/proto/gen/api/v2/README.md @@ -134,6 +134,8 @@ - [CreateMemoResponse](#memos-api-v2-CreateMemoResponse) - [DeleteMemoRequest](#memos-api-v2-DeleteMemoRequest) - [DeleteMemoResponse](#memos-api-v2-DeleteMemoResponse) + - [ExportMemosRequest](#memos-api-v2-ExportMemosRequest) + - [ExportMemosResponse](#memos-api-v2-ExportMemosResponse) - [GetMemoByNameRequest](#memos-api-v2-GetMemoByNameRequest) - [GetMemoByNameResponse](#memos-api-v2-GetMemoByNameResponse) - [GetMemoRequest](#memos-api-v2-GetMemoRequest) @@ -150,8 +152,6 @@ - [ListMemosRequest](#memos-api-v2-ListMemosRequest) - [ListMemosResponse](#memos-api-v2-ListMemosResponse) - [Memo](#memos-api-v2-Memo) - - [MemosExportRequest](#memos-api-v2-MemosExportRequest) - - [MemosExportResponse](#memos-api-v2-MemosExportResponse) - [SetMemoRelationsRequest](#memos-api-v2-SetMemoRelationsRequest) - [SetMemoRelationsResponse](#memos-api-v2-SetMemoRelationsResponse) - [SetMemoResourcesRequest](#memos-api-v2-SetMemoResourcesRequest) @@ -1941,6 +1941,36 @@ Used internally for obfuscating the page token. +<a name="memos-api-v2-ExportMemosRequest"></a> + +### ExportMemosRequest + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| filter | [string](#string) | | Same as ListMemosRequest.filter | + + + + + + +<a name="memos-api-v2-ExportMemosResponse"></a> + +### ExportMemosResponse + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| file | [bytes](#bytes) | | | + + + + + + <a name="memos-api-v2-GetMemoByNameRequest"></a> ### GetMemoByNameRequest @@ -2201,36 +2231,6 @@ Used internally for obfuscating the page token. -<a name="memos-api-v2-MemosExportRequest"></a> - -### MemosExportRequest - - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| filter | [string](#string) | | Same as ListMemosRequest.filter | - - - - - - -<a name="memos-api-v2-MemosExportResponse"></a> - -### MemosExportResponse - - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| file | [bytes](#bytes) | | | - - - - - - <a name="memos-api-v2-SetMemoRelationsRequest"></a> ### SetMemoRelationsRequest @@ -2355,7 +2355,7 @@ Used internally for obfuscating the page token. | CreateMemoComment | [CreateMemoCommentRequest](#memos-api-v2-CreateMemoCommentRequest) | [CreateMemoCommentResponse](#memos-api-v2-CreateMemoCommentResponse) | CreateMemoComment creates a comment for a memo. | | ListMemoComments | [ListMemoCommentsRequest](#memos-api-v2-ListMemoCommentsRequest) | [ListMemoCommentsResponse](#memos-api-v2-ListMemoCommentsResponse) | ListMemoComments lists comments for a memo. | | GetUserMemosStats | [GetUserMemosStatsRequest](#memos-api-v2-GetUserMemosStatsRequest) | [GetUserMemosStatsResponse](#memos-api-v2-GetUserMemosStatsResponse) | GetUserMemosStats gets stats of memos for a user. | -| MemosExport | [MemosExportRequest](#memos-api-v2-MemosExportRequest) | [MemosExportResponse](#memos-api-v2-MemosExportResponse) stream | | +| ExportMemos | [ExportMemosRequest](#memos-api-v2-ExportMemosRequest) | [ExportMemosResponse](#memos-api-v2-ExportMemosResponse) stream | | diff --git a/proto/gen/api/v2/memo_service.pb.go b/proto/gen/api/v2/memo_service.pb.go index d3b1c76faa9c6..89a30d24fcb8d 100644 --- a/proto/gen/api/v2/memo_service.pb.go +++ b/proto/gen/api/v2/memo_service.pb.go @@ -1537,7 +1537,7 @@ func (x *GetUserMemosStatsResponse) GetStats() map[string]int32 { return nil } -type MemosExportRequest struct { +type ExportMemosRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields @@ -1546,8 +1546,8 @@ type MemosExportRequest struct { Filter string `protobuf:"bytes,1,opt,name=filter,proto3" json:"filter,omitempty"` } -func (x *MemosExportRequest) Reset() { - *x = MemosExportRequest{} +func (x *ExportMemosRequest) Reset() { + *x = ExportMemosRequest{} if protoimpl.UnsafeEnabled { mi := &file_api_v2_memo_service_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1555,13 +1555,13 @@ func (x *MemosExportRequest) Reset() { } } -func (x *MemosExportRequest) String() string { +func (x *ExportMemosRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*MemosExportRequest) ProtoMessage() {} +func (*ExportMemosRequest) ProtoMessage() {} -func (x *MemosExportRequest) ProtoReflect() protoreflect.Message { +func (x *ExportMemosRequest) ProtoReflect() protoreflect.Message { mi := &file_api_v2_memo_service_proto_msgTypes[27] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1573,19 +1573,19 @@ func (x *MemosExportRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use MemosExportRequest.ProtoReflect.Descriptor instead. -func (*MemosExportRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use ExportMemosRequest.ProtoReflect.Descriptor instead. +func (*ExportMemosRequest) Descriptor() ([]byte, []int) { return file_api_v2_memo_service_proto_rawDescGZIP(), []int{27} } -func (x *MemosExportRequest) GetFilter() string { +func (x *ExportMemosRequest) GetFilter() string { if x != nil { return x.Filter } return "" } -type MemosExportResponse struct { +type ExportMemosResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields @@ -1593,8 +1593,8 @@ type MemosExportResponse struct { File []byte `protobuf:"bytes,1,opt,name=file,proto3" json:"file,omitempty"` } -func (x *MemosExportResponse) Reset() { - *x = MemosExportResponse{} +func (x *ExportMemosResponse) Reset() { + *x = ExportMemosResponse{} if protoimpl.UnsafeEnabled { mi := &file_api_v2_memo_service_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1602,13 +1602,13 @@ func (x *MemosExportResponse) Reset() { } } -func (x *MemosExportResponse) String() string { +func (x *ExportMemosResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*MemosExportResponse) ProtoMessage() {} +func (*ExportMemosResponse) ProtoMessage() {} -func (x *MemosExportResponse) ProtoReflect() protoreflect.Message { +func (x *ExportMemosResponse) ProtoReflect() protoreflect.Message { mi := &file_api_v2_memo_service_proto_msgTypes[28] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1620,12 +1620,12 @@ func (x *MemosExportResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use MemosExportResponse.ProtoReflect.Descriptor instead. -func (*MemosExportResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use ExportMemosResponse.ProtoReflect.Descriptor instead. +func (*ExportMemosResponse) Descriptor() ([]byte, []int) { return file_api_v2_memo_service_proto_rawDescGZIP(), []int{28} } -func (x *MemosExportResponse) GetFile() []byte { +func (x *ExportMemosResponse) GetFile() []byte { if x != nil { return x.File } @@ -1815,10 +1815,10 @@ var file_api_v2_memo_service_proto_rawDesc = []byte{ 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x2c, 0x0a, 0x12, - 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0x29, 0x0a, 0x13, 0x4d, 0x65, - 0x6d, 0x6f, 0x73, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x28, 0x09, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0x29, 0x0a, 0x13, 0x45, 0x78, + 0x70, 0x6f, 0x72, 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x2a, 0x50, 0x0a, 0x0a, 0x56, 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x1a, 0x0a, 0x16, 0x56, 0x49, 0x53, 0x49, 0x42, 0x49, 0x4c, 0x49, 0x54, @@ -1932,12 +1932,12 @@ var file_api_v2_memo_service_proto_rawDesc = []byte{ 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0xda, 0x41, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x6d, - 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x12, 0x72, 0x0a, 0x0b, 0x4d, 0x65, - 0x6d, 0x6f, 0x73, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x20, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, - 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x45, 0x78, - 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6d, 0x65, - 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4d, 0x65, 0x6d, 0x6f, 0x73, - 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, + 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x12, 0x72, 0x0a, 0x0b, 0x45, 0x78, + 0x70, 0x6f, 0x72, 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x12, 0x20, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, + 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x4d, + 0x65, 0x6d, 0x6f, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6d, 0x65, + 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x45, 0x78, 0x70, 0x6f, 0x72, + 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x30, 0x01, 0x42, 0xa8, 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, @@ -1997,8 +1997,8 @@ var file_api_v2_memo_service_proto_goTypes = []interface{}{ (*ListMemoCommentsResponse)(nil), // 25: memos.api.v2.ListMemoCommentsResponse (*GetUserMemosStatsRequest)(nil), // 26: memos.api.v2.GetUserMemosStatsRequest (*GetUserMemosStatsResponse)(nil), // 27: memos.api.v2.GetUserMemosStatsResponse - (*MemosExportRequest)(nil), // 28: memos.api.v2.MemosExportRequest - (*MemosExportResponse)(nil), // 29: memos.api.v2.MemosExportResponse + (*ExportMemosRequest)(nil), // 28: memos.api.v2.ExportMemosRequest + (*ExportMemosResponse)(nil), // 29: memos.api.v2.ExportMemosResponse nil, // 30: memos.api.v2.GetUserMemosStatsResponse.StatsEntry (RowStatus)(0), // 31: memos.api.v2.RowStatus (*timestamppb.Timestamp)(nil), // 32: google.protobuf.Timestamp @@ -2045,7 +2045,7 @@ var file_api_v2_memo_service_proto_depIdxs = []int32{ 22, // 34: memos.api.v2.MemoService.CreateMemoComment:input_type -> memos.api.v2.CreateMemoCommentRequest 24, // 35: memos.api.v2.MemoService.ListMemoComments:input_type -> memos.api.v2.ListMemoCommentsRequest 26, // 36: memos.api.v2.MemoService.GetUserMemosStats:input_type -> memos.api.v2.GetUserMemosStatsRequest - 28, // 37: memos.api.v2.MemoService.MemosExport:input_type -> memos.api.v2.MemosExportRequest + 28, // 37: memos.api.v2.MemoService.ExportMemos:input_type -> memos.api.v2.ExportMemosRequest 3, // 38: memos.api.v2.MemoService.CreateMemo:output_type -> memos.api.v2.CreateMemoResponse 5, // 39: memos.api.v2.MemoService.ListMemos:output_type -> memos.api.v2.ListMemosResponse 7, // 40: memos.api.v2.MemoService.GetMemo:output_type -> memos.api.v2.GetMemoResponse @@ -2059,7 +2059,7 @@ var file_api_v2_memo_service_proto_depIdxs = []int32{ 23, // 48: memos.api.v2.MemoService.CreateMemoComment:output_type -> memos.api.v2.CreateMemoCommentResponse 25, // 49: memos.api.v2.MemoService.ListMemoComments:output_type -> memos.api.v2.ListMemoCommentsResponse 27, // 50: memos.api.v2.MemoService.GetUserMemosStats:output_type -> memos.api.v2.GetUserMemosStatsResponse - 29, // 51: memos.api.v2.MemoService.MemosExport:output_type -> memos.api.v2.MemosExportResponse + 29, // 51: memos.api.v2.MemoService.ExportMemos:output_type -> memos.api.v2.ExportMemosResponse 38, // [38:52] is the sub-list for method output_type 24, // [24:38] is the sub-list for method input_type 24, // [24:24] is the sub-list for extension type_name @@ -2402,7 +2402,7 @@ func file_api_v2_memo_service_proto_init() { } } file_api_v2_memo_service_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MemosExportRequest); i { + switch v := v.(*ExportMemosRequest); i { case 0: return &v.state case 1: @@ -2414,7 +2414,7 @@ func file_api_v2_memo_service_proto_init() { } } file_api_v2_memo_service_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MemosExportResponse); i { + switch v := v.(*ExportMemosResponse); i { case 0: return &v.state case 1: diff --git a/proto/gen/api/v2/memo_service.pb.gw.go b/proto/gen/api/v2/memo_service.pb.gw.go index 0a4686f55b4ec..d46ad1f0647de 100644 --- a/proto/gen/api/v2/memo_service.pb.gw.go +++ b/proto/gen/api/v2/memo_service.pb.gw.go @@ -724,21 +724,21 @@ func local_request_MemoService_GetUserMemosStats_0(ctx context.Context, marshale } var ( - filter_MemoService_MemosExport_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} + filter_MemoService_ExportMemos_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} ) -func request_MemoService_MemosExport_0(ctx context.Context, marshaler runtime.Marshaler, client MemoServiceClient, req *http.Request, pathParams map[string]string) (MemoService_MemosExportClient, runtime.ServerMetadata, error) { - var protoReq MemosExportRequest +func request_MemoService_ExportMemos_0(ctx context.Context, marshaler runtime.Marshaler, client MemoServiceClient, req *http.Request, pathParams map[string]string) (MemoService_ExportMemosClient, runtime.ServerMetadata, error) { + var protoReq ExportMemosRequest var metadata runtime.ServerMetadata if err := req.ParseForm(); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_MemoService_MemosExport_0); err != nil { + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_MemoService_ExportMemos_0); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - stream, err := client.MemosExport(ctx, &protoReq) + stream, err := client.ExportMemos(ctx, &protoReq) if err != nil { return nil, metadata, err } @@ -1082,7 +1082,7 @@ func RegisterMemoServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux }) - mux.Handle("GET", pattern_MemoService_MemosExport_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_MemoService_ExportMemos_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { err := status.Error(codes.Unimplemented, "streaming calls are not yet supported in the in-process transport") _, outboundMarshaler := runtime.MarshalerForRequest(mux, req) runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) @@ -1416,25 +1416,25 @@ func RegisterMemoServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux }) - mux.Handle("GET", pattern_MemoService_MemosExport_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_MemoService_ExportMemos_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) var err error var annotatedContext context.Context - annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/memos.api.v2.MemoService/MemosExport", runtime.WithHTTPPathPattern("/api/v2/memos/export")) + annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/memos.api.v2.MemoService/ExportMemos", runtime.WithHTTPPathPattern("/api/v2/memos/export")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := request_MemoService_MemosExport_0(annotatedContext, inboundMarshaler, client, req, pathParams) + resp, md, err := request_MemoService_ExportMemos_0(annotatedContext, inboundMarshaler, client, req, pathParams) annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) if err != nil { runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) return } - forward_MemoService_MemosExport_0(annotatedContext, mux, outboundMarshaler, w, req, func() (proto.Message, error) { return resp.Recv() }, mux.GetForwardResponseOptions()...) + forward_MemoService_ExportMemos_0(annotatedContext, mux, outboundMarshaler, w, req, func() (proto.Message, error) { return resp.Recv() }, mux.GetForwardResponseOptions()...) }) @@ -1468,7 +1468,7 @@ var ( pattern_MemoService_GetUserMemosStats_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"api", "v2", "memos", "stats"}, "")) - pattern_MemoService_MemosExport_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"api", "v2", "memos", "export"}, "")) + pattern_MemoService_ExportMemos_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"api", "v2", "memos", "export"}, "")) ) var ( @@ -1498,5 +1498,5 @@ var ( forward_MemoService_GetUserMemosStats_0 = runtime.ForwardResponseMessage - forward_MemoService_MemosExport_0 = runtime.ForwardResponseStream + forward_MemoService_ExportMemos_0 = runtime.ForwardResponseStream ) diff --git a/proto/gen/api/v2/memo_service_grpc.pb.go b/proto/gen/api/v2/memo_service_grpc.pb.go index b8cb63718c21d..380e3721d5913 100644 --- a/proto/gen/api/v2/memo_service_grpc.pb.go +++ b/proto/gen/api/v2/memo_service_grpc.pb.go @@ -32,7 +32,7 @@ const ( MemoService_CreateMemoComment_FullMethodName = "/memos.api.v2.MemoService/CreateMemoComment" MemoService_ListMemoComments_FullMethodName = "/memos.api.v2.MemoService/ListMemoComments" MemoService_GetUserMemosStats_FullMethodName = "/memos.api.v2.MemoService/GetUserMemosStats" - MemoService_MemosExport_FullMethodName = "/memos.api.v2.MemoService/MemosExport" + MemoService_ExportMemos_FullMethodName = "/memos.api.v2.MemoService/ExportMemos" ) // MemoServiceClient is the client API for MemoService service. @@ -65,7 +65,7 @@ type MemoServiceClient interface { ListMemoComments(ctx context.Context, in *ListMemoCommentsRequest, opts ...grpc.CallOption) (*ListMemoCommentsResponse, error) // GetUserMemosStats gets stats of memos for a user. GetUserMemosStats(ctx context.Context, in *GetUserMemosStatsRequest, opts ...grpc.CallOption) (*GetUserMemosStatsResponse, error) - MemosExport(ctx context.Context, in *MemosExportRequest, opts ...grpc.CallOption) (MemoService_MemosExportClient, error) + ExportMemos(ctx context.Context, in *ExportMemosRequest, opts ...grpc.CallOption) (MemoService_ExportMemosClient, error) } type memoServiceClient struct { @@ -193,12 +193,12 @@ func (c *memoServiceClient) GetUserMemosStats(ctx context.Context, in *GetUserMe return out, nil } -func (c *memoServiceClient) MemosExport(ctx context.Context, in *MemosExportRequest, opts ...grpc.CallOption) (MemoService_MemosExportClient, error) { - stream, err := c.cc.NewStream(ctx, &MemoService_ServiceDesc.Streams[0], MemoService_MemosExport_FullMethodName, opts...) +func (c *memoServiceClient) ExportMemos(ctx context.Context, in *ExportMemosRequest, opts ...grpc.CallOption) (MemoService_ExportMemosClient, error) { + stream, err := c.cc.NewStream(ctx, &MemoService_ServiceDesc.Streams[0], MemoService_ExportMemos_FullMethodName, opts...) if err != nil { return nil, err } - x := &memoServiceMemosExportClient{stream} + x := &memoServiceExportMemosClient{stream} if err := x.ClientStream.SendMsg(in); err != nil { return nil, err } @@ -208,17 +208,17 @@ func (c *memoServiceClient) MemosExport(ctx context.Context, in *MemosExportRequ return x, nil } -type MemoService_MemosExportClient interface { - Recv() (*MemosExportResponse, error) +type MemoService_ExportMemosClient interface { + Recv() (*ExportMemosResponse, error) grpc.ClientStream } -type memoServiceMemosExportClient struct { +type memoServiceExportMemosClient struct { grpc.ClientStream } -func (x *memoServiceMemosExportClient) Recv() (*MemosExportResponse, error) { - m := new(MemosExportResponse) +func (x *memoServiceExportMemosClient) Recv() (*ExportMemosResponse, error) { + m := new(ExportMemosResponse) if err := x.ClientStream.RecvMsg(m); err != nil { return nil, err } @@ -255,7 +255,7 @@ type MemoServiceServer interface { ListMemoComments(context.Context, *ListMemoCommentsRequest) (*ListMemoCommentsResponse, error) // GetUserMemosStats gets stats of memos for a user. GetUserMemosStats(context.Context, *GetUserMemosStatsRequest) (*GetUserMemosStatsResponse, error) - MemosExport(*MemosExportRequest, MemoService_MemosExportServer) error + ExportMemos(*ExportMemosRequest, MemoService_ExportMemosServer) error mustEmbedUnimplementedMemoServiceServer() } @@ -302,8 +302,8 @@ func (UnimplementedMemoServiceServer) ListMemoComments(context.Context, *ListMem func (UnimplementedMemoServiceServer) GetUserMemosStats(context.Context, *GetUserMemosStatsRequest) (*GetUserMemosStatsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetUserMemosStats not implemented") } -func (UnimplementedMemoServiceServer) MemosExport(*MemosExportRequest, MemoService_MemosExportServer) error { - return status.Errorf(codes.Unimplemented, "method MemosExport not implemented") +func (UnimplementedMemoServiceServer) ExportMemos(*ExportMemosRequest, MemoService_ExportMemosServer) error { + return status.Errorf(codes.Unimplemented, "method ExportMemos not implemented") } func (UnimplementedMemoServiceServer) mustEmbedUnimplementedMemoServiceServer() {} @@ -552,24 +552,24 @@ func _MemoService_GetUserMemosStats_Handler(srv interface{}, ctx context.Context return interceptor(ctx, in, info, handler) } -func _MemoService_MemosExport_Handler(srv interface{}, stream grpc.ServerStream) error { - m := new(MemosExportRequest) +func _MemoService_ExportMemos_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(ExportMemosRequest) if err := stream.RecvMsg(m); err != nil { return err } - return srv.(MemoServiceServer).MemosExport(m, &memoServiceMemosExportServer{stream}) + return srv.(MemoServiceServer).ExportMemos(m, &memoServiceExportMemosServer{stream}) } -type MemoService_MemosExportServer interface { - Send(*MemosExportResponse) error +type MemoService_ExportMemosServer interface { + Send(*ExportMemosResponse) error grpc.ServerStream } -type memoServiceMemosExportServer struct { +type memoServiceExportMemosServer struct { grpc.ServerStream } -func (x *memoServiceMemosExportServer) Send(m *MemosExportResponse) error { +func (x *memoServiceExportMemosServer) Send(m *ExportMemosResponse) error { return x.ServerStream.SendMsg(m) } @@ -635,8 +635,8 @@ var MemoService_ServiceDesc = grpc.ServiceDesc{ }, Streams: []grpc.StreamDesc{ { - StreamName: "MemosExport", - Handler: _MemoService_MemosExport_Handler, + StreamName: "ExportMemos", + Handler: _MemoService_ExportMemos_Handler, ServerStreams: true, }, }, diff --git a/web/src/components/Settings/MyAccountSection.tsx b/web/src/components/Settings/MyAccountSection.tsx index fdbeda2e2b58d..5830b6fc9e447 100644 --- a/web/src/components/Settings/MyAccountSection.tsx +++ b/web/src/components/Settings/MyAccountSection.tsx @@ -14,7 +14,7 @@ const MyAccountSection = () => { const downloadExportedMemos = async (user: any) => { const chunks = []; - for await (const response of memoServiceClient.memosExport({ filter: `creator == "${user.name}"` })) { + for await (const response of memoServiceClient.exportMemos({ filter: `creator == "${user.name}"` })) { chunks.push(response.file.buffer); } const blob = new Blob(chunks);