From ed5ed8c7a116955ddcf15b3439f567b67cf8e4ae Mon Sep 17 00:00:00 2001 From: Samuel N Cui Date: Fri, 20 Oct 2023 20:22:07 +0800 Subject: [PATCH] feat: add get disk usage and toast infomation --- apis/file_get.go | 31 +- apis/source_get_size.go | 31 + apis/source_list.go | 3 - cmd/httpd/yatm-httpd.service | 1 + entity/service.pb.go | 801 +++++++++++++---------- entity/service.proto | 11 + entity/service_grpc.pb.go | 36 + frontend/package.json | 1 + frontend/src/{actions.ts => actions.tsx} | 27 +- frontend/src/api.ts | 118 +--- frontend/src/app.tsx | 43 +- frontend/src/components/job-archive.tsx | 7 +- frontend/src/components/toolbarInfo.tsx | 26 +- frontend/src/entity/service.client.ts | 19 +- frontend/src/entity/service.ts | 126 +++- frontend/src/init.tsx | 42 +- frontend/src/pages/backup.tsx | 117 +++- frontend/src/pages/file.tsx | 33 +- frontend/src/pages/jobs.tsx | 7 +- frontend/src/pages/restore.tsx | 148 +++-- frontend/src/pages/tapes.tsx | 3 +- 21 files changed, 1064 insertions(+), 567 deletions(-) create mode 100644 apis/source_get_size.go rename frontend/src/{actions.ts => actions.tsx} (66%) diff --git a/apis/file_get.go b/apis/file_get.go index 9560f9e..6ff53f0 100644 --- a/apis/file_get.go +++ b/apis/file_get.go @@ -4,6 +4,7 @@ import ( "context" "errors" + "github.com/samber/lo" "github.com/samuelncui/yatm/entity" "github.com/samuelncui/yatm/library" ) @@ -24,14 +25,28 @@ func (api *API) FileGet(ctx context.Context, req *entity.FileGetRequest) (*entit return nil, err } - children, err := api.lib.ListWithSize(ctx, req.Id) - if err != nil { - return nil, err - } - - return &entity.FileGetReply{ + reply := &entity.FileGetReply{ File: file, Positions: convertPositions(positions...), - Children: convertFiles(children...), - }, nil + } + + if req.GetNeedSize() { + children, err := api.lib.ListWithSize(ctx, req.Id) + if err != nil { + return nil, err + } + reply.Children = convertFiles(children...) + + if reply.File != nil { + reply.File.Size += lo.Sum(lo.Map(children, func(file *library.File, _ int) int64 { return file.Size })) + } + } else { + children, err := api.lib.List(ctx, req.Id) + if err != nil { + return nil, err + } + reply.Children = convertFiles(children...) + } + + return reply, nil } diff --git a/apis/source_get_size.go b/apis/source_get_size.go new file mode 100644 index 0000000..e3885cc --- /dev/null +++ b/apis/source_get_size.go @@ -0,0 +1,31 @@ +package apis + +import ( + "context" + "os" + "path" + "path/filepath" + + "github.com/samuelncui/yatm/entity" +) + +func (api *API) SourceGetSize(ctx context.Context, req *entity.SourceGetSizeRequest) (*entity.SourceGetSizeReply, error) { + if req.Path == "./" { + req.Path = "" + } + + var size int64 + if err := filepath.Walk(path.Join(api.sourceBase, req.Path), func(_ string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if !info.IsDir() { + size += info.Size() + } + return err + }); err != nil { + return nil, err + } + + return &entity.SourceGetSizeReply{Size: size}, nil +} diff --git a/apis/source_list.go b/apis/source_list.go index 1788239..3c08e48 100644 --- a/apis/source_list.go +++ b/apis/source_list.go @@ -26,9 +26,6 @@ func (api *API) SourceList(ctx context.Context, req *entity.SourceListRequest) ( filteredParts = append(filteredParts, part) } - // buf, _ := json.Marshal(filteredParts) - // logrus.WithContext(ctx).Infof("parts= %s", buf) - current := "" chain := make([]*entity.SourceFile, 0, len(filteredParts)) for _, part := range filteredParts { diff --git a/cmd/httpd/yatm-httpd.service b/cmd/httpd/yatm-httpd.service index a10e4f6..591a62d 100644 --- a/cmd/httpd/yatm-httpd.service +++ b/cmd/httpd/yatm-httpd.service @@ -6,6 +6,7 @@ After=network.target [Service] User=root Type=simple +UMask=0002 WorkingDirectory=/opt/yatm ExecStart=/opt/yatm/yatm-httpd Restart=always diff --git a/entity/service.pb.go b/entity/service.pb.go index a59484b..edb1699 100644 --- a/entity/service.pb.go +++ b/entity/service.pb.go @@ -25,7 +25,8 @@ type FileGetRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + NeedSize *bool `protobuf:"varint,17,opt,name=needSize,proto3,oneof" json:"needSize,omitempty"` } func (x *FileGetRequest) Reset() { @@ -67,6 +68,13 @@ func (x *FileGetRequest) GetId() int64 { return 0 } +func (x *FileGetRequest) GetNeedSize() bool { + if x != nil && x.NeedSize != nil { + return *x.NeedSize + } + return false +} + type FileGetReply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1749,6 +1757,100 @@ func (x *SourceListReply) GetChildren() []*SourceFile { return nil } +type SourceGetSizeRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` +} + +func (x *SourceGetSizeRequest) Reset() { + *x = SourceGetSizeRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_service_proto_msgTypes[34] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SourceGetSizeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SourceGetSizeRequest) ProtoMessage() {} + +func (x *SourceGetSizeRequest) ProtoReflect() protoreflect.Message { + mi := &file_service_proto_msgTypes[34] + 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 SourceGetSizeRequest.ProtoReflect.Descriptor instead. +func (*SourceGetSizeRequest) Descriptor() ([]byte, []int) { + return file_service_proto_rawDescGZIP(), []int{34} +} + +func (x *SourceGetSizeRequest) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +type SourceGetSizeReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Size int64 `protobuf:"varint,1,opt,name=size,proto3" json:"size,omitempty"` +} + +func (x *SourceGetSizeReply) Reset() { + *x = SourceGetSizeReply{} + if protoimpl.UnsafeEnabled { + mi := &file_service_proto_msgTypes[35] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SourceGetSizeReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SourceGetSizeReply) ProtoMessage() {} + +func (x *SourceGetSizeReply) ProtoReflect() protoreflect.Message { + mi := &file_service_proto_msgTypes[35] + 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 SourceGetSizeReply.ProtoReflect.Descriptor instead. +func (*SourceGetSizeReply) Descriptor() ([]byte, []int) { + return file_service_proto_rawDescGZIP(), []int{35} +} + +func (x *SourceGetSizeReply) GetSize() int64 { + if x != nil { + return x.Size + } + return 0 +} + type DeviceListRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1758,7 +1860,7 @@ type DeviceListRequest struct { func (x *DeviceListRequest) Reset() { *x = DeviceListRequest{} if protoimpl.UnsafeEnabled { - mi := &file_service_proto_msgTypes[34] + mi := &file_service_proto_msgTypes[36] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1771,7 +1873,7 @@ func (x *DeviceListRequest) String() string { func (*DeviceListRequest) ProtoMessage() {} func (x *DeviceListRequest) ProtoReflect() protoreflect.Message { - mi := &file_service_proto_msgTypes[34] + mi := &file_service_proto_msgTypes[36] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1784,7 +1886,7 @@ func (x *DeviceListRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeviceListRequest.ProtoReflect.Descriptor instead. func (*DeviceListRequest) Descriptor() ([]byte, []int) { - return file_service_proto_rawDescGZIP(), []int{34} + return file_service_proto_rawDescGZIP(), []int{36} } type DeviceListReply struct { @@ -1798,7 +1900,7 @@ type DeviceListReply struct { func (x *DeviceListReply) Reset() { *x = DeviceListReply{} if protoimpl.UnsafeEnabled { - mi := &file_service_proto_msgTypes[35] + mi := &file_service_proto_msgTypes[37] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1811,7 +1913,7 @@ func (x *DeviceListReply) String() string { func (*DeviceListReply) ProtoMessage() {} func (x *DeviceListReply) ProtoReflect() protoreflect.Message { - mi := &file_service_proto_msgTypes[35] + mi := &file_service_proto_msgTypes[37] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1824,7 +1926,7 @@ func (x *DeviceListReply) ProtoReflect() protoreflect.Message { // Deprecated: Use DeviceListReply.ProtoReflect.Descriptor instead. func (*DeviceListReply) Descriptor() ([]byte, []int) { - return file_service_proto_rawDescGZIP(), []int{35} + return file_service_proto_rawDescGZIP(), []int{37} } func (x *DeviceListReply) GetDevices() []string { @@ -1845,7 +1947,7 @@ type LibraryExportRequest struct { func (x *LibraryExportRequest) Reset() { *x = LibraryExportRequest{} if protoimpl.UnsafeEnabled { - mi := &file_service_proto_msgTypes[36] + mi := &file_service_proto_msgTypes[38] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1858,7 +1960,7 @@ func (x *LibraryExportRequest) String() string { func (*LibraryExportRequest) ProtoMessage() {} func (x *LibraryExportRequest) ProtoReflect() protoreflect.Message { - mi := &file_service_proto_msgTypes[36] + mi := &file_service_proto_msgTypes[38] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1871,7 +1973,7 @@ func (x *LibraryExportRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use LibraryExportRequest.ProtoReflect.Descriptor instead. func (*LibraryExportRequest) Descriptor() ([]byte, []int) { - return file_service_proto_rawDescGZIP(), []int{36} + return file_service_proto_rawDescGZIP(), []int{38} } func (x *LibraryExportRequest) GetTypes() []LibraryEntityType { @@ -1892,7 +1994,7 @@ type LibraryExportReply struct { func (x *LibraryExportReply) Reset() { *x = LibraryExportReply{} if protoimpl.UnsafeEnabled { - mi := &file_service_proto_msgTypes[37] + mi := &file_service_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1905,7 +2007,7 @@ func (x *LibraryExportReply) String() string { func (*LibraryExportReply) ProtoMessage() {} func (x *LibraryExportReply) ProtoReflect() protoreflect.Message { - mi := &file_service_proto_msgTypes[37] + mi := &file_service_proto_msgTypes[39] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1918,7 +2020,7 @@ func (x *LibraryExportReply) ProtoReflect() protoreflect.Message { // Deprecated: Use LibraryExportReply.ProtoReflect.Descriptor instead. func (*LibraryExportReply) Descriptor() ([]byte, []int) { - return file_service_proto_rawDescGZIP(), []int{37} + return file_service_proto_rawDescGZIP(), []int{39} } func (x *LibraryExportReply) GetJson() []byte { @@ -1940,7 +2042,7 @@ type LibraryTrimRequest struct { func (x *LibraryTrimRequest) Reset() { *x = LibraryTrimRequest{} if protoimpl.UnsafeEnabled { - mi := &file_service_proto_msgTypes[38] + mi := &file_service_proto_msgTypes[40] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1953,7 +2055,7 @@ func (x *LibraryTrimRequest) String() string { func (*LibraryTrimRequest) ProtoMessage() {} func (x *LibraryTrimRequest) ProtoReflect() protoreflect.Message { - mi := &file_service_proto_msgTypes[38] + mi := &file_service_proto_msgTypes[40] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1966,7 +2068,7 @@ func (x *LibraryTrimRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use LibraryTrimRequest.ProtoReflect.Descriptor instead. func (*LibraryTrimRequest) Descriptor() ([]byte, []int) { - return file_service_proto_rawDescGZIP(), []int{38} + return file_service_proto_rawDescGZIP(), []int{40} } func (x *LibraryTrimRequest) GetTrimPosition() bool { @@ -1992,7 +2094,7 @@ type LibraryTrimReply struct { func (x *LibraryTrimReply) Reset() { *x = LibraryTrimReply{} if protoimpl.UnsafeEnabled { - mi := &file_service_proto_msgTypes[39] + mi := &file_service_proto_msgTypes[41] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2005,7 +2107,7 @@ func (x *LibraryTrimReply) String() string { func (*LibraryTrimReply) ProtoMessage() {} func (x *LibraryTrimReply) ProtoReflect() protoreflect.Message { - mi := &file_service_proto_msgTypes[39] + mi := &file_service_proto_msgTypes[41] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2018,7 +2120,7 @@ func (x *LibraryTrimReply) ProtoReflect() protoreflect.Message { // Deprecated: Use LibraryTrimReply.ProtoReflect.Descriptor instead. func (*LibraryTrimReply) Descriptor() ([]byte, []int) { - return file_service_proto_rawDescGZIP(), []int{39} + return file_service_proto_rawDescGZIP(), []int{41} } var File_service_proto protoreflect.FileDescriptor @@ -2031,246 +2133,260 @@ var file_service_proto_rawDesc = []byte{ 0x0a, 0x74, 0x61, 0x70, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x19, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x5f, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x20, 0x0a, 0x0e, 0x46, 0x69, 0x6c, 0x65, 0x47, 0x65, 0x74, 0x52, + 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x4e, 0x0a, 0x0e, 0x46, 0x69, 0x6c, 0x65, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x22, 0x96, 0x01, 0x0a, 0x0c, 0x46, 0x69, 0x6c, 0x65, 0x47, - 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x23, 0x0a, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x2e, 0x46, 0x69, 0x6c, - 0x65, 0x48, 0x00, 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x88, 0x01, 0x01, 0x12, 0x30, 0x0a, 0x09, - 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x12, 0x2e, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x50, 0x6f, 0x73, 0x69, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x26, - 0x0a, 0x08, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x72, 0x65, 0x6e, 0x18, 0x11, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x0a, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x08, 0x63, 0x68, - 0x69, 0x6c, 0x64, 0x72, 0x65, 0x6e, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x22, - 0x47, 0x0a, 0x0f, 0x46, 0x69, 0x6c, 0x65, 0x45, 0x64, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, - 0x69, 0x64, 0x12, 0x24, 0x0a, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x10, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x2e, 0x45, 0x64, 0x69, 0x74, 0x65, 0x64, 0x46, 0x69, - 0x6c, 0x65, 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x22, 0x2f, 0x0a, 0x0d, 0x46, 0x69, 0x6c, 0x65, - 0x45, 0x64, 0x69, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1e, 0x0a, 0x04, 0x66, 0x69, 0x6c, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x2e, 0x46, - 0x69, 0x6c, 0x65, 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x22, 0x43, 0x0a, 0x10, 0x46, 0x69, 0x6c, - 0x65, 0x4d, 0x6b, 0x64, 0x69, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, - 0x09, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x08, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, - 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x30, - 0x0a, 0x0e, 0x46, 0x69, 0x6c, 0x65, 0x4d, 0x6b, 0x64, 0x69, 0x72, 0x52, 0x65, 0x70, 0x6c, 0x79, - 0x12, 0x1e, 0x0a, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, - 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, - 0x22, 0x25, 0x0a, 0x11, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x03, 0x52, 0x03, 0x69, 0x64, 0x73, 0x22, 0x11, 0x0a, 0x0f, 0x46, 0x69, 0x6c, 0x65, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x28, 0x0a, 0x16, 0x46, 0x69, - 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x02, 0x69, 0x64, 0x22, 0x3c, 0x0a, 0x14, 0x46, 0x69, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, - 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x24, 0x0a, 0x07, - 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, - 0x66, 0x69, 0x6c, 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x07, 0x70, 0x61, 0x72, 0x65, 0x6e, - 0x74, 0x73, 0x22, 0x72, 0x0a, 0x0f, 0x54, 0x61, 0x70, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x04, 0x6d, 0x67, 0x65, 0x74, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x54, 0x61, - 0x70, 0x65, 0x4d, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, - 0x04, 0x6d, 0x67, 0x65, 0x74, 0x12, 0x26, 0x0a, 0x04, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x61, 0x70, 0x65, 0x2e, 0x54, 0x61, 0x70, 0x65, 0x46, - 0x69, 0x6c, 0x74, 0x65, 0x72, 0x48, 0x00, 0x52, 0x04, 0x6c, 0x69, 0x73, 0x74, 0x42, 0x07, 0x0a, - 0x05, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x22, 0x23, 0x0a, 0x0f, 0x54, 0x61, 0x70, 0x65, 0x4d, 0x47, - 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x64, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x03, 0x52, 0x03, 0x69, 0x64, 0x73, 0x22, 0x31, 0x0a, 0x0d, 0x54, - 0x61, 0x70, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x20, 0x0a, 0x05, - 0x74, 0x61, 0x70, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x74, 0x61, - 0x70, 0x65, 0x2e, 0x54, 0x61, 0x70, 0x65, 0x52, 0x05, 0x74, 0x61, 0x70, 0x65, 0x73, 0x22, 0x25, - 0x0a, 0x11, 0x54, 0x61, 0x70, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x03, - 0x52, 0x03, 0x69, 0x64, 0x73, 0x22, 0x11, 0x0a, 0x0f, 0x54, 0x61, 0x70, 0x65, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x47, 0x0a, 0x17, 0x54, 0x61, 0x70, 0x65, - 0x47, 0x65, 0x74, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x02, 0x69, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, - 0x79, 0x22, 0x49, 0x0a, 0x15, 0x54, 0x61, 0x70, 0x65, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x73, 0x69, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x30, 0x0a, 0x09, 0x70, 0x6f, - 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, + 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x08, 0x6e, 0x65, 0x65, 0x64, 0x53, 0x69, + 0x7a, 0x65, 0x18, 0x11, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x08, 0x6e, 0x65, 0x65, 0x64, + 0x53, 0x69, 0x7a, 0x65, 0x88, 0x01, 0x01, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x6e, 0x65, 0x65, 0x64, + 0x53, 0x69, 0x7a, 0x65, 0x22, 0x96, 0x01, 0x0a, 0x0c, 0x46, 0x69, 0x6c, 0x65, 0x47, 0x65, 0x74, + 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x23, 0x0a, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x48, + 0x00, 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x88, 0x01, 0x01, 0x12, 0x30, 0x0a, 0x09, 0x70, 0x6f, + 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x09, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xb7, 0x01, 0x0a, - 0x0e, 0x4a, 0x6f, 0x62, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x2d, 0x0a, 0x04, 0x6d, 0x67, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x4d, 0x47, 0x65, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x04, 0x6d, 0x67, 0x65, 0x74, 0x12, 0x24, - 0x0a, 0x04, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6a, - 0x6f, 0x62, 0x2e, 0x4a, 0x6f, 0x62, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x48, 0x00, 0x52, 0x04, - 0x6c, 0x69, 0x73, 0x74, 0x12, 0x47, 0x0a, 0x0f, 0x72, 0x65, 0x63, 0x65, 0x6e, 0x74, 0x6c, 0x79, - 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, - 0x6a, 0x6f, 0x62, 0x2e, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x63, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x48, 0x00, 0x52, 0x0e, 0x72, - 0x65, 0x63, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x42, 0x07, 0x0a, - 0x05, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x22, 0x22, 0x0a, 0x0e, 0x4a, 0x6f, 0x62, 0x4d, 0x47, 0x65, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x64, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x03, 0x52, 0x03, 0x69, 0x64, 0x73, 0x22, 0x2c, 0x0a, 0x0c, 0x4a, 0x6f, - 0x62, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1c, 0x0a, 0x04, 0x6a, 0x6f, - 0x62, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x08, 0x2e, 0x6a, 0x6f, 0x62, 0x2e, 0x4a, - 0x6f, 0x62, 0x52, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x22, 0x37, 0x0a, 0x10, 0x4a, 0x6f, 0x62, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x03, - 0x6a, 0x6f, 0x62, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6a, 0x6f, 0x62, 0x2e, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x03, 0x6a, 0x6f, - 0x62, 0x22, 0x2c, 0x0a, 0x0e, 0x4a, 0x6f, 0x62, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, - 0x70, 0x6c, 0x79, 0x12, 0x1a, 0x0a, 0x03, 0x6a, 0x6f, 0x62, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x08, 0x2e, 0x6a, 0x6f, 0x62, 0x2e, 0x4a, 0x6f, 0x62, 0x52, 0x03, 0x6a, 0x6f, 0x62, 0x22, - 0x91, 0x01, 0x0a, 0x13, 0x4a, 0x6f, 0x62, 0x45, 0x64, 0x69, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2b, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x6a, 0x6f, 0x62, 0x2e, 0x4a, 0x6f, - 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x48, 0x00, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x88, 0x01, 0x01, 0x12, 0x28, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x6a, 0x6f, 0x62, 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x48, 0x01, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x88, 0x01, 0x01, 0x42, 0x09, - 0x0a, 0x07, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x73, 0x74, - 0x61, 0x74, 0x65, 0x22, 0x13, 0x0a, 0x11, 0x4a, 0x6f, 0x62, 0x45, 0x64, 0x69, 0x74, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x24, 0x0a, 0x10, 0x4a, 0x6f, 0x62, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, - 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x03, 0x52, 0x03, 0x69, 0x64, 0x73, 0x22, 0x10, - 0x0a, 0x0e, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, - 0x22, 0x51, 0x0a, 0x12, 0x4a, 0x6f, 0x62, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2b, 0x0a, 0x05, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6a, 0x6f, 0x62, 0x2e, 0x4a, 0x6f, 0x62, 0x44, - 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x52, 0x05, 0x70, 0x61, - 0x72, 0x61, 0x6d, 0x22, 0x12, 0x0a, 0x10, 0x4a, 0x6f, 0x62, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, - 0x63, 0x68, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x23, 0x0a, 0x11, 0x4a, 0x6f, 0x62, 0x44, 0x69, - 0x73, 0x70, 0x6c, 0x61, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x22, 0x3c, 0x0a, 0x0f, - 0x4a, 0x6f, 0x62, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, - 0x29, 0x0a, 0x07, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x0f, 0x2e, 0x6a, 0x6f, 0x62, 0x2e, 0x4a, 0x6f, 0x62, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, - 0x79, 0x52, 0x07, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x22, 0x51, 0x0a, 0x10, 0x4a, 0x6f, - 0x62, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, - 0x0a, 0x06, 0x6a, 0x6f, 0x62, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, - 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x03, 0x48, 0x00, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x88, - 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3c, 0x0a, - 0x0e, 0x4a, 0x6f, 0x62, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, - 0x12, 0x0a, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x6c, - 0x6f, 0x67, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x27, 0x0a, 0x11, 0x53, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x6e, 0x52, 0x09, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x26, 0x0a, 0x08, + 0x63, 0x68, 0x69, 0x6c, 0x64, 0x72, 0x65, 0x6e, 0x18, 0x11, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, + 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x08, 0x63, 0x68, 0x69, 0x6c, + 0x64, 0x72, 0x65, 0x6e, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x22, 0x47, 0x0a, + 0x0f, 0x46, 0x69, 0x6c, 0x65, 0x45, 0x64, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, + 0x12, 0x24, 0x0a, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, + 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x2e, 0x45, 0x64, 0x69, 0x74, 0x65, 0x64, 0x46, 0x69, 0x6c, 0x65, + 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x22, 0x2f, 0x0a, 0x0d, 0x46, 0x69, 0x6c, 0x65, 0x45, 0x64, + 0x69, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1e, 0x0a, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x2e, 0x46, 0x69, 0x6c, + 0x65, 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x22, 0x43, 0x0a, 0x10, 0x46, 0x69, 0x6c, 0x65, 0x4d, + 0x6b, 0x64, 0x69, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, + 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, + 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x30, 0x0a, 0x0e, + 0x46, 0x69, 0x6c, 0x65, 0x4d, 0x6b, 0x64, 0x69, 0x72, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1e, + 0x0a, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x66, + 0x69, 0x6c, 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x22, 0x25, + 0x0a, 0x11, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x03, + 0x52, 0x03, 0x69, 0x64, 0x73, 0x22, 0x11, 0x0a, 0x0f, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x28, 0x0a, 0x16, 0x46, 0x69, 0x6c, 0x65, + 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, + 0x69, 0x64, 0x22, 0x3c, 0x0a, 0x14, 0x46, 0x69, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, + 0x72, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x24, 0x0a, 0x07, 0x70, 0x61, + 0x72, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x66, 0x69, + 0x6c, 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x07, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, + 0x22, 0x72, 0x0a, 0x0f, 0x54, 0x61, 0x70, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x04, 0x6d, 0x67, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x18, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x54, 0x61, 0x70, 0x65, + 0x4d, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x04, 0x6d, + 0x67, 0x65, 0x74, 0x12, 0x26, 0x0a, 0x04, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x61, 0x70, 0x65, 0x2e, 0x54, 0x61, 0x70, 0x65, 0x46, 0x69, 0x6c, + 0x74, 0x65, 0x72, 0x48, 0x00, 0x52, 0x04, 0x6c, 0x69, 0x73, 0x74, 0x42, 0x07, 0x0a, 0x05, 0x70, + 0x61, 0x72, 0x61, 0x6d, 0x22, 0x23, 0x0a, 0x0f, 0x54, 0x61, 0x70, 0x65, 0x4d, 0x47, 0x65, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x64, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x03, 0x52, 0x03, 0x69, 0x64, 0x73, 0x22, 0x31, 0x0a, 0x0d, 0x54, 0x61, 0x70, + 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x20, 0x0a, 0x05, 0x74, 0x61, + 0x70, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x74, 0x61, 0x70, 0x65, + 0x2e, 0x54, 0x61, 0x70, 0x65, 0x52, 0x05, 0x74, 0x61, 0x70, 0x65, 0x73, 0x22, 0x25, 0x0a, 0x11, + 0x54, 0x61, 0x70, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x03, 0x52, 0x03, + 0x69, 0x64, 0x73, 0x22, 0x11, 0x0a, 0x0f, 0x54, 0x61, 0x70, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x47, 0x0a, 0x17, 0x54, 0x61, 0x70, 0x65, 0x47, 0x65, + 0x74, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, + 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x22, + 0x49, 0x0a, 0x15, 0x54, 0x61, 0x70, 0x65, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x30, 0x0a, 0x09, 0x70, 0x6f, 0x73, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x6f, + 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x09, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xb7, 0x01, 0x0a, 0x0e, 0x4a, + 0x6f, 0x62, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, + 0x04, 0x6d, 0x67, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x4d, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x04, 0x6d, 0x67, 0x65, 0x74, 0x12, 0x24, 0x0a, 0x04, + 0x6c, 0x69, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6a, 0x6f, 0x62, + 0x2e, 0x4a, 0x6f, 0x62, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x48, 0x00, 0x52, 0x04, 0x6c, 0x69, + 0x73, 0x74, 0x12, 0x47, 0x0a, 0x0f, 0x72, 0x65, 0x63, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x5f, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6a, 0x6f, + 0x62, 0x2e, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x63, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x48, 0x00, 0x52, 0x0e, 0x72, 0x65, 0x63, + 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x70, + 0x61, 0x72, 0x61, 0x6d, 0x22, 0x22, 0x0a, 0x0e, 0x4a, 0x6f, 0x62, 0x4d, 0x47, 0x65, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x03, 0x52, 0x03, 0x69, 0x64, 0x73, 0x22, 0x2c, 0x0a, 0x0c, 0x4a, 0x6f, 0x62, 0x4c, + 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1c, 0x0a, 0x04, 0x6a, 0x6f, 0x62, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x08, 0x2e, 0x6a, 0x6f, 0x62, 0x2e, 0x4a, 0x6f, 0x62, + 0x52, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x22, 0x37, 0x0a, 0x10, 0x4a, 0x6f, 0x62, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x03, 0x6a, 0x6f, + 0x62, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6a, 0x6f, 0x62, 0x2e, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x03, 0x6a, 0x6f, 0x62, 0x22, + 0x2c, 0x0a, 0x0e, 0x4a, 0x6f, 0x62, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, + 0x79, 0x12, 0x1a, 0x0a, 0x03, 0x6a, 0x6f, 0x62, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x08, + 0x2e, 0x6a, 0x6f, 0x62, 0x2e, 0x4a, 0x6f, 0x62, 0x52, 0x03, 0x6a, 0x6f, 0x62, 0x22, 0x91, 0x01, + 0x0a, 0x13, 0x4a, 0x6f, 0x62, 0x45, 0x64, 0x69, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2b, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x6a, 0x6f, 0x62, 0x2e, 0x4a, 0x6f, 0x62, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x48, 0x00, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x88, + 0x01, 0x01, 0x12, 0x28, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x0d, 0x2e, 0x6a, 0x6f, 0x62, 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x48, 0x01, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, + 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x22, 0x13, 0x0a, 0x11, 0x4a, 0x6f, 0x62, 0x45, 0x64, 0x69, 0x74, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x24, 0x0a, 0x10, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x64, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x03, 0x52, 0x03, 0x69, 0x64, 0x73, 0x22, 0x10, 0x0a, 0x0e, + 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x51, + 0x0a, 0x12, 0x4a, 0x6f, 0x62, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x02, 0x69, 0x64, 0x12, 0x2b, 0x0a, 0x05, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6a, 0x6f, 0x62, 0x2e, 0x4a, 0x6f, 0x62, 0x44, 0x69, 0x73, + 0x70, 0x61, 0x74, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x52, 0x05, 0x70, 0x61, 0x72, 0x61, + 0x6d, 0x22, 0x12, 0x0a, 0x10, 0x4a, 0x6f, 0x62, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, + 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x23, 0x0a, 0x11, 0x4a, 0x6f, 0x62, 0x44, 0x69, 0x73, 0x70, + 0x6c, 0x61, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x22, 0x3c, 0x0a, 0x0f, 0x4a, 0x6f, + 0x62, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x29, 0x0a, + 0x07, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, + 0x2e, 0x6a, 0x6f, 0x62, 0x2e, 0x4a, 0x6f, 0x62, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x52, + 0x07, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x22, 0x51, 0x0a, 0x10, 0x4a, 0x6f, 0x62, 0x47, + 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, + 0x6a, 0x6f, 0x62, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6a, 0x6f, + 0x62, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x03, 0x48, 0x00, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x88, 0x01, 0x01, + 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3c, 0x0a, 0x0e, 0x4a, + 0x6f, 0x62, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x12, 0x0a, + 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x6c, 0x6f, 0x67, + 0x73, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x27, 0x0a, 0x11, 0x53, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, + 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, + 0x74, 0x68, 0x22, 0x93, 0x01, 0x0a, 0x0f, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4c, 0x69, 0x73, + 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x26, 0x0a, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x53, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x28, + 0x0a, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6c, + 0x65, 0x52, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x2e, 0x0a, 0x08, 0x63, 0x68, 0x69, 0x6c, + 0x64, 0x72, 0x65, 0x6e, 0x18, 0x11, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x08, + 0x63, 0x68, 0x69, 0x6c, 0x64, 0x72, 0x65, 0x6e, 0x22, 0x2a, 0x0a, 0x14, 0x53, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x47, 0x65, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x70, 0x61, 0x74, 0x68, 0x22, 0x93, 0x01, 0x0a, 0x0f, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4c, - 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x26, 0x0a, 0x04, 0x66, 0x69, 0x6c, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, - 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, - 0x12, 0x28, 0x0a, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x12, 0x2e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, - 0x69, 0x6c, 0x65, 0x52, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x2e, 0x0a, 0x08, 0x63, 0x68, - 0x69, 0x6c, 0x64, 0x72, 0x65, 0x6e, 0x18, 0x11, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6c, 0x65, - 0x52, 0x08, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x72, 0x65, 0x6e, 0x22, 0x13, 0x0a, 0x11, 0x44, 0x65, - 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, - 0x2b, 0x0a, 0x0f, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, - 0x6c, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x09, 0x52, 0x07, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x22, 0x54, 0x0a, 0x14, - 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x3c, 0x0a, 0x05, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x5f, 0x65, 0x6e, - 0x74, 0x69, 0x74, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, - 0x79, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x54, 0x79, 0x70, 0x65, 0x52, 0x05, 0x74, 0x79, 0x70, - 0x65, 0x73, 0x22, 0x28, 0x0a, 0x12, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x45, 0x78, 0x70, - 0x6f, 0x72, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6a, 0x73, 0x6f, 0x6e, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x6a, 0x73, 0x6f, 0x6e, 0x22, 0x56, 0x0a, 0x12, - 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x54, 0x72, 0x69, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x72, 0x69, 0x6d, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, - 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x74, 0x72, 0x69, 0x6d, 0x50, - 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x72, 0x69, 0x6d, 0x5f, - 0x66, 0x69, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x74, 0x72, 0x69, 0x6d, - 0x46, 0x69, 0x6c, 0x65, 0x22, 0x12, 0x0a, 0x10, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x54, - 0x72, 0x69, 0x6d, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x32, 0xc7, 0x0a, 0x0a, 0x07, 0x53, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x12, 0x3b, 0x0a, 0x07, 0x46, 0x69, 0x6c, 0x65, 0x47, 0x65, 0x74, 0x12, - 0x17, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x47, 0x65, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x47, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, - 0x00, 0x12, 0x3e, 0x0a, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x45, 0x64, 0x69, 0x74, 0x12, 0x18, 0x2e, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x45, 0x64, 0x69, 0x74, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x45, 0x64, 0x69, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, - 0x00, 0x12, 0x41, 0x0a, 0x09, 0x46, 0x69, 0x6c, 0x65, 0x4d, 0x6b, 0x64, 0x69, 0x72, 0x12, 0x19, - 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x4d, 0x6b, 0x64, - 0x69, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x4d, 0x6b, 0x64, 0x69, 0x72, 0x52, 0x65, 0x70, - 0x6c, 0x79, 0x22, 0x00, 0x12, 0x44, 0x0a, 0x0a, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x12, 0x1a, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x46, 0x69, 0x6c, - 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, - 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x53, 0x0a, 0x0f, 0x46, 0x69, - 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1f, 0x2e, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, - 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, - 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x4c, 0x69, 0x73, - 0x74, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, - 0x3e, 0x0a, 0x08, 0x54, 0x61, 0x70, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x18, 0x2e, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x54, 0x61, 0x70, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, - 0x54, 0x61, 0x70, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, - 0x44, 0x0a, 0x0a, 0x54, 0x61, 0x70, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x1a, 0x2e, + 0x70, 0x61, 0x74, 0x68, 0x22, 0x28, 0x0a, 0x12, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x47, 0x65, + 0x74, 0x53, 0x69, 0x7a, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, + 0x7a, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x22, 0x13, + 0x0a, 0x11, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x22, 0x2b, 0x0a, 0x0f, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, + 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, + 0x22, 0x54, 0x0a, 0x14, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x45, 0x78, 0x70, 0x6f, 0x72, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3c, 0x0a, 0x05, 0x74, 0x79, 0x70, 0x65, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, + 0x79, 0x5f, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x4c, 0x69, + 0x62, 0x72, 0x61, 0x72, 0x79, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x54, 0x79, 0x70, 0x65, 0x52, + 0x05, 0x74, 0x79, 0x70, 0x65, 0x73, 0x22, 0x28, 0x0a, 0x12, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, + 0x79, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x12, 0x0a, 0x04, + 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x6a, 0x73, 0x6f, 0x6e, + 0x22, 0x56, 0x0a, 0x12, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x54, 0x72, 0x69, 0x6d, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x72, 0x69, 0x6d, 0x5f, 0x70, + 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x74, + 0x72, 0x69, 0x6d, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x74, + 0x72, 0x69, 0x6d, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, + 0x74, 0x72, 0x69, 0x6d, 0x46, 0x69, 0x6c, 0x65, 0x22, 0x12, 0x0a, 0x10, 0x4c, 0x69, 0x62, 0x72, + 0x61, 0x72, 0x79, 0x54, 0x72, 0x69, 0x6d, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x32, 0x96, 0x0b, 0x0a, + 0x07, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x3b, 0x0a, 0x07, 0x46, 0x69, 0x6c, 0x65, + 0x47, 0x65, 0x74, 0x12, 0x17, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x46, 0x69, + 0x6c, 0x65, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x47, 0x65, 0x74, 0x52, 0x65, + 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x3e, 0x0a, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x45, 0x64, 0x69, + 0x74, 0x12, 0x18, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, + 0x45, 0x64, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x45, 0x64, 0x69, 0x74, 0x52, 0x65, + 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x09, 0x46, 0x69, 0x6c, 0x65, 0x4d, 0x6b, 0x64, + 0x69, 0x72, 0x12, 0x19, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x46, 0x69, 0x6c, + 0x65, 0x4d, 0x6b, 0x64, 0x69, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x4d, 0x6b, 0x64, 0x69, + 0x72, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x44, 0x0a, 0x0a, 0x46, 0x69, 0x6c, 0x65, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x1a, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x46, 0x69, 0x6c, + 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x53, + 0x0a, 0x0f, 0x46, 0x69, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, + 0x73, 0x12, 0x1f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, + 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x46, 0x69, 0x6c, + 0x65, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x70, 0x6c, + 0x79, 0x22, 0x00, 0x12, 0x3e, 0x0a, 0x08, 0x54, 0x61, 0x70, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, + 0x18, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x54, 0x61, 0x70, 0x65, 0x4c, 0x69, + 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x2e, 0x54, 0x61, 0x70, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6c, + 0x79, 0x22, 0x00, 0x12, 0x44, 0x0a, 0x0a, 0x54, 0x61, 0x70, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x12, 0x1a, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x54, 0x61, 0x70, 0x65, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x54, 0x61, 0x70, 0x65, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x2e, 0x54, 0x61, 0x70, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, - 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x56, 0x0a, 0x10, 0x54, 0x61, 0x70, 0x65, 0x47, 0x65, 0x74, - 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x20, 0x2e, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x2e, 0x54, 0x61, 0x70, 0x65, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x73, 0x69, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x54, 0x61, 0x70, 0x65, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x73, - 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x3b, 0x0a, - 0x07, 0x4a, 0x6f, 0x62, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x17, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x15, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x4c, - 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x09, 0x4a, 0x6f, - 0x62, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x19, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4a, 0x6f, 0x62, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x4a, 0x0a, - 0x0c, 0x4a, 0x6f, 0x62, 0x45, 0x64, 0x69, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1c, 0x2e, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x45, 0x64, 0x69, 0x74, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x45, 0x64, 0x69, 0x74, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x09, 0x4a, 0x6f, 0x62, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x19, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x2e, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x17, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x47, 0x0a, 0x0b, - 0x4a, 0x6f, 0x62, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x12, 0x1b, 0x2e, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, - 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, - 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x44, 0x0a, 0x0a, 0x4a, 0x6f, 0x62, 0x44, 0x69, 0x73, 0x70, - 0x6c, 0x61, 0x79, 0x12, 0x1a, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4a, 0x6f, - 0x62, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x18, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x44, 0x69, 0x73, - 0x70, 0x6c, 0x61, 0x79, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x09, 0x4a, - 0x6f, 0x62, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x12, 0x19, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4a, 0x6f, - 0x62, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x44, - 0x0a, 0x0a, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x1a, 0x2e, 0x73, + 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x56, 0x0a, 0x10, 0x54, 0x61, 0x70, + 0x65, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x20, 0x2e, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x54, 0x61, 0x70, 0x65, 0x47, 0x65, 0x74, 0x50, + 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1e, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x54, 0x61, 0x70, 0x65, 0x47, 0x65, + 0x74, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, + 0x00, 0x12, 0x3b, 0x0a, 0x07, 0x4a, 0x6f, 0x62, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x17, 0x2e, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, + 0x4a, 0x6f, 0x62, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x41, + 0x0a, 0x09, 0x4a, 0x6f, 0x62, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x19, 0x2e, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x2e, 0x4a, 0x6f, 0x62, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, + 0x00, 0x12, 0x4a, 0x0a, 0x0c, 0x4a, 0x6f, 0x62, 0x45, 0x64, 0x69, 0x74, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x12, 0x1c, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x45, + 0x64, 0x69, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1a, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x45, 0x64, 0x69, + 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x41, 0x0a, + 0x09, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x19, 0x2e, 0x73, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, + 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, + 0x12, 0x47, 0x0a, 0x0b, 0x4a, 0x6f, 0x62, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x12, + 0x1b, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x44, 0x69, 0x73, + 0x70, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, + 0x63, 0x68, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x44, 0x0a, 0x0a, 0x4a, 0x6f, 0x62, + 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x12, 0x1a, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4a, 0x6f, + 0x62, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, + 0x41, 0x0a, 0x09, 0x4a, 0x6f, 0x62, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x12, 0x19, 0x2e, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x2e, 0x4a, 0x6f, 0x62, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x70, 0x6c, 0x79, + 0x22, 0x00, 0x12, 0x44, 0x0a, 0x0a, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, + 0x12, 0x1a, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4c, 0x69, 0x73, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, - 0x6c, 0x79, 0x22, 0x00, 0x12, 0x44, 0x0a, 0x0a, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, - 0x73, 0x74, 0x12, 0x1a, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x44, 0x65, 0x76, - 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, - 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4c, - 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x4d, 0x0a, 0x0d, 0x4c, 0x69, - 0x62, 0x72, 0x61, 0x72, 0x79, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x1d, 0x2e, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x45, 0x78, 0x70, - 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x45, 0x78, 0x70, 0x6f, - 0x72, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x47, 0x0a, 0x0b, 0x4c, 0x69, 0x62, - 0x72, 0x61, 0x72, 0x79, 0x54, 0x72, 0x69, 0x6d, 0x12, 0x1b, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x4d, 0x0a, 0x0d, 0x53, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x47, 0x65, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1d, 0x2e, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x47, 0x65, 0x74, 0x53, 0x69, 0x7a, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x47, 0x65, 0x74, 0x53, 0x69, 0x7a, 0x65, + 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x44, 0x0a, 0x0a, 0x44, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x1a, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, + 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x18, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x44, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x4d, 0x0a, + 0x0d, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x1d, + 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, + 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x45, + 0x78, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x47, 0x0a, 0x0b, + 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x54, 0x72, 0x69, 0x6d, 0x12, 0x1b, 0x2e, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x54, 0x72, 0x69, + 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x54, 0x72, 0x69, 0x6d, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, - 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x54, 0x72, 0x69, 0x6d, 0x52, 0x65, 0x70, 0x6c, 0x79, - 0x22, 0x00, 0x42, 0x23, 0x5a, 0x21, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x73, 0x61, 0x6d, 0x75, 0x65, 0x6c, 0x6e, 0x63, 0x75, 0x69, 0x2f, 0x79, 0x61, 0x74, 0x6d, - 0x2f, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x70, 0x6c, 0x79, 0x22, 0x00, 0x42, 0x23, 0x5a, 0x21, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x61, 0x6d, 0x75, 0x65, 0x6c, 0x6e, 0x63, 0x75, 0x69, 0x2f, 0x79, + 0x61, 0x74, 0x6d, 0x2f, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( @@ -2285,7 +2401,7 @@ func file_service_proto_rawDescGZIP() []byte { return file_service_proto_rawDescData } -var file_service_proto_msgTypes = make([]protoimpl.MessageInfo, 40) +var file_service_proto_msgTypes = make([]protoimpl.MessageInfo, 42) var file_service_proto_goTypes = []interface{}{ (*FileGetRequest)(nil), // 0: service.FileGetRequest (*FileGetReply)(nil), // 1: service.FileGetReply @@ -2321,54 +2437,56 @@ var file_service_proto_goTypes = []interface{}{ (*JobGetLogReply)(nil), // 31: service.JobGetLogReply (*SourceListRequest)(nil), // 32: service.SourceListRequest (*SourceListReply)(nil), // 33: service.SourceListReply - (*DeviceListRequest)(nil), // 34: service.DeviceListRequest - (*DeviceListReply)(nil), // 35: service.DeviceListReply - (*LibraryExportRequest)(nil), // 36: service.LibraryExportRequest - (*LibraryExportReply)(nil), // 37: service.LibraryExportReply - (*LibraryTrimRequest)(nil), // 38: service.LibraryTrimRequest - (*LibraryTrimReply)(nil), // 39: service.LibraryTrimReply - (*File)(nil), // 40: file.File - (*Position)(nil), // 41: position.Position - (*EditedFile)(nil), // 42: file.EditedFile - (*TapeFilter)(nil), // 43: tape.TapeFilter - (*Tape)(nil), // 44: tape.Tape - (*JobFilter)(nil), // 45: job.JobFilter - (*JobRecentlyUpdateFilter)(nil), // 46: job.JobRecentlyUpdateFilter - (*Job)(nil), // 47: job.Job - (*CreatableJob)(nil), // 48: job.CreatableJob - (JobStatus)(0), // 49: job.JobStatus - (*JobState)(nil), // 50: job.JobState - (*JobDispatchParam)(nil), // 51: job.JobDispatchParam - (*JobDisplay)(nil), // 52: job.JobDisplay - (*SourceFile)(nil), // 53: source.SourceFile - (LibraryEntityType)(0), // 54: library_entity_type.LibraryEntityType + (*SourceGetSizeRequest)(nil), // 34: service.SourceGetSizeRequest + (*SourceGetSizeReply)(nil), // 35: service.SourceGetSizeReply + (*DeviceListRequest)(nil), // 36: service.DeviceListRequest + (*DeviceListReply)(nil), // 37: service.DeviceListReply + (*LibraryExportRequest)(nil), // 38: service.LibraryExportRequest + (*LibraryExportReply)(nil), // 39: service.LibraryExportReply + (*LibraryTrimRequest)(nil), // 40: service.LibraryTrimRequest + (*LibraryTrimReply)(nil), // 41: service.LibraryTrimReply + (*File)(nil), // 42: file.File + (*Position)(nil), // 43: position.Position + (*EditedFile)(nil), // 44: file.EditedFile + (*TapeFilter)(nil), // 45: tape.TapeFilter + (*Tape)(nil), // 46: tape.Tape + (*JobFilter)(nil), // 47: job.JobFilter + (*JobRecentlyUpdateFilter)(nil), // 48: job.JobRecentlyUpdateFilter + (*Job)(nil), // 49: job.Job + (*CreatableJob)(nil), // 50: job.CreatableJob + (JobStatus)(0), // 51: job.JobStatus + (*JobState)(nil), // 52: job.JobState + (*JobDispatchParam)(nil), // 53: job.JobDispatchParam + (*JobDisplay)(nil), // 54: job.JobDisplay + (*SourceFile)(nil), // 55: source.SourceFile + (LibraryEntityType)(0), // 56: library_entity_type.LibraryEntityType } var file_service_proto_depIdxs = []int32{ - 40, // 0: service.FileGetReply.file:type_name -> file.File - 41, // 1: service.FileGetReply.positions:type_name -> position.Position - 40, // 2: service.FileGetReply.children:type_name -> file.File - 42, // 3: service.FileEditRequest.file:type_name -> file.EditedFile - 40, // 4: service.FileEditReply.file:type_name -> file.File - 40, // 5: service.FileMkdirReply.file:type_name -> file.File - 40, // 6: service.FileListParentsReply.parents:type_name -> file.File + 42, // 0: service.FileGetReply.file:type_name -> file.File + 43, // 1: service.FileGetReply.positions:type_name -> position.Position + 42, // 2: service.FileGetReply.children:type_name -> file.File + 44, // 3: service.FileEditRequest.file:type_name -> file.EditedFile + 42, // 4: service.FileEditReply.file:type_name -> file.File + 42, // 5: service.FileMkdirReply.file:type_name -> file.File + 42, // 6: service.FileListParentsReply.parents:type_name -> file.File 11, // 7: service.TapeListRequest.mget:type_name -> service.TapeMGetRequest - 43, // 8: service.TapeListRequest.list:type_name -> tape.TapeFilter - 44, // 9: service.TapeListReply.tapes:type_name -> tape.Tape - 41, // 10: service.TapeGetPositionsReply.positions:type_name -> position.Position + 45, // 8: service.TapeListRequest.list:type_name -> tape.TapeFilter + 46, // 9: service.TapeListReply.tapes:type_name -> tape.Tape + 43, // 10: service.TapeGetPositionsReply.positions:type_name -> position.Position 18, // 11: service.JobListRequest.mget:type_name -> service.JobMGetRequest - 45, // 12: service.JobListRequest.list:type_name -> job.JobFilter - 46, // 13: service.JobListRequest.recently_update:type_name -> job.JobRecentlyUpdateFilter - 47, // 14: service.JobListReply.jobs:type_name -> job.Job - 48, // 15: service.JobCreateRequest.job:type_name -> job.CreatableJob - 47, // 16: service.JobCreateReply.job:type_name -> job.Job - 49, // 17: service.JobEditStateRequest.status:type_name -> job.JobStatus - 50, // 18: service.JobEditStateRequest.state:type_name -> job.JobState - 51, // 19: service.JobDispatchRequest.param:type_name -> job.JobDispatchParam - 52, // 20: service.JobDisplayReply.display:type_name -> job.JobDisplay - 53, // 21: service.SourceListReply.file:type_name -> source.SourceFile - 53, // 22: service.SourceListReply.chain:type_name -> source.SourceFile - 53, // 23: service.SourceListReply.children:type_name -> source.SourceFile - 54, // 24: service.LibraryExportRequest.types:type_name -> library_entity_type.LibraryEntityType + 47, // 12: service.JobListRequest.list:type_name -> job.JobFilter + 48, // 13: service.JobListRequest.recently_update:type_name -> job.JobRecentlyUpdateFilter + 49, // 14: service.JobListReply.jobs:type_name -> job.Job + 50, // 15: service.JobCreateRequest.job:type_name -> job.CreatableJob + 49, // 16: service.JobCreateReply.job:type_name -> job.Job + 51, // 17: service.JobEditStateRequest.status:type_name -> job.JobStatus + 52, // 18: service.JobEditStateRequest.state:type_name -> job.JobState + 53, // 19: service.JobDispatchRequest.param:type_name -> job.JobDispatchParam + 54, // 20: service.JobDisplayReply.display:type_name -> job.JobDisplay + 55, // 21: service.SourceListReply.file:type_name -> source.SourceFile + 55, // 22: service.SourceListReply.chain:type_name -> source.SourceFile + 55, // 23: service.SourceListReply.children:type_name -> source.SourceFile + 56, // 24: service.LibraryExportRequest.types:type_name -> library_entity_type.LibraryEntityType 0, // 25: service.Service.FileGet:input_type -> service.FileGetRequest 2, // 26: service.Service.FileEdit:input_type -> service.FileEditRequest 4, // 27: service.Service.FileMkdir:input_type -> service.FileMkdirRequest @@ -2385,30 +2503,32 @@ var file_service_proto_depIdxs = []int32{ 28, // 38: service.Service.JobDisplay:input_type -> service.JobDisplayRequest 30, // 39: service.Service.JobGetLog:input_type -> service.JobGetLogRequest 32, // 40: service.Service.SourceList:input_type -> service.SourceListRequest - 34, // 41: service.Service.DeviceList:input_type -> service.DeviceListRequest - 36, // 42: service.Service.LibraryExport:input_type -> service.LibraryExportRequest - 38, // 43: service.Service.LibraryTrim:input_type -> service.LibraryTrimRequest - 1, // 44: service.Service.FileGet:output_type -> service.FileGetReply - 3, // 45: service.Service.FileEdit:output_type -> service.FileEditReply - 5, // 46: service.Service.FileMkdir:output_type -> service.FileMkdirReply - 7, // 47: service.Service.FileDelete:output_type -> service.FileDeleteReply - 9, // 48: service.Service.FileListParents:output_type -> service.FileListParentsReply - 12, // 49: service.Service.TapeList:output_type -> service.TapeListReply - 14, // 50: service.Service.TapeDelete:output_type -> service.TapeDeleteReply - 16, // 51: service.Service.TapeGetPositions:output_type -> service.TapeGetPositionsReply - 19, // 52: service.Service.JobList:output_type -> service.JobListReply - 21, // 53: service.Service.JobCreate:output_type -> service.JobCreateReply - 23, // 54: service.Service.JobEditState:output_type -> service.JobEditStateReply - 25, // 55: service.Service.JobDelete:output_type -> service.JobDeleteReply - 27, // 56: service.Service.JobDispatch:output_type -> service.JobDispatchReply - 29, // 57: service.Service.JobDisplay:output_type -> service.JobDisplayReply - 31, // 58: service.Service.JobGetLog:output_type -> service.JobGetLogReply - 33, // 59: service.Service.SourceList:output_type -> service.SourceListReply - 35, // 60: service.Service.DeviceList:output_type -> service.DeviceListReply - 37, // 61: service.Service.LibraryExport:output_type -> service.LibraryExportReply - 39, // 62: service.Service.LibraryTrim:output_type -> service.LibraryTrimReply - 44, // [44:63] is the sub-list for method output_type - 25, // [25:44] is the sub-list for method input_type + 34, // 41: service.Service.SourceGetSize:input_type -> service.SourceGetSizeRequest + 36, // 42: service.Service.DeviceList:input_type -> service.DeviceListRequest + 38, // 43: service.Service.LibraryExport:input_type -> service.LibraryExportRequest + 40, // 44: service.Service.LibraryTrim:input_type -> service.LibraryTrimRequest + 1, // 45: service.Service.FileGet:output_type -> service.FileGetReply + 3, // 46: service.Service.FileEdit:output_type -> service.FileEditReply + 5, // 47: service.Service.FileMkdir:output_type -> service.FileMkdirReply + 7, // 48: service.Service.FileDelete:output_type -> service.FileDeleteReply + 9, // 49: service.Service.FileListParents:output_type -> service.FileListParentsReply + 12, // 50: service.Service.TapeList:output_type -> service.TapeListReply + 14, // 51: service.Service.TapeDelete:output_type -> service.TapeDeleteReply + 16, // 52: service.Service.TapeGetPositions:output_type -> service.TapeGetPositionsReply + 19, // 53: service.Service.JobList:output_type -> service.JobListReply + 21, // 54: service.Service.JobCreate:output_type -> service.JobCreateReply + 23, // 55: service.Service.JobEditState:output_type -> service.JobEditStateReply + 25, // 56: service.Service.JobDelete:output_type -> service.JobDeleteReply + 27, // 57: service.Service.JobDispatch:output_type -> service.JobDispatchReply + 29, // 58: service.Service.JobDisplay:output_type -> service.JobDisplayReply + 31, // 59: service.Service.JobGetLog:output_type -> service.JobGetLogReply + 33, // 60: service.Service.SourceList:output_type -> service.SourceListReply + 35, // 61: service.Service.SourceGetSize:output_type -> service.SourceGetSizeReply + 37, // 62: service.Service.DeviceList:output_type -> service.DeviceListReply + 39, // 63: service.Service.LibraryExport:output_type -> service.LibraryExportReply + 41, // 64: service.Service.LibraryTrim:output_type -> service.LibraryTrimReply + 45, // [45:65] is the sub-list for method output_type + 25, // [25:45] is the sub-list for method input_type 25, // [25:25] is the sub-list for extension type_name 25, // [25:25] is the sub-list for extension extendee 0, // [0:25] is the sub-list for field type_name @@ -2835,7 +2955,7 @@ func file_service_proto_init() { } } file_service_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeviceListRequest); i { + switch v := v.(*SourceGetSizeRequest); i { case 0: return &v.state case 1: @@ -2847,7 +2967,7 @@ func file_service_proto_init() { } } file_service_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeviceListReply); i { + switch v := v.(*SourceGetSizeReply); i { case 0: return &v.state case 1: @@ -2859,7 +2979,7 @@ func file_service_proto_init() { } } file_service_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LibraryExportRequest); i { + switch v := v.(*DeviceListRequest); i { case 0: return &v.state case 1: @@ -2871,7 +2991,7 @@ func file_service_proto_init() { } } file_service_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LibraryExportReply); i { + switch v := v.(*DeviceListReply); i { case 0: return &v.state case 1: @@ -2883,7 +3003,7 @@ func file_service_proto_init() { } } file_service_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LibraryTrimRequest); i { + switch v := v.(*LibraryExportRequest); i { case 0: return &v.state case 1: @@ -2895,6 +3015,30 @@ func file_service_proto_init() { } } file_service_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LibraryExportReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_service_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LibraryTrimRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_service_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*LibraryTrimReply); i { case 0: return &v.state @@ -2907,6 +3051,7 @@ func file_service_proto_init() { } } } + file_service_proto_msgTypes[0].OneofWrappers = []interface{}{} file_service_proto_msgTypes[1].OneofWrappers = []interface{}{} file_service_proto_msgTypes[10].OneofWrappers = []interface{}{ (*TapeListRequest_Mget)(nil), @@ -2925,7 +3070,7 @@ func file_service_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_service_proto_rawDesc, NumEnums: 0, - NumMessages: 40, + NumMessages: 42, NumExtensions: 0, NumServices: 1, }, diff --git a/entity/service.proto b/entity/service.proto index 932d0cb..b776119 100644 --- a/entity/service.proto +++ b/entity/service.proto @@ -29,6 +29,7 @@ service Service { rpc JobGetLog(JobGetLogRequest) returns (JobGetLogReply) {} rpc SourceList(SourceListRequest) returns (SourceListReply) {} + rpc SourceGetSize(SourceGetSizeRequest) returns (SourceGetSizeReply) {} rpc DeviceList(DeviceListRequest) returns (DeviceListReply) {} @@ -38,6 +39,8 @@ service Service { message FileGetRequest { int64 id = 1; + + optional bool needSize = 17; } message FileGetReply { @@ -186,6 +189,14 @@ message SourceListReply { repeated source.SourceFile children = 17; } +message SourceGetSizeRequest { + string path = 1; +} + +message SourceGetSizeReply { + int64 size = 1; +} + message DeviceListRequest {} message DeviceListReply { diff --git a/entity/service_grpc.pb.go b/entity/service_grpc.pb.go index 09dba25..16602cb 100644 --- a/entity/service_grpc.pb.go +++ b/entity/service_grpc.pb.go @@ -38,6 +38,7 @@ type ServiceClient interface { JobDisplay(ctx context.Context, in *JobDisplayRequest, opts ...grpc.CallOption) (*JobDisplayReply, error) JobGetLog(ctx context.Context, in *JobGetLogRequest, opts ...grpc.CallOption) (*JobGetLogReply, error) SourceList(ctx context.Context, in *SourceListRequest, opts ...grpc.CallOption) (*SourceListReply, error) + SourceGetSize(ctx context.Context, in *SourceGetSizeRequest, opts ...grpc.CallOption) (*SourceGetSizeReply, error) DeviceList(ctx context.Context, in *DeviceListRequest, opts ...grpc.CallOption) (*DeviceListReply, error) LibraryExport(ctx context.Context, in *LibraryExportRequest, opts ...grpc.CallOption) (*LibraryExportReply, error) LibraryTrim(ctx context.Context, in *LibraryTrimRequest, opts ...grpc.CallOption) (*LibraryTrimReply, error) @@ -195,6 +196,15 @@ func (c *serviceClient) SourceList(ctx context.Context, in *SourceListRequest, o return out, nil } +func (c *serviceClient) SourceGetSize(ctx context.Context, in *SourceGetSizeRequest, opts ...grpc.CallOption) (*SourceGetSizeReply, error) { + out := new(SourceGetSizeReply) + err := c.cc.Invoke(ctx, "/service.Service/SourceGetSize", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *serviceClient) DeviceList(ctx context.Context, in *DeviceListRequest, opts ...grpc.CallOption) (*DeviceListReply, error) { out := new(DeviceListReply) err := c.cc.Invoke(ctx, "/service.Service/DeviceList", in, out, opts...) @@ -242,6 +252,7 @@ type ServiceServer interface { JobDisplay(context.Context, *JobDisplayRequest) (*JobDisplayReply, error) JobGetLog(context.Context, *JobGetLogRequest) (*JobGetLogReply, error) SourceList(context.Context, *SourceListRequest) (*SourceListReply, error) + SourceGetSize(context.Context, *SourceGetSizeRequest) (*SourceGetSizeReply, error) DeviceList(context.Context, *DeviceListRequest) (*DeviceListReply, error) LibraryExport(context.Context, *LibraryExportRequest) (*LibraryExportReply, error) LibraryTrim(context.Context, *LibraryTrimRequest) (*LibraryTrimReply, error) @@ -300,6 +311,9 @@ func (UnimplementedServiceServer) JobGetLog(context.Context, *JobGetLogRequest) func (UnimplementedServiceServer) SourceList(context.Context, *SourceListRequest) (*SourceListReply, error) { return nil, status.Errorf(codes.Unimplemented, "method SourceList not implemented") } +func (UnimplementedServiceServer) SourceGetSize(context.Context, *SourceGetSizeRequest) (*SourceGetSizeReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method SourceGetSize not implemented") +} func (UnimplementedServiceServer) DeviceList(context.Context, *DeviceListRequest) (*DeviceListReply, error) { return nil, status.Errorf(codes.Unimplemented, "method DeviceList not implemented") } @@ -610,6 +624,24 @@ func _Service_SourceList_Handler(srv interface{}, ctx context.Context, dec func( return interceptor(ctx, in, info, handler) } +func _Service_SourceGetSize_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SourceGetSizeRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ServiceServer).SourceGetSize(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/service.Service/SourceGetSize", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ServiceServer).SourceGetSize(ctx, req.(*SourceGetSizeRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _Service_DeviceList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(DeviceListRequest) if err := dec(in); err != nil { @@ -735,6 +767,10 @@ var Service_ServiceDesc = grpc.ServiceDesc{ MethodName: "SourceList", Handler: _Service_SourceList_Handler, }, + { + MethodName: "SourceGetSize", + Handler: _Service_SourceGetSize_Handler, + }, { MethodName: "DeviceList", Handler: _Service_DeviceList_Handler, diff --git a/frontend/package.json b/frontend/package.json index ade5123..f26e4b8 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -38,6 +38,7 @@ "react-intl": "^6.4.7", "react-is": "^18.2.0", "react-router-dom": "^6.4.5", + "react-toastify": "^9.1.3", "react-virtuoso": "^4.6.1", "sort-by": "^1.2.0" }, diff --git a/frontend/src/actions.ts b/frontend/src/actions.tsx similarity index 66% rename from frontend/src/actions.ts rename to frontend/src/actions.tsx index 2692ec4..d5cb7be 100644 --- a/frontend/src/actions.ts +++ b/frontend/src/actions.tsx @@ -1,6 +1,5 @@ import { FileData, FileArray, FileAction } from "@samuelncui/chonky"; -import { defineFileAction } from "@samuelncui/chonky"; -import { ChonkyActions } from "@samuelncui/chonky"; +import { ChonkyActions, defineFileAction } from "@samuelncui/chonky"; type RenameFileState = { contextMenuTriggerFile: FileData; @@ -9,6 +8,14 @@ type RenameFileState = { selectedFilesForAction: FileArray; }; +export const CreateFolder = defineFileAction({ + ...ChonkyActions.CreateFolder, + button: { + ...ChonkyActions.CreateFolder.button, + // iconOnly: true, + }, +} as FileAction); + export const RenameFileAction = defineFileAction({ id: "rename_file", requiresSelection: true, @@ -17,7 +24,18 @@ export const RenameFileAction = defineFileAction({ toolbar: true, contextMenu: true, group: "Actions", - icon: "edit", + icon: "mui-rename", + }, + __extraStateType: {} as RenameFileState, +} as FileAction); + +export const GetDataUsageAction = defineFileAction({ + id: "get_data_usage", + button: { + name: "Data Usage", + toolbar: true, + icon: "mui-data-usage", + // iconOnly: true, }, __extraStateType: {} as RenameFileState, } as FileAction); @@ -36,6 +54,7 @@ export const CreateBackupJobAction = defineFileAction({ button: { name: "Create Backup Job", toolbar: true, + icon: "mui-fiber-new", }, } as FileAction); @@ -44,6 +63,7 @@ export const CreateRestoreJobAction = defineFileAction({ button: { name: "Create Restore Job", toolbar: true, + icon: "mui-fiber-new", }, } as FileAction); @@ -52,5 +72,6 @@ export const TrimLibraryAction = defineFileAction({ button: { name: "Trim Library", toolbar: true, + icon: "mui-cleaning", }, } as FileAction); diff --git a/frontend/src/api.ts b/frontend/src/api.ts index 929d98c..a0edbd0 100644 --- a/frontend/src/api.ts +++ b/frontend/src/api.ts @@ -19,6 +19,16 @@ export const fileBase: string = (() => { return apiBase.replace("/services", "/files"); })(); +export const cli = (() => { + return new ServiceClient( + new GrpcWebFetchTransport({ + baseUrl: apiBase, + format: "binary", + }), + ); +})(); +(window as any).cli = cli; + export const Root: FileData = { id: "0", name: "Root", @@ -29,20 +39,12 @@ export const Root: FileData = { droppable: true, }; -const transport = new GrpcWebFetchTransport({ - baseUrl: apiBase, - format: "binary", -}); - -export const cli = new ServiceClient(transport); -(window as any).cli = cli; - -export function convertFiles(files: Array): FileData[] { +export function convertFiles(files: Array, dirWithSize: boolean = false): FileData[] { return files.map((file) => { const isDir = (file.mode & MODE_DIR) > 0; return { - id: getID(file), + id: `${file.id}`, name: file.name, ext: extname(file.name), isDir, @@ -51,7 +53,7 @@ export function convertFiles(files: Array): FileData[] { selectable: true, draggable: true, droppable: isDir, - size: Number(file.size), + size: !isDir || dirWithSize ? Number(file.size) : undefined, modDate: moment.unix(Number(file.modTime)).toDate(), }; }); @@ -62,7 +64,7 @@ export function convertSourceFiles(files: Array): FileData[] { const isDir = (file.mode & MODE_DIR) > 0; return { - id: getID(file), + id: file.path, name: file.name, ext: extname(file.name), isDir, @@ -71,7 +73,7 @@ export function convertSourceFiles(files: Array): FileData[] { selectable: true, draggable: true, droppable: false, - size: Number(file.size), + size: isDir ? undefined : Number(file.size), modDate: moment.unix(Number(file.modTime)).toDate(), }; }); @@ -134,93 +136,3 @@ function extname(filename: string): string { } return filename.slice(idx); } - -function getID(file: File | SourceFile): string { - if ("id" in file) { - return `${file.id}`; - } - return file.path; -} - -// export interface GetFileResponse { -// file: File; -// positions: Position[]; -// children: FileArray; -// } -// export const getFile = async (id: string) => { -// const result = await fetch(`${Domain}/api/v1/file/${id}`); -// const body: GetFileResponse = await result.json(); -// return body; -// }; - -// export interface ListFileParentsResponse { -// parents: FileArray; -// } -// export const listFileParents = async (id: string) => { -// const result = await fetch(`${Domain}/api/v1/file/${id}/_parent`); -// const body: ListFileParentsResponse = await result.json(); -// return [Root, ...body.parents]; -// }; - -// export interface SetFileResponse { -// file?: File; -// result?: string; -// } -// export const editFile = async (id: string, payload: Partial) => { -// const result = await fetch(`${Domain}/api/v1/file/${id}`, { -// method: "POST", -// headers: { -// "Content-Type": "application/json", -// }, -// body: JSON.stringify(payload), -// }); -// const body: SetFileResponse = await result.json(); -// return body; -// }; - -// export const createFolder = async ( -// parentID: string, -// payload: Partial -// ) => { -// const result = await fetch(`${Domain}/api/v1/file/${parentID}/`, { -// method: "PUT", -// headers: { -// "Content-Type": "application/json", -// }, -// body: JSON.stringify(payload), -// }); -// const body: SetFileResponse = await result.json(); -// return body.file; -// }; - -// export const deleteFolder = async (ids: string[]) => { -// const result = await fetch(`${Domain}/api/v1/file/`, { -// method: "DELETE", -// headers: { -// "Content-Type": "application/json", -// }, -// body: JSON.stringify({ fileids: ids }), -// }); -// const body: SetFileResponse = await result.json(); -// return body; -// }; - -// interface GetTapeResponse { -// tape: Tape; -// } -// export const getTape = async (id: number) => { -// const result = await fetch(`${Domain}/api/v1/tape/${id}`); -// const body: GetTapeResponse = await result.json(); -// return body; -// }; - -// interface GetSourceResponse { -// file: File; -// chain: File[]; -// children: FileArray; -// } -// export const getSource = async (path: string) => { -// const result = await fetch(`${Domain}/api/v1/source/${path}`); -// const body: GetSourceResponse = await result.json(); -// return body; -// }; diff --git a/frontend/src/app.tsx b/frontend/src/app.tsx index 9e57d02..db0b00f 100644 --- a/frontend/src/app.tsx +++ b/frontend/src/app.tsx @@ -1,9 +1,11 @@ -import { Fragment, useCallback, ChangeEvent } from "react"; -import { Routes, Route, Link, useNavigate, Navigate, useLocation } from "react-router-dom"; +import { useCallback, ChangeEvent } from "react"; +import { Routes, Route, useNavigate, Navigate, useLocation } from "react-router-dom"; import Tabs from "@mui/material/Tabs"; import Tab from "@mui/material/Tab"; -import { createTheme, ThemeProvider, styled } from "@mui/material/styles"; +import { createTheme, styled, ThemeProvider } from "@mui/material/styles"; +import { ToastContainer, toast } from "react-toastify"; +import "react-toastify/dist/ReactToastify.css"; import { FileBrowser, FileBrowserType } from "./pages/file"; import { BackupBrowser, BackupType } from "./pages/backup"; @@ -37,6 +39,11 @@ const Delay = ({ inner }: { inner: JSX.Element }) => { return ok ? inner : null; }; +const ErrorMessage = styled("p")({ + margin: 0, + textAlign: "left", +}); + const App = () => { const location = useLocation(); const navigate = useNavigate(); @@ -47,6 +54,35 @@ const App = () => { [navigate], ); + useEffect(() => { + const origin = window.onunhandledrejection; + window.onunhandledrejection = (error) => { + if (error.reason.name !== "RpcError") { + return; + } + + console.log("rpc request have error:", error); + toast.error( +
+ + RPC Request Error + + + Method: + {error.reason.methodName} + + + Message: + {error.reason.message} + +
, + ); + }; + return () => { + window.onunhandledrejection = origin; + }; + }, []); + return (
@@ -76,6 +112,7 @@ const App = () => { +
); }; diff --git a/frontend/src/components/job-archive.tsx b/frontend/src/components/job-archive.tsx index f78203d..625731c 100644 --- a/frontend/src/components/job-archive.tsx +++ b/frontend/src/components/job-archive.tsx @@ -3,13 +3,10 @@ import { assert } from "@protobuf-ts/runtime"; import format from "format-duration"; import { Virtuoso, VirtuosoHandle } from "react-virtuoso"; +import { toast } from "react-toastify"; -import { styled } from "@mui/material/styles"; import Grid from "@mui/material/Grid"; import Box from "@mui/material/Box"; -import ListItem from "@mui/material/ListItem"; -import ListItemButton from "@mui/material/ListItemButton"; -import ListItemText from "@mui/material/ListItemText"; import Typography from "@mui/material/Typography"; import Button from "@mui/material/Button"; import TextField from "@mui/material/TextField"; @@ -239,7 +236,7 @@ const RollbackFileList = ({ onClose, jobID, state }: { onClose: () => void; jobI await cli.jobEditState({ id: jobID, state: { state: { oneofKind: "archive", archive: { ...state, sources } } } }); await refresh(); - alert(`Rollback to file '${path}' success!`); + toast.success(`Rollback to file '${path}' success!`); }, [state, refresh], ); diff --git a/frontend/src/components/toolbarInfo.tsx b/frontend/src/components/toolbarInfo.tsx index 32b7fd3..fd3efcd 100644 --- a/frontend/src/components/toolbarInfo.tsx +++ b/frontend/src/components/toolbarInfo.tsx @@ -1,4 +1,4 @@ -import { memo } from "react"; +import { memo, useMemo } from "react"; import Typography from "@mui/material/Typography"; import { FileArray } from "@samuelncui/chonky"; @@ -8,11 +8,31 @@ export interface ToobarInfoProps { files?: FileArray; } -export const ToobarInfo: React.FC = memo((props) => { +export const ToobarInfo: React.FC = memo(({ files }) => { + const [size, notFinished] = useMemo(() => { + let size = 0; + let notFinished = false; + for (const file of files || []) { + if (!file) { + continue; + } + + if (file.size === undefined) { + notFinished = true; + continue; + } + + size += file.size; + } + + return [size, notFinished]; + }, [files]); + return (
- {formatFilesize((props.files || []).reduce((total, file) => total + (file?.size ? file.size : 0), 0))} + {notFinished && "? "} + {formatFilesize(size)}
); diff --git a/frontend/src/entity/service.client.ts b/frontend/src/entity/service.client.ts index c56cd85..5fdb480 100644 --- a/frontend/src/entity/service.client.ts +++ b/frontend/src/entity/service.client.ts @@ -10,6 +10,8 @@ import type { LibraryExportReply } from "./service"; import type { LibraryExportRequest } from "./service"; import type { DeviceListReply } from "./service"; import type { DeviceListRequest } from "./service"; +import type { SourceGetSizeReply } from "./service"; +import type { SourceGetSizeRequest } from "./service"; import type { SourceListReply } from "./service"; import type { SourceListRequest } from "./service"; import type { JobGetLogReply } from "./service"; @@ -113,6 +115,10 @@ export interface IServiceClient { * @generated from protobuf rpc: SourceList(service.SourceListRequest) returns (service.SourceListReply); */ sourceList(input: SourceListRequest, options?: RpcOptions): UnaryCall; + /** + * @generated from protobuf rpc: SourceGetSize(service.SourceGetSizeRequest) returns (service.SourceGetSizeReply); + */ + sourceGetSize(input: SourceGetSizeRequest, options?: RpcOptions): UnaryCall; /** * @generated from protobuf rpc: DeviceList(service.DeviceListRequest) returns (service.DeviceListReply); */ @@ -247,25 +253,32 @@ export class ServiceClient implements IServiceClient, ServiceInfo { const method = this.methods[15], opt = this._transport.mergeOptions(options); return stackIntercept("unary", this._transport, method, opt, input); } + /** + * @generated from protobuf rpc: SourceGetSize(service.SourceGetSizeRequest) returns (service.SourceGetSizeReply); + */ + sourceGetSize(input: SourceGetSizeRequest, options?: RpcOptions): UnaryCall { + const method = this.methods[16], opt = this._transport.mergeOptions(options); + return stackIntercept("unary", this._transport, method, opt, input); + } /** * @generated from protobuf rpc: DeviceList(service.DeviceListRequest) returns (service.DeviceListReply); */ deviceList(input: DeviceListRequest, options?: RpcOptions): UnaryCall { - const method = this.methods[16], opt = this._transport.mergeOptions(options); + const method = this.methods[17], opt = this._transport.mergeOptions(options); return stackIntercept("unary", this._transport, method, opt, input); } /** * @generated from protobuf rpc: LibraryExport(service.LibraryExportRequest) returns (service.LibraryExportReply); */ libraryExport(input: LibraryExportRequest, options?: RpcOptions): UnaryCall { - const method = this.methods[17], opt = this._transport.mergeOptions(options); + const method = this.methods[18], opt = this._transport.mergeOptions(options); return stackIntercept("unary", this._transport, method, opt, input); } /** * @generated from protobuf rpc: LibraryTrim(service.LibraryTrimRequest) returns (service.LibraryTrimReply); */ libraryTrim(input: LibraryTrimRequest, options?: RpcOptions): UnaryCall { - const method = this.methods[18], opt = this._transport.mergeOptions(options); + const method = this.methods[19], opt = this._transport.mergeOptions(options); return stackIntercept("unary", this._transport, method, opt, input); } } diff --git a/frontend/src/entity/service.ts b/frontend/src/entity/service.ts index 79dfa52..5bc388f 100644 --- a/frontend/src/entity/service.ts +++ b/frontend/src/entity/service.ts @@ -35,6 +35,10 @@ export interface FileGetRequest { * @generated from protobuf field: int64 id = 1; */ id: bigint; + /** + * @generated from protobuf field: optional bool needSize = 17; + */ + needSize?: boolean; } /** * @generated from protobuf message service.FileGetReply @@ -395,6 +399,24 @@ export interface SourceListReply { */ children: SourceFile[]; } +/** + * @generated from protobuf message service.SourceGetSizeRequest + */ +export interface SourceGetSizeRequest { + /** + * @generated from protobuf field: string path = 1; + */ + path: string; +} +/** + * @generated from protobuf message service.SourceGetSizeReply + */ +export interface SourceGetSizeReply { + /** + * @generated from protobuf field: int64 size = 1; + */ + size: bigint; +} /** * @generated from protobuf message service.DeviceListRequest */ @@ -449,7 +471,8 @@ export interface LibraryTrimReply { class FileGetRequest$Type extends MessageType { constructor() { super("service.FileGetRequest", [ - { no: 1, name: "id", kind: "scalar", T: 3 /*ScalarType.INT64*/, L: 0 /*LongType.BIGINT*/ } + { no: 1, name: "id", kind: "scalar", T: 3 /*ScalarType.INT64*/, L: 0 /*LongType.BIGINT*/ }, + { no: 17, name: "needSize", kind: "scalar", opt: true, T: 8 /*ScalarType.BOOL*/ } ]); } create(value?: PartialMessage): FileGetRequest { @@ -467,6 +490,9 @@ class FileGetRequest$Type extends MessageType { case /* int64 id */ 1: message.id = reader.int64().toBigInt(); break; + case /* optional bool needSize */ 17: + message.needSize = reader.bool(); + break; default: let u = options.readUnknownField; if (u === "throw") @@ -482,6 +508,9 @@ class FileGetRequest$Type extends MessageType { /* int64 id = 1; */ if (message.id !== 0n) writer.tag(1, WireType.Varint).int64(message.id); + /* optional bool needSize = 17; */ + if (message.needSize !== undefined) + writer.tag(17, WireType.Varint).bool(message.needSize); let u = options.writeUnknownFields; if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); @@ -2099,6 +2128,100 @@ class SourceListReply$Type extends MessageType { */ export const SourceListReply = new SourceListReply$Type(); // @generated message type with reflection information, may provide speed optimized methods +class SourceGetSizeRequest$Type extends MessageType { + constructor() { + super("service.SourceGetSizeRequest", [ + { no: 1, name: "path", kind: "scalar", T: 9 /*ScalarType.STRING*/ } + ]); + } + create(value?: PartialMessage): SourceGetSizeRequest { + const message = { path: "" }; + globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: SourceGetSizeRequest): SourceGetSizeRequest { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* string path */ 1: + message.path = reader.string(); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: SourceGetSizeRequest, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* string path = 1; */ + if (message.path !== "") + writer.tag(1, WireType.LengthDelimited).string(message.path); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message service.SourceGetSizeRequest + */ +export const SourceGetSizeRequest = new SourceGetSizeRequest$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class SourceGetSizeReply$Type extends MessageType { + constructor() { + super("service.SourceGetSizeReply", [ + { no: 1, name: "size", kind: "scalar", T: 3 /*ScalarType.INT64*/, L: 0 /*LongType.BIGINT*/ } + ]); + } + create(value?: PartialMessage): SourceGetSizeReply { + const message = { size: 0n }; + globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: SourceGetSizeReply): SourceGetSizeReply { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* int64 size */ 1: + message.size = reader.int64().toBigInt(); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: SourceGetSizeReply, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* int64 size = 1; */ + if (message.size !== 0n) + writer.tag(1, WireType.Varint).int64(message.size); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message service.SourceGetSizeReply + */ +export const SourceGetSizeReply = new SourceGetSizeReply$Type(); +// @generated message type with reflection information, may provide speed optimized methods class DeviceListRequest$Type extends MessageType { constructor() { super("service.DeviceListRequest", []); @@ -2373,6 +2496,7 @@ export const Service = new ServiceType("service.Service", [ { name: "JobDisplay", options: {}, I: JobDisplayRequest, O: JobDisplayReply }, { name: "JobGetLog", options: {}, I: JobGetLogRequest, O: JobGetLogReply }, { name: "SourceList", options: {}, I: SourceListRequest, O: SourceListReply }, + { name: "SourceGetSize", options: {}, I: SourceGetSizeRequest, O: SourceGetSizeReply }, { name: "DeviceList", options: {}, I: DeviceListRequest, O: DeviceListReply }, { name: "LibraryExport", options: {}, I: LibraryExportRequest, O: LibraryExportReply }, { name: "LibraryTrim", options: {}, I: LibraryTrimRequest, O: LibraryTrimReply } diff --git a/frontend/src/init.tsx b/frontend/src/init.tsx index c58efec..c051517 100644 --- a/frontend/src/init.tsx +++ b/frontend/src/init.tsx @@ -1,30 +1,38 @@ -import { setChonkyDefaults } from "@samuelncui/chonky"; +import * as React from "react"; +import { ChonkyIconProps, setChonkyDefaults } from "@samuelncui/chonky"; import { ChonkyIconFA } from "@samuelncui/chonky-icon-fontawesome"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faPencilAlt } from "@fortawesome/free-solid-svg-icons/faPencilAlt"; +import { unstable_ClassNameGenerator as ClassNameGenerator } from "@mui/material/className"; +import { styled } from "@mui/material/styles"; -const ExternalIcons: Record = { - edit: faPencilAlt, -}; +import DataUsageIcon from "@mui/icons-material/DataUsage"; +import DriveFileRenameOutlineIcon from "@mui/icons-material/DriveFileRenameOutline"; +import FiberNewIcon from "@mui/icons-material/FiberNew"; +import CleaningServicesIcon from "@mui/icons-material/CleaningServices"; + +const MUIStyled = (Icon: typeof DataUsageIcon) => styled(Icon)({ verticalAlign: "-0.2em", fontSize: "1.1rem" }); + +const MUIIconMap = { + "mui-data-usage": MUIStyled(DataUsageIcon), + "mui-rename": MUIStyled(DriveFileRenameOutlineIcon), + "mui-fiber-new": MUIStyled(FiberNewIcon), + "mui-cleaning": MUIStyled(CleaningServicesIcon), +} as const; setChonkyDefaults({ - iconComponent: (props) => { - const icon = ExternalIcons[props.icon] as any; - if (!!icon) { - const faProps = { - ...props, - icon: icon, - } as const; - return ; + iconComponent: React.memo((props) => { + const { icon, ...otherProps } = props; + + const MUIIcon = MUIIconMap[icon as keyof typeof MUIIconMap]; + if (!!MUIIcon) { + const { fixedWidth: _, ...props } = otherProps; + return ; } return ; - }, + }) as React.FC, }); -import { unstable_ClassNameGenerator as ClassNameGenerator } from "@mui/material/className"; - ClassNameGenerator.configure( // Do something with the componentName (componentName: string) => `app-${componentName}`, diff --git a/frontend/src/pages/backup.tsx b/frontend/src/pages/backup.tsx index 3d4447d..acca2e8 100644 --- a/frontend/src/pages/backup.tsx +++ b/frontend/src/pages/backup.tsx @@ -1,4 +1,5 @@ import { useState, useEffect, useMemo, useCallback, FC, useRef, RefObject } from "react"; +import { toast } from "react-toastify"; import Grid from "@mui/material/Grid"; import Box from "@mui/material/Box"; @@ -10,19 +11,23 @@ import { Root } from "../api"; import { AddFileAction, RefreshListAction, CreateBackupJobAction } from "../actions"; import { JobArchiveParam, JobCreateRequest, Source } from "../entity"; import { chonkyI18n } from "../tools"; +import { ToobarInfo } from "../components/toolbarInfo"; -const useBackupSourceBrowser = (source: RefObject) => { +const useBackupSourceBrowser = (targetFiles: FileArray, source: RefObject) => { const [files, setFiles] = useState(Array(1).fill(null)); - const [folderChain, setFolderChan] = useState([Root]); + const [folderChain, setFolderChain] = useState([Root]); - const openFolder = useCallback((path: string) => { - (async () => { - const result = await cli.sourceList({ path }).response; + const openFolder = useCallback( + (path: string) => { + (async () => { + const result = await cli.sourceList({ path }).response; - setFiles(convertSourceFiles(result.children)); - setFolderChan(convertSourceFiles(result.chain)); - })(); - }, []); + setFiles(convertSourceFiles(result.children)); + setFolderChain(convertSourceFiles(result.chain)); + })(); + }, + [targetFiles, setFiles, setFolderChain], + ); useEffect(() => openFolder(""), []); const onFileAction = useCallback( @@ -49,7 +54,14 @@ const useBackupSourceBrowser = (source: RefObject) => { return; } - source.current.requestFileAction(AddFileAction, data.payload); + const selectedFiles = data.payload.selectedFiles.map((file) => ({ + ...file, + name: file.id, + openable: false, + draggable: false, + })); + + source.current.requestFileAction(AddFileAction, { ...data.payload, selectedFiles }); return; } }, @@ -59,7 +71,21 @@ const useBackupSourceBrowser = (source: RefObject) => { const fileActions = useMemo(() => [ChonkyActions.StartDragNDrop, RefreshListAction], []); return { - files, + files: useMemo(() => { + const targetFileIDs = new Set((targetFiles.filter((f) => !!f) as FileData[]).map((f) => f.id)); + const getDragable = !!folderChain.find((file) => file && targetFileIDs.has(file.id)) + ? (_: FileData) => false + : (file: FileData) => !targetFileIDs.has(file.id); + + return files.map((file) => { + if (!file) { + return file; + } + + const draggable = getDragable(file); + return { ...file, droppable: false, draggable, selectable: draggable }; + }); + }, [files, folderChain, targetFiles]), folderChain, onFileAction, fileActions, @@ -69,19 +95,37 @@ const useBackupSourceBrowser = (source: RefObject) => { }; }; +const targetFolderChain = [ + { + id: "backup_waitlist", + name: "Backup Waitlist", + isDir: true, + openable: true, + selectable: true, + draggable: true, + droppable: true, + }, +] as FileArray; + const useBackupTargetBrowser = () => { const [files, setFiles] = useState(Array(0)); - const [folderChain, setFolderChan] = useState([ - { - id: "backup_waitlist", - name: "Backup Waitlist", - isDir: true, - openable: true, - selectable: true, - draggable: true, - droppable: true, + + const onFileSizeUpdated = useCallback( + (id: string, size: number) => { + setFiles( + (files.filter((file) => !!file) as FileData[]).map((file: FileData) => { + if (file.id === id) { + return { ...file, size }; + } + + return file; + }), + ); }, - ]); + [files, setFiles], + ); + const onFileSizeUpdatedRef = useRef(onFileSizeUpdated); + onFileSizeUpdatedRef.current = onFileSizeUpdated; const onFileAction = useCallback( (data: ChonkyFileActionData) => { @@ -93,10 +137,23 @@ const useBackupTargetBrowser = () => { })(); return; case AddFileAction.id: - setFiles([ - ...files, - ...((data.payload as any)?.selectedFiles as FileData[]).map((file) => ({ ...file, name: file.id, openable: false, draggable: false })), - ]); + const addedFiles = (data.payload as any)?.selectedFiles as FileData[]; + setFiles([...files, ...addedFiles]); + + (async () => { + for (const file of addedFiles) { + if (!file) { + continue; + } + if (file.size !== undefined) { + continue; + } + + const reply = await cli.sourceGetSize({ path: file.id }).response; + onFileSizeUpdatedRef.current(file.id, Number(reply.size)); + } + })(); + return; case CreateBackupJobAction.id: (async () => { @@ -124,7 +181,7 @@ const useBackupTargetBrowser = () => { const req = makeArchiveParam(1n, { sources }); console.log(req, await cli.jobCreate(req).response); - alert("Create Backup Job Success!"); + toast.success("Create Backup Job Success!"); })(); return; } @@ -136,7 +193,7 @@ const useBackupTargetBrowser = () => { return { files, - folderChain, + folderChain: targetFolderChain, onFileAction, fileActions, defaultFileViewActionId: ChonkyActions.EnableListView.id, @@ -149,8 +206,8 @@ export const BackupType = "backup"; export const BackupBrowser = () => { const target = useRef(null); - const sourceProps = useBackupSourceBrowser(target); const targetProps = useBackupTargetBrowser(); + const sourceProps = useBackupSourceBrowser(targetProps.files, target); return ( @@ -166,7 +223,9 @@ export const BackupBrowser = () => { - + + + diff --git a/frontend/src/pages/file.tsx b/frontend/src/pages/file.tsx index a422712..99c6717 100644 --- a/frontend/src/pages/file.tsx +++ b/frontend/src/pages/file.tsx @@ -7,7 +7,7 @@ import { ChonkyActions, ChonkyFileActionData } from "@samuelncui/chonky"; import { cli, convertFiles } from "../api"; import { Root } from "../api"; -import { RenameFileAction, RefreshListAction } from "../actions"; +import { RenameFileAction, RefreshListAction, GetDataUsageAction, CreateFolder } from "../actions"; import { ToobarInfo } from "../components/toolbarInfo"; import { useDetailModal, DetailModal } from "./file-detail"; @@ -49,13 +49,16 @@ const useFileBrowser = (storageKey: string, refreshAll: () => Promise, ope return last.id; }, [folderChain]); - const openFolder = useCallback(async (id: string) => { - const [file, folderChain] = await Promise.all([cli.fileGet({ id: BigInt(id) }).response, cli.fileListParents({ id: BigInt(id) }).response]); + const openFolder = useCallback( + async (id: string, needSize: boolean = false) => { + const [file, folderChain] = await Promise.all([cli.fileGet({ id: BigInt(id), needSize }).response, cli.fileListParents({ id: BigInt(id) }).response]); - setFiles(convertFiles(file.children)); - setFolderChan([Root, ...convertFiles(folderChain.parents)]); - localStorage.setItem(storageKey, id); - }, []); + setFiles(convertFiles(file.children, needSize)); + setFolderChan([Root, ...convertFiles(folderChain.parents, needSize)]); + localStorage.setItem(storageKey, id); + }, + [setFiles, setFolderChan], + ); useEffect(() => { (async () => { const storagedID = localStorage.getItem(storageKey); @@ -74,7 +77,6 @@ const useFileBrowser = (storageKey: string, refreshAll: () => Promise, ope const onFileAction = useCallback( (data: ChonkyFileActionData) => { - // console.log(data); switch (data.id) { case ChonkyActions.OpenFiles.id: (async () => { @@ -125,7 +127,7 @@ const useFileBrowser = (storageKey: string, refreshAll: () => Promise, ope await refreshAll(); })(); return; - case ChonkyActions.CreateFolder.id: + case CreateFolder.id: (async () => { const name = prompt("Provide the name for your new folder:"); if (!name) { @@ -144,6 +146,9 @@ const useFileBrowser = (storageKey: string, refreshAll: () => Promise, ope await refreshAll(); })(); + return; + case GetDataUsageAction.id: + openFolder(currentID, true); return; case RefreshListAction.id: openFolder(currentID); @@ -153,7 +158,10 @@ const useFileBrowser = (storageKey: string, refreshAll: () => Promise, ope [openFolder, openDetailModel, refreshAll, currentID], ); - const fileActions = useMemo(() => [ChonkyActions.CreateFolder, ChonkyActions.DeleteFiles, ChonkyActions.MoveFiles, RenameFileAction, RefreshListAction], []); + const fileActions = useMemo( + () => [CreateFolder, GetDataUsageAction, ChonkyActions.DeleteFiles, ChonkyActions.MoveFiles, RenameFileAction, RefreshListAction], + [], + ); const totalSize = useMemo(() => { return files.reduce((total, file) => total + (file?.size ? file.size : 0), 0); }, [files]); @@ -186,7 +194,6 @@ export const FileBrowser = () => { }, 10000); return () => clearInterval(interval); }, []); - useEffect(() => {}); return ( @@ -195,7 +202,7 @@ export const FileBrowser = () => { - + @@ -205,7 +212,7 @@ export const FileBrowser = () => { - + diff --git a/frontend/src/pages/jobs.tsx b/frontend/src/pages/jobs.tsx index 16db266..8c7ed1c 100644 --- a/frontend/src/pages/jobs.tsx +++ b/frontend/src/pages/jobs.tsx @@ -98,11 +98,8 @@ export const JobsBrowser = () => { refreshRef.current = refresh; useEffect(() => { - var timer: NodeJS.Timeout; - (async () => { - await refreshRef.current(true); - timer = setInterval(() => refreshRef.current(), 2000); - })(); + refreshRef.current(true); + const timer = setInterval(() => refreshRef.current(), 2000); return () => { if (!timer) { diff --git a/frontend/src/pages/restore.tsx b/frontend/src/pages/restore.tsx index d59184a..e0b9e8d 100644 --- a/frontend/src/pages/restore.tsx +++ b/frontend/src/pages/restore.tsx @@ -1,4 +1,5 @@ import { useState, useEffect, useMemo, useCallback, FC, useRef, RefObject } from "react"; +import { toast } from "react-toastify"; import Grid from "@mui/material/Grid"; import Box from "@mui/material/Box"; @@ -8,20 +9,35 @@ import { ChonkyActions, ChonkyFileActionData, FileData } from "@samuelncui/chonk import { ToobarInfo } from "../components/toolbarInfo"; import { Root, cli, convertFiles } from "../api"; -import { AddFileAction, RefreshListAction, CreateRestoreJobAction } from "../actions"; +import { AddFileAction, RefreshListAction, CreateRestoreJobAction, GetDataUsageAction } from "../actions"; import { JobCreateRequest, JobRestoreParam, Source } from "../entity"; import { chonkyI18n } from "../tools"; -const useRestoreSourceBrowser = (target: RefObject) => { +const useRestoreSourceBrowser = (targetFiles: FileArray, target: RefObject) => { const [files, setFiles] = useState(Array(1).fill(null)); const [folderChain, setFolderChan] = useState([Root]); + const currentID = useMemo(() => { + if (folderChain.length === 0) { + return "0"; + } - const openFolder = useCallback(async (id: string) => { - const [file, folderChain] = await Promise.all([cli.fileGet({ id: BigInt(id) }).response, cli.fileListParents({ id: BigInt(id) }).response]); + const last = folderChain.slice(-1)[0]; + if (!last) { + return "0"; + } - setFiles(convertFiles(file.children).map((file) => ({ ...file, droppable: false }))); - setFolderChan([Root, ...convertFiles(folderChain.parents)]); - }, []); + return last.id; + }, [folderChain]); + + const openFolder = useCallback( + async (id: string, needSize: boolean = false) => { + const [file, folderChain] = await Promise.all([cli.fileGet({ id: BigInt(id), needSize }).response, cli.fileListParents({ id: BigInt(id) }).response]); + + setFiles(convertFiles(file.children, needSize).map((file) => ({ ...file, droppable: false }))); + setFolderChan([Root, ...convertFiles(folderChain.parents, needSize)]); + }, + [setFiles, setFolderChan], + ); useEffect(() => { openFolder(Root.id); }, []); @@ -44,39 +60,52 @@ const useRestoreSourceBrowser = (target: RefObject) => { } })(); + return; + case GetDataUsageAction.id: + openFolder(currentID, true); return; case ChonkyActions.EndDragNDrop.id: - (async () => { - if (!target.current) { - return; - } + if (!target.current) { + return; + } - const base = folderChain - .filter((file): file is FileData => !!file && file.id !== "0") - .map((file) => file.name) - .join("/"); + const base = folderChain + .filter((file): file is FileData => !!file && file.id !== "0") + .map((file) => file.name) + .join("/"); - const selectedFiles = data.payload.selectedFiles.map((file) => ({ - ...file, - name: base ? base + "/" + file.name : file.name, - openable: false, - draggable: false, - })); - await target.current.requestFileAction(AddFileAction, { ...data.payload, selectedFiles }); - })(); + const selectedFiles = data.payload.selectedFiles.map((file) => ({ + ...file, + name: base ? base + "/" + file.name : file.name, + openable: false, + draggable: false, + })); + target.current.requestFileAction(AddFileAction, { ...data.payload, selectedFiles }); return; } - - console.log("source done", data); }, - [openFolder, target, folderChain], + [openFolder, target, folderChain, currentID], ); - const fileActions = useMemo(() => [ChonkyActions.StartDragNDrop, RefreshListAction], []); + const fileActions = useMemo(() => [GetDataUsageAction, ChonkyActions.StartDragNDrop, RefreshListAction], []); return { - files, + files: useMemo(() => { + const targetFileIDs = new Set((targetFiles.filter((f) => !!f) as FileData[]).map((f) => f.id)); + const getDragable = !!folderChain.find((file) => file && targetFileIDs.has(file.id)) + ? (_: FileData) => false + : (file: FileData) => !targetFileIDs.has(file.id); + + return files.map((file) => { + if (!file) { + return file; + } + + const draggable = getDragable(file); + return { ...file, droppable: false, draggable, selectable: draggable }; + }); + }, [files, folderChain, targetFiles]), folderChain, onFileAction, fileActions, @@ -86,19 +115,37 @@ const useRestoreSourceBrowser = (target: RefObject) => { }; }; +const targetFolderChain = [ + { + id: "restore_waitlist", + name: "Restore Waitlist", + isDir: true, + openable: true, + selectable: true, + draggable: true, + droppable: true, + }, +] as FileArray; + const useRestoreTargetBrowser = () => { const [files, setFiles] = useState(Array(0)); - const [folderChain, setFolderChan] = useState([ - { - id: "restore_waitlist", - name: "Restore Waitlist", - isDir: true, - openable: true, - selectable: true, - draggable: true, - droppable: true, + + const onFileSizeUpdated = useCallback( + (id: string, size: number) => { + setFiles( + (files.filter((file) => !!file) as FileData[]).map((file: FileData) => { + if (file.id === id) { + return { ...file, size }; + } + + return file; + }), + ); }, - ]); + [files, setFiles], + ); + const onFileSizeUpdatedRef = useRef(onFileSizeUpdated); + onFileSizeUpdatedRef.current = onFileSizeUpdated; const onFileAction = useCallback( (data: ChonkyFileActionData) => { @@ -110,13 +157,30 @@ const useRestoreTargetBrowser = () => { })(); return; case AddFileAction.id: - setFiles([...files, ...((data.payload as any)?.selectedFiles as FileData[])]); + const addedFiles = (data.payload as any)?.selectedFiles as FileData[]; + setFiles([...files, ...addedFiles]); + + (async () => { + for (const file of addedFiles) { + if (!file) { + continue; + } + if (file.size !== undefined) { + continue; + } + + const reply = await cli.fileGet({ id: BigInt(file.id), needSize: true }).response; + onFileSizeUpdatedRef.current(file.id, Number(reply.file?.size)); + } + })(); + return; case CreateRestoreJobAction.id: (async () => { const fileIds = files.filter((file): file is FileData => !!file && file.id !== "0").map((file) => BigInt(file.id)); console.log(await cli.jobCreate(makeParam(1n, { fileIds })).response); - alert("Create Restore Job Success!"); + + toast.success("Create Restore Job Success!"); })(); return; } @@ -128,7 +192,7 @@ const useRestoreTargetBrowser = () => { return { files, - folderChain, + folderChain: targetFolderChain, onFileAction, fileActions, defaultFileViewActionId: ChonkyActions.EnableListView.id, @@ -141,8 +205,8 @@ export const RestoreType = "restore"; export const RestoreBrowser = () => { const target = useRef(null); - const sourceProps = useRestoreSourceBrowser(target); const targetProps = useRestoreTargetBrowser(); + const sourceProps = useRestoreSourceBrowser(targetProps.files, target); return ( diff --git a/frontend/src/pages/tapes.tsx b/frontend/src/pages/tapes.tsx index ea60a52..3690160 100644 --- a/frontend/src/pages/tapes.tsx +++ b/frontend/src/pages/tapes.tsx @@ -1,4 +1,5 @@ import { useState, useEffect, useMemo, useCallback, FC, useRef, RefObject } from "react"; +import { toast } from "react-toastify"; import Grid from "@mui/material/Grid"; import Box from "@mui/material/Box"; @@ -111,7 +112,7 @@ const useTapesSourceBrowser = (source: RefObject) => { } console.log(await cli.libraryTrim({ trimFile: true, trimPosition: true }).response); - alert("Trim Library Success!"); + toast.success("Trim Library Success!"); })(); return; }