From adb0e3f23050a86cd1c507d374e9d45f5eb5ee27 Mon Sep 17 00:00:00 2001 From: Gareth George Date: Mon, 25 Dec 2023 07:41:31 +0000 Subject: [PATCH] feat: add stats to repo view e.g. total size in storage --- gen/go/v1/operations.pb.go | 242 +++++++++++++++------- gen/go/v1/service.pb.go | 246 +++++++++++------------ gen/go/v1/service_grpc.pb.go | 41 ++++ gen/go/v1/v1connect/service.connect.go | 33 +++ internal/api/server.go | 9 +- internal/orchestrator/orchestrator.go | 11 +- internal/orchestrator/repo.go | 13 ++ internal/orchestrator/taskbackup.go | 1 + internal/orchestrator/taskrestore.go | 13 +- internal/orchestrator/taskstats.go | 81 ++++++++ proto/v1/operations.proto | 5 + proto/v1/service.proto | 12 +- webui/gen/ts/v1/operations_pb.ts | 46 ++++- webui/gen/ts/v1/service_connect.ts | 13 ++ webui/gen/ts/v1/service_pb.ts | 22 +- webui/src/components/OperationList.tsx | 112 ++++++----- webui/src/components/OperationTree.tsx | 34 ---- webui/src/components/SnapshotBrowser.tsx | 8 +- webui/src/state/oplog.ts | 11 +- webui/src/views/RepoView.tsx | 110 +++++++--- 20 files changed, 697 insertions(+), 366 deletions(-) create mode 100644 internal/orchestrator/taskstats.go diff --git a/gen/go/v1/operations.pb.go b/gen/go/v1/operations.pb.go index 80dc989a..9d784e82 100644 --- a/gen/go/v1/operations.pb.go +++ b/gen/go/v1/operations.pb.go @@ -211,6 +211,7 @@ type Operation struct { // *Operation_OperationForget // *Operation_OperationPrune // *Operation_OperationRestore + // *Operation_OperationStats Op isOperation_Op `protobuf_oneof:"op"` } @@ -344,6 +345,13 @@ func (x *Operation) GetOperationRestore() *OperationRestore { return nil } +func (x *Operation) GetOperationStats() *OperationStats { + if x, ok := x.GetOp().(*Operation_OperationStats); ok { + return x.OperationStats + } + return nil +} + type isOperation_Op interface { isOperation_Op() } @@ -368,6 +376,10 @@ type Operation_OperationRestore struct { OperationRestore *OperationRestore `protobuf:"bytes,104,opt,name=operation_restore,json=operationRestore,proto3,oneof"` } +type Operation_OperationStats struct { + OperationStats *OperationStats `protobuf:"bytes,105,opt,name=operation_stats,json=operationStats,proto3,oneof"` +} + func (*Operation_OperationBackup) isOperation_Op() {} func (*Operation_OperationIndexSnapshot) isOperation_Op() {} @@ -378,6 +390,8 @@ func (*Operation_OperationPrune) isOperation_Op() {} func (*Operation_OperationRestore) isOperation_Op() {} +func (*Operation_OperationStats) isOperation_Op() {} + // OperationEvent is used in the wireformat to stream operation changes to clients type OperationEvent struct { state protoimpl.MessageState @@ -712,6 +726,53 @@ func (x *OperationRestore) GetStatus() *RestoreProgressEntry { return nil } +type OperationStats struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Stats *RepoStats `protobuf:"bytes,1,opt,name=stats,proto3" json:"stats,omitempty"` +} + +func (x *OperationStats) Reset() { + *x = OperationStats{} + if protoimpl.UnsafeEnabled { + mi := &file_v1_operations_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *OperationStats) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*OperationStats) ProtoMessage() {} + +func (x *OperationStats) ProtoReflect() protoreflect.Message { + mi := &file_v1_operations_proto_msgTypes[8] + 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 OperationStats.ProtoReflect.Descriptor instead. +func (*OperationStats) Descriptor() ([]byte, []int) { + return file_v1_operations_proto_rawDescGZIP(), []int{8} +} + +func (x *OperationStats) GetStats() *RepoStats { + if x != nil { + return x.Stats + } + return nil +} + var File_v1_operations_proto protoreflect.FileDescriptor var file_v1_operations_proto_rawDesc = []byte{ @@ -722,7 +783,7 @@ var file_v1_operations_proto_rawDesc = []byte{ 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x80, 0x05, 0x0a, 0x09, + 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xbf, 0x05, 0x0a, 0x09, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x70, 0x6f, @@ -762,65 +823,73 @@ var file_v1_operations_proto_rawDesc = []byte{ 0x5f, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x18, 0x68, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x48, 0x00, 0x52, 0x10, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x42, 0x04, 0x0a, 0x02, 0x6f, 0x70, 0x22, 0x69, - 0x0a, 0x0e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x12, 0x2a, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, - 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2b, 0x0a, 0x09, - 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x0d, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, - 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x4b, 0x0a, 0x0f, 0x4f, 0x70, 0x65, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0x38, 0x0a, 0x0b, - 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x17, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x50, 0x72, 0x6f, - 0x67, 0x72, 0x65, 0x73, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x6c, 0x61, 0x73, 0x74, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x82, 0x01, 0x0a, 0x16, 0x4f, 0x70, 0x65, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, - 0x74, 0x12, 0x2e, 0x0a, 0x08, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x69, 0x63, 0x53, - 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, 0x08, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, - 0x74, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x6f, 0x72, 0x67, 0x6f, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x06, 0x66, 0x6f, 0x72, 0x67, 0x6f, 0x74, 0x12, 0x20, 0x0a, 0x0c, 0x66, 0x6f, 0x72, - 0x67, 0x6f, 0x74, 0x5f, 0x62, 0x79, 0x5f, 0x6f, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x0a, 0x66, 0x6f, 0x72, 0x67, 0x6f, 0x74, 0x42, 0x79, 0x4f, 0x70, 0x22, 0x6a, 0x0a, 0x0f, 0x4f, - 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6f, 0x72, 0x67, 0x65, 0x74, 0x12, 0x2a, - 0x0a, 0x06, 0x66, 0x6f, 0x72, 0x67, 0x65, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, - 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x69, 0x63, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, - 0x6f, 0x74, 0x52, 0x06, 0x66, 0x6f, 0x72, 0x67, 0x65, 0x74, 0x12, 0x2b, 0x0a, 0x06, 0x70, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x76, 0x31, 0x2e, - 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, - 0x06, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x22, 0x28, 0x0a, 0x0e, 0x4f, 0x70, 0x65, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x75, 0x6e, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x75, 0x74, - 0x70, 0x75, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, - 0x74, 0x22, 0x70, 0x0a, 0x10, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, - 0x73, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x72, - 0x67, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, - 0x74, 0x12, 0x30, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x18, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x50, 0x72, - 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x2a, 0x60, 0x0a, 0x12, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x11, 0x0a, 0x0d, 0x45, 0x56, 0x45, - 0x4e, 0x54, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, - 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, - 0x11, 0x0a, 0x0d, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x44, - 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x44, 0x45, 0x4c, 0x45, - 0x54, 0x45, 0x44, 0x10, 0x03, 0x2a, 0xc2, 0x01, 0x0a, 0x0f, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x54, 0x41, - 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x12, 0x0a, - 0x0e, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, - 0x01, 0x12, 0x15, 0x0a, 0x11, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x49, 0x4e, 0x50, 0x52, - 0x4f, 0x47, 0x52, 0x45, 0x53, 0x53, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x54, 0x41, 0x54, - 0x55, 0x53, 0x5f, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x03, 0x12, 0x12, 0x0a, 0x0e, - 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x57, 0x41, 0x52, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x07, - 0x12, 0x10, 0x0a, 0x0c, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, - 0x10, 0x04, 0x12, 0x1b, 0x0a, 0x17, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x53, 0x59, 0x53, - 0x54, 0x45, 0x4d, 0x5f, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x4c, 0x45, 0x44, 0x10, 0x05, 0x12, - 0x19, 0x0a, 0x15, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x43, - 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x4c, 0x45, 0x44, 0x10, 0x06, 0x42, 0x2c, 0x5a, 0x2a, 0x67, 0x69, - 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x61, 0x72, 0x65, 0x74, 0x68, 0x67, - 0x65, 0x6f, 0x72, 0x67, 0x65, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x72, 0x65, 0x73, 0x74, 0x2f, 0x67, - 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6e, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x3d, 0x0a, 0x0f, 0x6f, 0x70, 0x65, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x69, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x12, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x53, 0x74, 0x61, 0x74, 0x73, 0x48, 0x00, 0x52, 0x0e, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x42, 0x04, 0x0a, 0x02, 0x6f, 0x70, 0x22, 0x69, 0x0a, + 0x0e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, + 0x2a, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, + 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2b, 0x0a, 0x09, 0x6f, + 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, + 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x6f, + 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x4b, 0x0a, 0x0f, 0x4f, 0x70, 0x65, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0x38, 0x0a, 0x0b, 0x6c, + 0x61, 0x73, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x17, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x50, 0x72, 0x6f, 0x67, + 0x72, 0x65, 0x73, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x6c, 0x61, 0x73, 0x74, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x82, 0x01, 0x0a, 0x16, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, + 0x12, 0x2e, 0x0a, 0x08, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x69, 0x63, 0x53, 0x6e, + 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, 0x08, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, + 0x12, 0x16, 0x0a, 0x06, 0x66, 0x6f, 0x72, 0x67, 0x6f, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x06, 0x66, 0x6f, 0x72, 0x67, 0x6f, 0x74, 0x12, 0x20, 0x0a, 0x0c, 0x66, 0x6f, 0x72, 0x67, + 0x6f, 0x74, 0x5f, 0x62, 0x79, 0x5f, 0x6f, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, + 0x66, 0x6f, 0x72, 0x67, 0x6f, 0x74, 0x42, 0x79, 0x4f, 0x70, 0x22, 0x6a, 0x0a, 0x0f, 0x4f, 0x70, + 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6f, 0x72, 0x67, 0x65, 0x74, 0x12, 0x2a, 0x0a, + 0x06, 0x66, 0x6f, 0x72, 0x67, 0x65, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, + 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x69, 0x63, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, + 0x74, 0x52, 0x06, 0x66, 0x6f, 0x72, 0x67, 0x65, 0x74, 0x12, 0x2b, 0x0a, 0x06, 0x70, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x76, 0x31, 0x2e, 0x52, + 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x06, + 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x22, 0x28, 0x0a, 0x0e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x75, 0x6e, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x75, 0x74, 0x70, + 0x75, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, + 0x22, 0x70, 0x0a, 0x10, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, + 0x74, 0x6f, 0x72, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x12, 0x30, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x18, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x50, 0x72, 0x6f, + 0x67, 0x72, 0x65, 0x73, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x22, 0x35, 0x0a, 0x0e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, + 0x74, 0x61, 0x74, 0x73, 0x12, 0x23, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x53, 0x74, 0x61, + 0x74, 0x73, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2a, 0x60, 0x0a, 0x12, 0x4f, 0x70, 0x65, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, + 0x11, 0x0a, 0x0d, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, + 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x43, 0x52, 0x45, 0x41, + 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x55, + 0x50, 0x44, 0x41, 0x54, 0x45, 0x44, 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x45, 0x56, 0x45, 0x4e, + 0x54, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x44, 0x10, 0x03, 0x2a, 0xc2, 0x01, 0x0a, 0x0f, + 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, + 0x12, 0x0a, 0x0e, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, + 0x4e, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x50, 0x45, + 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x15, 0x0a, 0x11, 0x53, 0x54, 0x41, 0x54, 0x55, + 0x53, 0x5f, 0x49, 0x4e, 0x50, 0x52, 0x4f, 0x47, 0x52, 0x45, 0x53, 0x53, 0x10, 0x02, 0x12, 0x12, + 0x0a, 0x0e, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, + 0x10, 0x03, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x57, 0x41, 0x52, + 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x07, 0x12, 0x10, 0x0a, 0x0c, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, + 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x04, 0x12, 0x1b, 0x0a, 0x17, 0x53, 0x54, 0x41, 0x54, + 0x55, 0x53, 0x5f, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x5f, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, + 0x4c, 0x45, 0x44, 0x10, 0x05, 0x12, 0x19, 0x0a, 0x15, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, + 0x55, 0x53, 0x45, 0x52, 0x5f, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x4c, 0x45, 0x44, 0x10, 0x06, + 0x42, 0x2c, 0x5a, 0x2a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, + 0x61, 0x72, 0x65, 0x74, 0x68, 0x67, 0x65, 0x6f, 0x72, 0x67, 0x65, 0x2f, 0x62, 0x61, 0x63, 0x6b, + 0x72, 0x65, 0x73, 0x74, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -836,7 +905,7 @@ func file_v1_operations_proto_rawDescGZIP() []byte { } var file_v1_operations_proto_enumTypes = make([]protoimpl.EnumInfo, 2) -var file_v1_operations_proto_msgTypes = make([]protoimpl.MessageInfo, 8) +var file_v1_operations_proto_msgTypes = make([]protoimpl.MessageInfo, 9) var file_v1_operations_proto_goTypes = []interface{}{ (OperationEventType)(0), // 0: v1.OperationEventType (OperationStatus)(0), // 1: v1.OperationStatus @@ -848,10 +917,12 @@ var file_v1_operations_proto_goTypes = []interface{}{ (*OperationForget)(nil), // 7: v1.OperationForget (*OperationPrune)(nil), // 8: v1.OperationPrune (*OperationRestore)(nil), // 9: v1.OperationRestore - (*BackupProgressEntry)(nil), // 10: v1.BackupProgressEntry - (*ResticSnapshot)(nil), // 11: v1.ResticSnapshot - (*RetentionPolicy)(nil), // 12: v1.RetentionPolicy - (*RestoreProgressEntry)(nil), // 13: v1.RestoreProgressEntry + (*OperationStats)(nil), // 10: v1.OperationStats + (*BackupProgressEntry)(nil), // 11: v1.BackupProgressEntry + (*ResticSnapshot)(nil), // 12: v1.ResticSnapshot + (*RetentionPolicy)(nil), // 13: v1.RetentionPolicy + (*RestoreProgressEntry)(nil), // 14: v1.RestoreProgressEntry + (*RepoStats)(nil), // 15: v1.RepoStats } var file_v1_operations_proto_depIdxs = []int32{ 3, // 0: v1.OperationList.operations:type_name -> v1.Operation @@ -861,18 +932,20 @@ var file_v1_operations_proto_depIdxs = []int32{ 7, // 4: v1.Operation.operation_forget:type_name -> v1.OperationForget 8, // 5: v1.Operation.operation_prune:type_name -> v1.OperationPrune 9, // 6: v1.Operation.operation_restore:type_name -> v1.OperationRestore - 0, // 7: v1.OperationEvent.type:type_name -> v1.OperationEventType - 3, // 8: v1.OperationEvent.operation:type_name -> v1.Operation - 10, // 9: v1.OperationBackup.last_status:type_name -> v1.BackupProgressEntry - 11, // 10: v1.OperationIndexSnapshot.snapshot:type_name -> v1.ResticSnapshot - 11, // 11: v1.OperationForget.forget:type_name -> v1.ResticSnapshot - 12, // 12: v1.OperationForget.policy:type_name -> v1.RetentionPolicy - 13, // 13: v1.OperationRestore.status:type_name -> v1.RestoreProgressEntry - 14, // [14:14] is the sub-list for method output_type - 14, // [14:14] is the sub-list for method input_type - 14, // [14:14] is the sub-list for extension type_name - 14, // [14:14] is the sub-list for extension extendee - 0, // [0:14] is the sub-list for field type_name + 10, // 7: v1.Operation.operation_stats:type_name -> v1.OperationStats + 0, // 8: v1.OperationEvent.type:type_name -> v1.OperationEventType + 3, // 9: v1.OperationEvent.operation:type_name -> v1.Operation + 11, // 10: v1.OperationBackup.last_status:type_name -> v1.BackupProgressEntry + 12, // 11: v1.OperationIndexSnapshot.snapshot:type_name -> v1.ResticSnapshot + 12, // 12: v1.OperationForget.forget:type_name -> v1.ResticSnapshot + 13, // 13: v1.OperationForget.policy:type_name -> v1.RetentionPolicy + 14, // 14: v1.OperationRestore.status:type_name -> v1.RestoreProgressEntry + 15, // 15: v1.OperationStats.stats:type_name -> v1.RepoStats + 16, // [16:16] is the sub-list for method output_type + 16, // [16:16] is the sub-list for method input_type + 16, // [16:16] is the sub-list for extension type_name + 16, // [16:16] is the sub-list for extension extendee + 0, // [0:16] is the sub-list for field type_name } func init() { file_v1_operations_proto_init() } @@ -979,6 +1052,18 @@ func file_v1_operations_proto_init() { return nil } } + file_v1_operations_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*OperationStats); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } file_v1_operations_proto_msgTypes[1].OneofWrappers = []interface{}{ (*Operation_OperationBackup)(nil), @@ -986,6 +1071,7 @@ func file_v1_operations_proto_init() { (*Operation_OperationForget)(nil), (*Operation_OperationPrune)(nil), (*Operation_OperationRestore)(nil), + (*Operation_OperationStats)(nil), } type x struct{} out := protoimpl.TypeBuilder{ @@ -993,7 +1079,7 @@ func file_v1_operations_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_v1_operations_proto_rawDesc, NumEnums: 2, - NumMessages: 8, + NumMessages: 9, NumExtensions: 0, NumServices: 0, }, diff --git a/gen/go/v1/service.pb.go b/gen/go/v1/service.pb.go index 081471c0..64101297 100644 --- a/gen/go/v1/service.pb.go +++ b/gen/go/v1/service.pb.go @@ -225,11 +225,10 @@ type RestoreSnapshotRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - RepoId string `protobuf:"bytes,1,opt,name=repo_id,json=repoId,proto3" json:"repo_id,omitempty"` - PlanId string `protobuf:"bytes,2,opt,name=plan_id,json=planId,proto3" json:"plan_id,omitempty"` - SnapshotId string `protobuf:"bytes,3,opt,name=snapshot_id,json=snapshotId,proto3" json:"snapshot_id,omitempty"` - Path string `protobuf:"bytes,4,opt,name=path,proto3" json:"path,omitempty"` - Target string `protobuf:"bytes,5,opt,name=target,proto3" json:"target,omitempty"` + PlanId string `protobuf:"bytes,1,opt,name=plan_id,json=planId,proto3" json:"plan_id,omitempty"` + SnapshotId string `protobuf:"bytes,2,opt,name=snapshot_id,json=snapshotId,proto3" json:"snapshot_id,omitempty"` + Path string `protobuf:"bytes,3,opt,name=path,proto3" json:"path,omitempty"` + Target string `protobuf:"bytes,4,opt,name=target,proto3" json:"target,omitempty"` } func (x *RestoreSnapshotRequest) Reset() { @@ -264,13 +263,6 @@ func (*RestoreSnapshotRequest) Descriptor() ([]byte, []int) { return file_v1_service_proto_rawDescGZIP(), []int{3} } -func (x *RestoreSnapshotRequest) GetRepoId() string { - if x != nil { - return x.RepoId - } - return "" -} - func (x *RestoreSnapshotRequest) GetPlanId() string { if x != nil { return x.PlanId @@ -569,101 +561,103 @@ var file_v1_service_proto_rawDesc = []byte{ 0x0a, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x64, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x03, 0x52, 0x03, 0x69, 0x64, 0x73, 0x12, 0x15, 0x0a, 0x06, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6c, - 0x61, 0x73, 0x74, 0x4e, 0x22, 0x97, 0x01, 0x0a, 0x16, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, - 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x17, 0x0a, 0x07, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x06, 0x72, 0x65, 0x70, 0x6f, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x6c, 0x61, 0x6e, - 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x6c, 0x61, 0x6e, 0x49, - 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x69, 0x64, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, - 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x22, 0x68, - 0x0a, 0x18, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x46, 0x69, - 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x72, 0x65, - 0x70, 0x6f, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x70, - 0x6f, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, - 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, - 0x6f, 0x74, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x56, 0x0a, 0x19, 0x4c, 0x69, 0x73, 0x74, - 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x25, 0x0a, 0x07, 0x65, 0x6e, 0x74, - 0x72, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x76, 0x31, 0x2e, - 0x4c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, - 0x22, 0xd3, 0x01, 0x0a, 0x07, 0x4c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x12, 0x0a, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x75, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x67, 0x69, - 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x67, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, - 0x73, 0x69, 0x7a, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, - 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, - 0x6d, 0x6f, 0x64, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x08, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x6d, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x74, - 0x69, 0x6d, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x74, 0x69, 0x6d, 0x65, - 0x12, 0x14, 0x0a, 0x05, 0x63, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x63, 0x74, 0x69, 0x6d, 0x65, 0x32, 0xfd, 0x06, 0x0a, 0x08, 0x42, 0x61, 0x63, 0x6b, 0x72, - 0x65, 0x73, 0x74, 0x12, 0x31, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x22, 0x00, 0x12, 0x25, 0x0a, 0x09, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x12, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, - 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x00, 0x12, 0x21, 0x0a, - 0x07, 0x41, 0x64, 0x64, 0x52, 0x65, 0x70, 0x6f, 0x12, 0x08, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, - 0x70, 0x6f, 0x1a, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x00, - 0x12, 0x44, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x12, - 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x22, 0x00, 0x30, 0x01, 0x12, 0x3e, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4f, 0x70, 0x65, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x18, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, - 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x11, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x4c, 0x69, 0x73, 0x74, 0x22, 0x00, 0x12, 0x43, 0x0a, 0x0d, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x6e, - 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x73, 0x12, 0x18, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, - 0x74, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x16, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x69, 0x63, 0x53, 0x6e, 0x61, - 0x70, 0x73, 0x68, 0x6f, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x00, 0x12, 0x52, 0x0a, 0x11, 0x4c, - 0x69, 0x73, 0x74, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, - 0x12, 0x1c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, - 0x6f, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, - 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, - 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, - 0x36, 0x0a, 0x06, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0x12, 0x2e, 0x74, 0x79, 0x70, 0x65, - 0x73, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x16, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x35, 0x0a, 0x05, 0x50, 0x72, 0x75, 0x6e, 0x65, - 0x12, 0x12, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x36, - 0x0a, 0x06, 0x46, 0x6f, 0x72, 0x67, 0x65, 0x74, 0x12, 0x12, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, - 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x16, 0x2e, 0x67, + 0x61, 0x73, 0x74, 0x4e, 0x22, 0x7e, 0x0a, 0x16, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x53, + 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, + 0x0a, 0x07, 0x70, 0x6c, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x70, 0x6c, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x6e, 0x61, 0x70, 0x73, + 0x68, 0x6f, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x6e, + 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x16, 0x0a, 0x06, + 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x22, 0x68, 0x0a, 0x18, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x6e, 0x61, 0x70, + 0x73, 0x68, 0x6f, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x17, 0x0a, 0x07, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x72, 0x65, 0x70, 0x6f, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x6e, 0x61, + 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, + 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, + 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x56, + 0x0a, 0x19, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x46, 0x69, + 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, + 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, + 0x25, 0x0a, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x0b, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x65, + 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x22, 0xd3, 0x01, 0x0a, 0x07, 0x4c, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, + 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x10, + 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x75, 0x69, 0x64, + 0x12, 0x10, 0x0a, 0x03, 0x67, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x67, + 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x74, + 0x69, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6d, 0x74, 0x69, 0x6d, 0x65, + 0x12, 0x14, 0x0a, 0x05, 0x61, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x61, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x74, 0x69, 0x6d, 0x65, 0x18, + 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x74, 0x69, 0x6d, 0x65, 0x32, 0xb4, 0x07, 0x0a, + 0x08, 0x42, 0x61, 0x63, 0x6b, 0x72, 0x65, 0x73, 0x74, 0x12, 0x31, 0x0a, 0x09, 0x47, 0x65, 0x74, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0a, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x00, 0x12, 0x25, 0x0a, 0x09, + 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x22, 0x00, 0x12, 0x21, 0x0a, 0x07, 0x41, 0x64, 0x64, 0x52, 0x65, 0x70, 0x6f, 0x12, 0x08, + 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x1a, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x22, 0x00, 0x12, 0x44, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4f, 0x70, 0x65, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, - 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x3f, 0x0a, 0x07, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, - 0x65, 0x12, 0x1a, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x53, 0x6e, - 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x36, 0x0a, 0x06, 0x55, 0x6e, 0x6c, 0x6f, 0x63, - 0x6b, 0x12, 0x12, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, - 0x35, 0x0a, 0x06, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x12, 0x11, 0x2e, 0x74, 0x79, 0x70, 0x65, - 0x73, 0x2e, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x16, 0x2e, 0x67, + 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x12, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x00, 0x30, 0x01, 0x12, 0x3e, 0x0a, 0x0d, + 0x47, 0x65, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x18, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x00, 0x12, 0x43, 0x0a, 0x0d, + 0x4c, 0x69, 0x73, 0x74, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x73, 0x12, 0x18, 0x2e, + 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, + 0x74, 0x69, 0x63, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x22, + 0x00, 0x12, 0x52, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, + 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x1c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x6e, + 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x36, 0x0a, 0x06, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, + 0x12, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x35, 0x0a, + 0x05, 0x50, 0x72, 0x75, 0x6e, 0x65, 0x12, 0x12, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x53, + 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, + 0x74, 0x79, 0x22, 0x00, 0x12, 0x36, 0x0a, 0x06, 0x46, 0x6f, 0x72, 0x67, 0x65, 0x74, 0x12, 0x12, + 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x3f, 0x0a, 0x07, + 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x1a, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, + 0x74, 0x6f, 0x72, 0x65, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x36, 0x0a, + 0x06, 0x55, 0x6e, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x12, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, + 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x16, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, + 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x35, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x12, + 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x35, 0x0a, 0x06, + 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x12, 0x11, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x49, + 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, + 0x79, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x0c, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x48, 0x69, 0x73, 0x74, + 0x6f, 0x72, 0x79, 0x12, 0x17, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x48, 0x69, + 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, - 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x0c, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x48, - 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x17, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x65, 0x61, - 0x72, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x3b, 0x0a, 0x10, 0x50, 0x61, 0x74, - 0x68, 0x41, 0x75, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x12, 0x2e, - 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x1a, 0x11, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, - 0x4c, 0x69, 0x73, 0x74, 0x22, 0x00, 0x42, 0x2c, 0x5a, 0x2a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x61, 0x72, 0x65, 0x74, 0x68, 0x67, 0x65, 0x6f, 0x72, 0x67, - 0x65, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x72, 0x65, 0x73, 0x74, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, - 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x3b, 0x0a, 0x10, 0x50, 0x61, 0x74, 0x68, 0x41, 0x75, + 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x12, 0x2e, 0x74, 0x79, 0x70, + 0x65, 0x73, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x11, + 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x69, 0x73, + 0x74, 0x22, 0x00, 0x42, 0x2c, 0x5a, 0x2a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x67, 0x61, 0x72, 0x65, 0x74, 0x68, 0x67, 0x65, 0x6f, 0x72, 0x67, 0x65, 0x2f, 0x62, + 0x61, 0x63, 0x6b, 0x72, 0x65, 0x73, 0x74, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x76, + 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -711,26 +705,28 @@ var file_v1_service_proto_depIdxs = []int32{ 10, // 10: v1.Backrest.Forget:input_type -> types.StringValue 3, // 11: v1.Backrest.Restore:input_type -> v1.RestoreSnapshotRequest 10, // 12: v1.Backrest.Unlock:input_type -> types.StringValue - 11, // 13: v1.Backrest.Cancel:input_type -> types.Int64Value - 0, // 14: v1.Backrest.ClearHistory:input_type -> v1.ClearHistoryRequest - 10, // 15: v1.Backrest.PathAutocomplete:input_type -> types.StringValue - 8, // 16: v1.Backrest.GetConfig:output_type -> v1.Config - 8, // 17: v1.Backrest.SetConfig:output_type -> v1.Config - 8, // 18: v1.Backrest.AddRepo:output_type -> v1.Config - 12, // 19: v1.Backrest.GetOperationEvents:output_type -> v1.OperationEvent - 13, // 20: v1.Backrest.GetOperations:output_type -> v1.OperationList - 14, // 21: v1.Backrest.ListSnapshots:output_type -> v1.ResticSnapshotList - 5, // 22: v1.Backrest.ListSnapshotFiles:output_type -> v1.ListSnapshotFilesResponse - 7, // 23: v1.Backrest.Backup:output_type -> google.protobuf.Empty - 7, // 24: v1.Backrest.Prune:output_type -> google.protobuf.Empty - 7, // 25: v1.Backrest.Forget:output_type -> google.protobuf.Empty - 7, // 26: v1.Backrest.Restore:output_type -> google.protobuf.Empty - 7, // 27: v1.Backrest.Unlock:output_type -> google.protobuf.Empty - 7, // 28: v1.Backrest.Cancel:output_type -> google.protobuf.Empty - 7, // 29: v1.Backrest.ClearHistory:output_type -> google.protobuf.Empty - 15, // 30: v1.Backrest.PathAutocomplete:output_type -> types.StringList - 16, // [16:31] is the sub-list for method output_type - 1, // [1:16] is the sub-list for method input_type + 10, // 13: v1.Backrest.Stats:input_type -> types.StringValue + 11, // 14: v1.Backrest.Cancel:input_type -> types.Int64Value + 0, // 15: v1.Backrest.ClearHistory:input_type -> v1.ClearHistoryRequest + 10, // 16: v1.Backrest.PathAutocomplete:input_type -> types.StringValue + 8, // 17: v1.Backrest.GetConfig:output_type -> v1.Config + 8, // 18: v1.Backrest.SetConfig:output_type -> v1.Config + 8, // 19: v1.Backrest.AddRepo:output_type -> v1.Config + 12, // 20: v1.Backrest.GetOperationEvents:output_type -> v1.OperationEvent + 13, // 21: v1.Backrest.GetOperations:output_type -> v1.OperationList + 14, // 22: v1.Backrest.ListSnapshots:output_type -> v1.ResticSnapshotList + 5, // 23: v1.Backrest.ListSnapshotFiles:output_type -> v1.ListSnapshotFilesResponse + 7, // 24: v1.Backrest.Backup:output_type -> google.protobuf.Empty + 7, // 25: v1.Backrest.Prune:output_type -> google.protobuf.Empty + 7, // 26: v1.Backrest.Forget:output_type -> google.protobuf.Empty + 7, // 27: v1.Backrest.Restore:output_type -> google.protobuf.Empty + 7, // 28: v1.Backrest.Unlock:output_type -> google.protobuf.Empty + 7, // 29: v1.Backrest.Stats:output_type -> google.protobuf.Empty + 7, // 30: v1.Backrest.Cancel:output_type -> google.protobuf.Empty + 7, // 31: v1.Backrest.ClearHistory:output_type -> google.protobuf.Empty + 15, // 32: v1.Backrest.PathAutocomplete:output_type -> types.StringList + 17, // [17:33] is the sub-list for method output_type + 1, // [1:17] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name diff --git a/gen/go/v1/service_grpc.pb.go b/gen/go/v1/service_grpc.pb.go index 63f4710f..0e37654c 100644 --- a/gen/go/v1/service_grpc.pb.go +++ b/gen/go/v1/service_grpc.pb.go @@ -33,6 +33,7 @@ const ( Backrest_Forget_FullMethodName = "/v1.Backrest/Forget" Backrest_Restore_FullMethodName = "/v1.Backrest/Restore" Backrest_Unlock_FullMethodName = "/v1.Backrest/Unlock" + Backrest_Stats_FullMethodName = "/v1.Backrest/Stats" Backrest_Cancel_FullMethodName = "/v1.Backrest/Cancel" Backrest_ClearHistory_FullMethodName = "/v1.Backrest/ClearHistory" Backrest_PathAutocomplete_FullMethodName = "/v1.Backrest/PathAutocomplete" @@ -59,8 +60,11 @@ type BackrestClient interface { Restore(ctx context.Context, in *RestoreSnapshotRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) // Unlock synchronously attempts to unlock the repo. Will block if other operations are in progress. Unlock(ctx context.Context, in *types.StringValue, opts ...grpc.CallOption) (*emptypb.Empty, error) + // Stats runs 'restic stats` on the repository and appends the results to the operations log. + Stats(ctx context.Context, in *types.StringValue, opts ...grpc.CallOption) (*emptypb.Empty, error) // Cancel attempts to cancel a task with the given operation ID. Not guaranteed to succeed. Cancel(ctx context.Context, in *types.Int64Value, opts ...grpc.CallOption) (*emptypb.Empty, error) + // Clears the history of operations ClearHistory(ctx context.Context, in *ClearHistoryRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) // PathAutocomplete provides path autocompletion options for a given filesystem path. PathAutocomplete(ctx context.Context, in *types.StringValue, opts ...grpc.CallOption) (*types.StringList, error) @@ -205,6 +209,15 @@ func (c *backrestClient) Unlock(ctx context.Context, in *types.StringValue, opts return out, nil } +func (c *backrestClient) Stats(ctx context.Context, in *types.StringValue, opts ...grpc.CallOption) (*emptypb.Empty, error) { + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, Backrest_Stats_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *backrestClient) Cancel(ctx context.Context, in *types.Int64Value, opts ...grpc.CallOption) (*emptypb.Empty, error) { out := new(emptypb.Empty) err := c.cc.Invoke(ctx, Backrest_Cancel_FullMethodName, in, out, opts...) @@ -253,8 +266,11 @@ type BackrestServer interface { Restore(context.Context, *RestoreSnapshotRequest) (*emptypb.Empty, error) // Unlock synchronously attempts to unlock the repo. Will block if other operations are in progress. Unlock(context.Context, *types.StringValue) (*emptypb.Empty, error) + // Stats runs 'restic stats` on the repository and appends the results to the operations log. + Stats(context.Context, *types.StringValue) (*emptypb.Empty, error) // Cancel attempts to cancel a task with the given operation ID. Not guaranteed to succeed. Cancel(context.Context, *types.Int64Value) (*emptypb.Empty, error) + // Clears the history of operations ClearHistory(context.Context, *ClearHistoryRequest) (*emptypb.Empty, error) // PathAutocomplete provides path autocompletion options for a given filesystem path. PathAutocomplete(context.Context, *types.StringValue) (*types.StringList, error) @@ -301,6 +317,9 @@ func (UnimplementedBackrestServer) Restore(context.Context, *RestoreSnapshotRequ func (UnimplementedBackrestServer) Unlock(context.Context, *types.StringValue) (*emptypb.Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method Unlock not implemented") } +func (UnimplementedBackrestServer) Stats(context.Context, *types.StringValue) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method Stats not implemented") +} func (UnimplementedBackrestServer) Cancel(context.Context, *types.Int64Value) (*emptypb.Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method Cancel not implemented") } @@ -542,6 +561,24 @@ func _Backrest_Unlock_Handler(srv interface{}, ctx context.Context, dec func(int return interceptor(ctx, in, info, handler) } +func _Backrest_Stats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(types.StringValue) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BackrestServer).Stats(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Backrest_Stats_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BackrestServer).Stats(ctx, req.(*types.StringValue)) + } + return interceptor(ctx, in, info, handler) +} + func _Backrest_Cancel_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(types.Int64Value) if err := dec(in); err != nil { @@ -647,6 +684,10 @@ var Backrest_ServiceDesc = grpc.ServiceDesc{ MethodName: "Unlock", Handler: _Backrest_Unlock_Handler, }, + { + MethodName: "Stats", + Handler: _Backrest_Stats_Handler, + }, { MethodName: "Cancel", Handler: _Backrest_Cancel_Handler, diff --git a/gen/go/v1/v1connect/service.connect.go b/gen/go/v1/v1connect/service.connect.go index 25ba08e0..63a7570b 100644 --- a/gen/go/v1/v1connect/service.connect.go +++ b/gen/go/v1/v1connect/service.connect.go @@ -61,6 +61,8 @@ const ( BackrestRestoreProcedure = "/v1.Backrest/Restore" // BackrestUnlockProcedure is the fully-qualified name of the Backrest's Unlock RPC. BackrestUnlockProcedure = "/v1.Backrest/Unlock" + // BackrestStatsProcedure is the fully-qualified name of the Backrest's Stats RPC. + BackrestStatsProcedure = "/v1.Backrest/Stats" // BackrestCancelProcedure is the fully-qualified name of the Backrest's Cancel RPC. BackrestCancelProcedure = "/v1.Backrest/Cancel" // BackrestClearHistoryProcedure is the fully-qualified name of the Backrest's ClearHistory RPC. @@ -85,6 +87,7 @@ var ( backrestForgetMethodDescriptor = backrestServiceDescriptor.Methods().ByName("Forget") backrestRestoreMethodDescriptor = backrestServiceDescriptor.Methods().ByName("Restore") backrestUnlockMethodDescriptor = backrestServiceDescriptor.Methods().ByName("Unlock") + backrestStatsMethodDescriptor = backrestServiceDescriptor.Methods().ByName("Stats") backrestCancelMethodDescriptor = backrestServiceDescriptor.Methods().ByName("Cancel") backrestClearHistoryMethodDescriptor = backrestServiceDescriptor.Methods().ByName("ClearHistory") backrestPathAutocompleteMethodDescriptor = backrestServiceDescriptor.Methods().ByName("PathAutocomplete") @@ -109,8 +112,11 @@ type BackrestClient interface { Restore(context.Context, *connect.Request[v1.RestoreSnapshotRequest]) (*connect.Response[emptypb.Empty], error) // Unlock synchronously attempts to unlock the repo. Will block if other operations are in progress. Unlock(context.Context, *connect.Request[types.StringValue]) (*connect.Response[emptypb.Empty], error) + // Stats runs 'restic stats` on the repository and appends the results to the operations log. + Stats(context.Context, *connect.Request[types.StringValue]) (*connect.Response[emptypb.Empty], error) // Cancel attempts to cancel a task with the given operation ID. Not guaranteed to succeed. Cancel(context.Context, *connect.Request[types.Int64Value]) (*connect.Response[emptypb.Empty], error) + // Clears the history of operations ClearHistory(context.Context, *connect.Request[v1.ClearHistoryRequest]) (*connect.Response[emptypb.Empty], error) // PathAutocomplete provides path autocompletion options for a given filesystem path. PathAutocomplete(context.Context, *connect.Request[types.StringValue]) (*connect.Response[types.StringList], error) @@ -198,6 +204,12 @@ func NewBackrestClient(httpClient connect.HTTPClient, baseURL string, opts ...co connect.WithSchema(backrestUnlockMethodDescriptor), connect.WithClientOptions(opts...), ), + stats: connect.NewClient[types.StringValue, emptypb.Empty]( + httpClient, + baseURL+BackrestStatsProcedure, + connect.WithSchema(backrestStatsMethodDescriptor), + connect.WithClientOptions(opts...), + ), cancel: connect.NewClient[types.Int64Value, emptypb.Empty]( httpClient, baseURL+BackrestCancelProcedure, @@ -233,6 +245,7 @@ type backrestClient struct { forget *connect.Client[types.StringValue, emptypb.Empty] restore *connect.Client[v1.RestoreSnapshotRequest, emptypb.Empty] unlock *connect.Client[types.StringValue, emptypb.Empty] + stats *connect.Client[types.StringValue, emptypb.Empty] cancel *connect.Client[types.Int64Value, emptypb.Empty] clearHistory *connect.Client[v1.ClearHistoryRequest, emptypb.Empty] pathAutocomplete *connect.Client[types.StringValue, types.StringList] @@ -298,6 +311,11 @@ func (c *backrestClient) Unlock(ctx context.Context, req *connect.Request[types. return c.unlock.CallUnary(ctx, req) } +// Stats calls v1.Backrest.Stats. +func (c *backrestClient) Stats(ctx context.Context, req *connect.Request[types.StringValue]) (*connect.Response[emptypb.Empty], error) { + return c.stats.CallUnary(ctx, req) +} + // Cancel calls v1.Backrest.Cancel. func (c *backrestClient) Cancel(ctx context.Context, req *connect.Request[types.Int64Value]) (*connect.Response[emptypb.Empty], error) { return c.cancel.CallUnary(ctx, req) @@ -332,8 +350,11 @@ type BackrestHandler interface { Restore(context.Context, *connect.Request[v1.RestoreSnapshotRequest]) (*connect.Response[emptypb.Empty], error) // Unlock synchronously attempts to unlock the repo. Will block if other operations are in progress. Unlock(context.Context, *connect.Request[types.StringValue]) (*connect.Response[emptypb.Empty], error) + // Stats runs 'restic stats` on the repository and appends the results to the operations log. + Stats(context.Context, *connect.Request[types.StringValue]) (*connect.Response[emptypb.Empty], error) // Cancel attempts to cancel a task with the given operation ID. Not guaranteed to succeed. Cancel(context.Context, *connect.Request[types.Int64Value]) (*connect.Response[emptypb.Empty], error) + // Clears the history of operations ClearHistory(context.Context, *connect.Request[v1.ClearHistoryRequest]) (*connect.Response[emptypb.Empty], error) // PathAutocomplete provides path autocompletion options for a given filesystem path. PathAutocomplete(context.Context, *connect.Request[types.StringValue]) (*connect.Response[types.StringList], error) @@ -417,6 +438,12 @@ func NewBackrestHandler(svc BackrestHandler, opts ...connect.HandlerOption) (str connect.WithSchema(backrestUnlockMethodDescriptor), connect.WithHandlerOptions(opts...), ) + backrestStatsHandler := connect.NewUnaryHandler( + BackrestStatsProcedure, + svc.Stats, + connect.WithSchema(backrestStatsMethodDescriptor), + connect.WithHandlerOptions(opts...), + ) backrestCancelHandler := connect.NewUnaryHandler( BackrestCancelProcedure, svc.Cancel, @@ -461,6 +488,8 @@ func NewBackrestHandler(svc BackrestHandler, opts ...connect.HandlerOption) (str backrestRestoreHandler.ServeHTTP(w, r) case BackrestUnlockProcedure: backrestUnlockHandler.ServeHTTP(w, r) + case BackrestStatsProcedure: + backrestStatsHandler.ServeHTTP(w, r) case BackrestCancelProcedure: backrestCancelHandler.ServeHTTP(w, r) case BackrestClearHistoryProcedure: @@ -524,6 +553,10 @@ func (UnimplementedBackrestHandler) Unlock(context.Context, *connect.Request[typ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("v1.Backrest.Unlock is not implemented")) } +func (UnimplementedBackrestHandler) Stats(context.Context, *connect.Request[types.StringValue]) (*connect.Response[emptypb.Empty], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("v1.Backrest.Stats is not implemented")) +} + func (UnimplementedBackrestHandler) Cancel(context.Context, *connect.Request[types.Int64Value]) (*connect.Response[emptypb.Empty], error) { return nil, connect.NewError(connect.CodeUnimplemented, errors.New("v1.Backrest.Cancel is not implemented")) } diff --git a/internal/api/server.go b/internal/api/server.go index 85a85d94..a908975b 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -267,7 +267,7 @@ func (s *Server) Forget(ctx context.Context, req *connect.Request[types.StringVa at := time.Now() s.orchestrator.ScheduleTask(orchestrator.NewOneofForgetTask(s.orchestrator, plan, "", at), orchestrator.TaskPriorityInteractive+orchestrator.TaskPriorityForget) - s.orchestrator.ScheduleTask(orchestrator.NewOneofForgetTask(s.orchestrator, plan, "", at), orchestrator.TaskPriorityInteractive+orchestrator.TaskPriorityIndexSnapshots) + s.orchestrator.ScheduleTask(orchestrator.NewOneofIndexSnapshotsTask(s.orchestrator, plan, at), orchestrator.TaskPriorityInteractive+orchestrator.TaskPriorityIndexSnapshots) return connect.NewResponse(&emptypb.Empty{}), nil } @@ -285,9 +285,9 @@ func (s *Server) Prune(ctx context.Context, req *connect.Request[types.StringVal } func (s *Server) Restore(ctx context.Context, req *connect.Request[v1.RestoreSnapshotRequest]) (*connect.Response[emptypb.Empty], error) { - _, err := s.orchestrator.GetRepo(req.Msg.RepoId) + plan, err := s.orchestrator.GetPlan(req.Msg.PlanId) if err != nil { - return nil, fmt.Errorf("failed to get repo %q: %w", req.Msg.RepoId, err) + return nil, fmt.Errorf("failed to get plan %q: %w", req.Msg.PlanId, err) } if req.Msg.Target == "" { @@ -303,8 +303,7 @@ func (s *Server) Restore(ctx context.Context, req *connect.Request[v1.RestoreSna at := time.Now() s.orchestrator.ScheduleTask(orchestrator.NewOneofRestoreTask(s.orchestrator, orchestrator.RestoreTaskOpts{ - PlanId: req.Msg.PlanId, - RepoId: req.Msg.RepoId, + Plan: plan, SnapshotId: req.Msg.SnapshotId, Path: req.Msg.Path, Target: target, diff --git a/internal/orchestrator/orchestrator.go b/internal/orchestrator/orchestrator.go index 88faa2a4..93b25af9 100644 --- a/internal/orchestrator/orchestrator.go +++ b/internal/orchestrator/orchestrator.go @@ -22,11 +22,12 @@ var ErrRepoInitializationFailed = errors.New("repo initialization failed") var ErrPlanNotFound = errors.New("plan not found") const ( - TaskPriorityDefault = iota - TaskPriorityIndexSnapshots - TaskPriorityPrune - TaskPriorityForget - TaskPriorityInteractive // highest priority (add other priorities to this value for offsets) + TaskPriorityDefault = 0 + TaskPriorityInteractive = 10 + TaskPriorityIndexSnapshots = 101 + TaskPriorityForget = 102 + TaskPriorityPrune = 103 + TaskPriorityStats = 104 ) // Orchestrator is responsible for managing repos and backups. diff --git a/internal/orchestrator/repo.go b/internal/orchestrator/repo.go index b883f712..9f65b2b4 100644 --- a/internal/orchestrator/repo.go +++ b/internal/orchestrator/repo.go @@ -195,6 +195,19 @@ func (r *RepoOrchestrator) Unlock(ctx context.Context) error { return nil } +func (r *RepoOrchestrator) Stats(ctx context.Context) (*v1.RepoStats, error) { + r.mu.Lock() + defer r.mu.Unlock() + + r.l.Debug("Get Stats") + stats, err := r.repo.Stats(ctx) + if err != nil { + return nil, fmt.Errorf("stats for repo %v: %w", r.repoConfig.Id, err) + } + + return protoutil.RepoStatsToProto(stats), nil +} + func tagForPlan(plan *v1.Plan) string { return fmt.Sprintf("plan:%s", plan.Id) } diff --git a/internal/orchestrator/taskbackup.go b/internal/orchestrator/taskbackup.go index a4a9c820..aa99575b 100644 --- a/internal/orchestrator/taskbackup.go +++ b/internal/orchestrator/taskbackup.go @@ -146,6 +146,7 @@ func backupHelper(ctx context.Context, orchestrator *Orchestrator, plan *v1.Plan } orchestrator.ScheduleTask(NewOneofIndexSnapshotsTask(orchestrator, plan, at), TaskPriorityIndexSnapshots) + orchestrator.ScheduleTask(NewOneofStatsTask(orchestrator, plan, op.SnapshotId, at), TaskPriorityStats) return nil } diff --git a/internal/orchestrator/taskrestore.go b/internal/orchestrator/taskrestore.go index 19cc6815..540d11a0 100644 --- a/internal/orchestrator/taskrestore.go +++ b/internal/orchestrator/taskrestore.go @@ -10,8 +10,7 @@ import ( ) type RestoreTaskOpts struct { - PlanId string - RepoId string + Plan *v1.Plan SnapshotId string Path string Target string @@ -37,7 +36,7 @@ func NewOneofRestoreTask(orchestrator *Orchestrator, opts RestoreTaskOpts, at ti } func (t *RestoreTask) Name() string { - return fmt.Sprintf("restore snapshot %v in repo %v", t.restoreOpts.SnapshotId, t.restoreOpts.RepoId) + return fmt.Sprintf("restore snapshot %v in repo %v", t.restoreOpts.SnapshotId, t.restoreOpts.Plan.Repo) } func (t *RestoreTask) Next(now time.Time) *time.Time { @@ -45,8 +44,8 @@ func (t *RestoreTask) Next(now time.Time) *time.Time { if ret != nil { t.at = nil if err := t.setOperation(&v1.Operation{ - PlanId: t.restoreOpts.PlanId, - RepoId: t.restoreOpts.RepoId, + PlanId: t.restoreOpts.Plan.Id, + RepoId: t.restoreOpts.Plan.Repo, SnapshotId: t.restoreOpts.SnapshotId, UnixTimeStartMs: timeToUnixMillis(*ret), Status: v1.OperationStatus_STATUS_PENDING, @@ -70,9 +69,9 @@ func (t *RestoreTask) Run(ctx context.Context) error { op.Op = forgetOp op.UnixTimeStartMs = curTimeMillis() - repo, err := t.orch.GetRepo(t.restoreOpts.RepoId) + repo, err := t.orch.GetRepo(t.restoreOpts.Plan.Repo) if err != nil { - return fmt.Errorf("couldn't get repo %q: %w", t.restoreOpts.RepoId, err) + return fmt.Errorf("couldn't get repo %q: %w", t.restoreOpts.Plan.Repo, err) } lastSent := time.Now() // debounce progress updates, these can endup being very frequent. diff --git a/internal/orchestrator/taskstats.go b/internal/orchestrator/taskstats.go new file mode 100644 index 00000000..619ddf33 --- /dev/null +++ b/internal/orchestrator/taskstats.go @@ -0,0 +1,81 @@ +package orchestrator + +import ( + "context" + "errors" + "fmt" + "time" + + v1 "github.com/garethgeorge/backrest/gen/go/v1" + "go.uber.org/zap" +) + +// StatsTask tracks a restic stats operation. +type StatsTask struct { + TaskWithOperation + plan *v1.Plan + linkSnapshot string // snapshot to link the task to (if any) + at *time.Time +} + +var _ Task = &ForgetTask{} + +func NewOneofStatsTask(orchestrator *Orchestrator, plan *v1.Plan, linkSnapshot string, at time.Time) *StatsTask { + return &StatsTask{ + TaskWithOperation: TaskWithOperation{ + orch: orchestrator, + }, + plan: plan, + at: &at, + linkSnapshot: linkSnapshot, + } +} + +func (t *StatsTask) Name() string { + return fmt.Sprintf("stats for plan %q", t.plan.Id) +} + +func (t *StatsTask) Next(now time.Time) *time.Time { + ret := t.at + if ret != nil { + t.at = nil + if err := t.setOperation(&v1.Operation{ + PlanId: t.plan.Id, + RepoId: t.plan.Repo, + SnapshotId: t.linkSnapshot, + UnixTimeStartMs: timeToUnixMillis(*ret), + Status: v1.OperationStatus_STATUS_PENDING, + Op: &v1.Operation_OperationStats{}, + }); err != nil { + zap.S().Errorf("task %v failed to add operation to oplog: %v", t.Name(), err) + return nil + } + } + return ret +} + +func (t *StatsTask) Run(ctx context.Context) error { + if t.plan.Retention == nil { + return errors.New("plan does not have a retention policy") + } + + return t.runWithOpAndContext(ctx, func(ctx context.Context, op *v1.Operation) error { + repo, err := t.orch.GetRepo(t.plan.Repo) + if err != nil { + return fmt.Errorf("get repo %q: %w", t.plan.Repo, err) + } + + stats, err := repo.Stats(ctx) + if err != nil { + return fmt.Errorf("get stats: %w", err) + } + + op.Op = &v1.Operation_OperationStats{ + OperationStats: &v1.OperationStats{ + Stats: stats, + }, + } + + return err + }) +} diff --git a/proto/v1/operations.proto b/proto/v1/operations.proto index bfb506ba..afc9a91e 100644 --- a/proto/v1/operations.proto +++ b/proto/v1/operations.proto @@ -34,6 +34,7 @@ message Operation { OperationForget operation_forget = 102; OperationPrune operation_prune = 103; OperationRestore operation_restore = 104; + OperationStats operation_stats = 105; } } @@ -88,4 +89,8 @@ message OperationRestore { string path = 1; // path in the snapshot to restore. string target = 2; // location to restore it to. RestoreProgressEntry status = 3; // status of the restore. +} + +message OperationStats { + RepoStats stats = 1; } \ No newline at end of file diff --git a/proto/v1/service.proto b/proto/v1/service.proto index b5b4baf0..b5dd52c2 100644 --- a/proto/v1/service.proto +++ b/proto/v1/service.proto @@ -41,6 +41,9 @@ service Backrest { // Unlock synchronously attempts to unlock the repo. Will block if other operations are in progress. rpc Unlock(types.StringValue) returns (google.protobuf.Empty) {} + // Stats runs 'restic stats` on the repository and appends the results to the operations log. + rpc Stats(types.StringValue) returns (google.protobuf.Empty) {} + // Cancel attempts to cancel a task with the given operation ID. Not guaranteed to succeed. rpc Cancel(types.Int64Value) returns (google.protobuf.Empty) {} @@ -71,11 +74,10 @@ message GetOperationsRequest { } message RestoreSnapshotRequest { - string repo_id = 1; - string plan_id = 2; - string snapshot_id = 3; - string path = 4; - string target = 5; + string plan_id = 1; + string snapshot_id = 2; + string path = 3; + string target = 4; } message ListSnapshotFilesRequest { diff --git a/webui/gen/ts/v1/operations_pb.ts b/webui/gen/ts/v1/operations_pb.ts index 5feadc9d..4d9876ab 100644 --- a/webui/gen/ts/v1/operations_pb.ts +++ b/webui/gen/ts/v1/operations_pb.ts @@ -5,7 +5,7 @@ import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf"; import { Message, proto3, protoInt64 } from "@bufbuild/protobuf"; -import { BackupProgressEntry, ResticSnapshot, RestoreProgressEntry } from "./restic_pb.js"; +import { BackupProgressEntry, RepoStats, ResticSnapshot, RestoreProgressEntry } from "./restic_pb.js"; import { RetentionPolicy } from "./config_pb.js"; /** @@ -242,6 +242,12 @@ export class Operation extends Message { */ value: OperationRestore; case: "operationRestore"; + } | { + /** + * @generated from field: v1.OperationStats operation_stats = 105; + */ + value: OperationStats; + case: "operationStats"; } | { case: undefined; value?: undefined } = { case: undefined }; constructor(data?: PartialMessage) { @@ -265,6 +271,7 @@ export class Operation extends Message { { no: 102, name: "operation_forget", kind: "message", T: OperationForget, oneof: "op" }, { no: 103, name: "operation_prune", kind: "message", T: OperationPrune, oneof: "op" }, { no: 104, name: "operation_restore", kind: "message", T: OperationRestore, oneof: "op" }, + { no: 105, name: "operation_stats", kind: "message", T: OperationStats, oneof: "op" }, ]); static fromBinary(bytes: Uint8Array, options?: Partial): Operation { @@ -562,3 +569,40 @@ export class OperationRestore extends Message { } } +/** + * @generated from message v1.OperationStats + */ +export class OperationStats extends Message { + /** + * @generated from field: v1.RepoStats stats = 1; + */ + stats?: RepoStats; + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "v1.OperationStats"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "stats", kind: "message", T: RepoStats }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): OperationStats { + return new OperationStats().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): OperationStats { + return new OperationStats().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): OperationStats { + return new OperationStats().fromJsonString(jsonString, options); + } + + static equals(a: OperationStats | PlainMessage | undefined, b: OperationStats | PlainMessage | undefined): boolean { + return proto3.util.equals(OperationStats, a, b); + } +} + diff --git a/webui/gen/ts/v1/service_connect.ts b/webui/gen/ts/v1/service_connect.ts index 8fc65d6f..fffbd6c6 100644 --- a/webui/gen/ts/v1/service_connect.ts +++ b/webui/gen/ts/v1/service_connect.ts @@ -134,6 +134,17 @@ export const Backrest = { O: Empty, kind: MethodKind.Unary, }, + /** + * Stats runs 'restic stats` on the repository and appends the results to the operations log. + * + * @generated from rpc v1.Backrest.Stats + */ + stats: { + name: "Stats", + I: StringValue, + O: Empty, + kind: MethodKind.Unary, + }, /** * Cancel attempts to cancel a task with the given operation ID. Not guaranteed to succeed. * @@ -146,6 +157,8 @@ export const Backrest = { kind: MethodKind.Unary, }, /** + * Clears the history of operations + * * @generated from rpc v1.Backrest.ClearHistory */ clearHistory: { diff --git a/webui/gen/ts/v1/service_pb.ts b/webui/gen/ts/v1/service_pb.ts index 83f2e20f..e2050cb9 100644 --- a/webui/gen/ts/v1/service_pb.ts +++ b/webui/gen/ts/v1/service_pb.ts @@ -166,27 +166,22 @@ export class GetOperationsRequest extends Message { */ export class RestoreSnapshotRequest extends Message { /** - * @generated from field: string repo_id = 1; - */ - repoId = ""; - - /** - * @generated from field: string plan_id = 2; + * @generated from field: string plan_id = 1; */ planId = ""; /** - * @generated from field: string snapshot_id = 3; + * @generated from field: string snapshot_id = 2; */ snapshotId = ""; /** - * @generated from field: string path = 4; + * @generated from field: string path = 3; */ path = ""; /** - * @generated from field: string target = 5; + * @generated from field: string target = 4; */ target = ""; @@ -198,11 +193,10 @@ export class RestoreSnapshotRequest extends Message { static readonly runtime: typeof proto3 = proto3; static readonly typeName = "v1.RestoreSnapshotRequest"; static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "repo_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 2, name: "plan_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 3, name: "snapshot_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 4, name: "path", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 5, name: "target", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 1, name: "plan_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 2, name: "snapshot_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 3, name: "path", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 4, name: "target", kind: "scalar", T: 9 /* ScalarType.STRING */ }, ]); static fromBinary(bytes: Uint8Array, options?: Partial): RestoreSnapshotRequest { diff --git a/webui/src/components/OperationList.tsx b/webui/src/components/OperationList.tsx index b8c1e2b0..afa27df8 100644 --- a/webui/src/components/OperationList.tsx +++ b/webui/src/components/OperationList.tsx @@ -3,6 +3,7 @@ import { Operation, OperationEvent, OperationEventType, + OperationForget, OperationStatus, } from "../../gen/ts/v1/operations_pb"; import { @@ -31,6 +32,7 @@ import { displayTypeToString, getOperations, getTypeForDisplay, + shouldHideOperation, shouldHideStatus, subscribeToOperations, unsubscribeFromOperations, @@ -131,7 +133,7 @@ export const OperationList = ({ return ( {ops.map((op) => { - if (shouldHideStatus(op.status!)) { + if (shouldHideOperation(op)) { return null; } return @@ -244,57 +246,7 @@ export const OperationRow = ({ if (forgetOp.forget?.length === 0) { return null; } - - const policy = forgetOp.policy! || {}; - const policyDesc = []; - if (policy.keepLastN) { - policyDesc.push(`Keep Last ${policy.keepLastN} Snapshots`); - } - if (policy.keepHourly) { - policyDesc.push(`Keep Hourly for ${policy.keepHourly} Hours`); - } - if (policy.keepDaily) { - policyDesc.push(`Keep Daily for ${policy.keepDaily} Days`); - } - if (policy.keepWeekly) { - policyDesc.push(`Keep Weekly for ${policy.keepWeekly} Weeks`); - } - if (policy.keepMonthly) { - policyDesc.push(`Keep Monthly for ${policy.keepMonthly} Months`); - } - if (policy.keepYearly) { - policyDesc.push(`Keep Yearly for ${policy.keepYearly} Years`); - } - - body = ( - <> -

- - Removed snapshots: -
{forgetOp.forget?.map((f) => (
-                  
- {"removed snapshot " + normalizeSnapshotId(f.id!) + " taken at " + formatTime(Number(f.unixTimeMs))}
-
- ))}
- Policy: -
    - {policyDesc.map((desc, idx) => ( -
  • {desc}
  • - ))} -
- , - }, - ]} - /> - - ); + body = } else if (operation.op.case === "operationPrune") { const prune = operation.op.value; body = ( @@ -407,9 +359,6 @@ const BackupOperationStatus = ({ <>
- {st.currentFile && st.currentFile.length > 0 ? ( -
Current file: {st.currentFile.join("\n")}
- ) : null} Bytes Done/Total @@ -422,6 +371,9 @@ const BackupOperationStatus = ({ {Number(st.filesDone)}/{Number(st.totalFiles)} + {st.currentFile && st.currentFile.length > 0 ? ( +
Current file: {st.currentFile.join("\n")}
+ ) : null} ); } else if (status.entry.case === "summary") { @@ -473,3 +425,53 @@ const BackupOperationStatus = ({ return <>No fields set. This shouldn't happen; } }; + +const ForgetOperationDetails = ({ forgetOp }: { forgetOp: OperationForget }) => { + const policy = forgetOp.policy! || {}; + const policyDesc = []; + if (policy.keepLastN) { + policyDesc.push(`Keep Last ${policy.keepLastN} Snapshots`); + } + if (policy.keepHourly) { + policyDesc.push(`Keep Hourly for ${policy.keepHourly} Hours`); + } + if (policy.keepDaily) { + policyDesc.push(`Keep Daily for ${policy.keepDaily} Days`); + } + if (policy.keepWeekly) { + policyDesc.push(`Keep Weekly for ${policy.keepWeekly} Weeks`); + } + if (policy.keepMonthly) { + policyDesc.push(`Keep Monthly for ${policy.keepMonthly} Months`); + } + if (policy.keepYearly) { + policyDesc.push(`Keep Yearly for ${policy.keepYearly} Years`); + } + + return ( + + Removed snapshots: +
{forgetOp.forget?.map((f) => (
+              
+ {"removed snapshot " + normalizeSnapshotId(f.id!) + " taken at " + formatTime(Number(f.unixTimeMs))}
+
+ ))}
+ Policy: +
    + {policyDesc.map((desc, idx) => ( +
  • {desc}
  • + ))} +
+ , + }, + ]} + /> + ); +} \ No newline at end of file diff --git a/webui/src/components/OperationTree.tsx b/webui/src/components/OperationTree.tsx index 3febbde7..bf402889 100644 --- a/webui/src/components/OperationTree.tsx +++ b/webui/src/components/OperationTree.tsx @@ -112,7 +112,6 @@ export const OperationTree = ({ return ( -

Browse Backups

treeData={treeData} showIcon @@ -210,39 +209,6 @@ const buildTreePlan = (operations: BackupInfo[]): OpTreeNode[] => { return entries; }; -const buildTreeYear = (keyPrefix: string, operations: BackupInfo[]): OpTreeNode[] => { - const grouped = _.groupBy(operations, (op) => { - return localISOTime(op.displayTime).substring(0, 4); - }); - - const entries: OpTreeNode[] = _.map(grouped, (value, key) => { - return { - key: keyPrefix + key, - title: "" + key, - children: buildTreeDay(keyPrefix, value), - }; - }); - entries.sort(sortByKey); - return entries; -}; - -const buildTreeMonth = (keyPrefix: string, operations: BackupInfo[]): OpTreeNode[] => { - const grouped = _.groupBy(operations, (op) => { - return localISOTime(op.displayTime).substring(0, 7); - }); - const entries: OpTreeNode[] = _.map(grouped, (value, key) => { - return { - key: keyPrefix + key, - title: value[0].displayTime.toLocaleString("default", { - month: "long", - }), - children: buildTreeDay(keyPrefix, value), - }; - }); - entries.sort(sortByKey); - return entries; -}; - const buildTreeDay = (keyPrefix: string, operations: BackupInfo[]): OpTreeNode[] => { const grouped = _.groupBy(operations, (op) => { return localISOTime(op.displayTime).substring(0, 10); diff --git a/webui/src/components/SnapshotBrowser.tsx b/webui/src/components/SnapshotBrowser.tsx index f76b6553..9e37eaea 100644 --- a/webui/src/components/SnapshotBrowser.tsx +++ b/webui/src/components/SnapshotBrowser.tsx @@ -199,7 +199,6 @@ const FileNode = ({ entry }: { entry: LsEntry }) => { ); @@ -228,12 +227,10 @@ const FileNode = ({ entry }: { entry: LsEntry }) => { const RestoreModal = ({ planId, - repoId, snapshotId, path, }: { planId?: string; // optional: purely to link restore operations to the right plan. - repoId: string; snapshotId: string; path: string; }) => { @@ -260,7 +257,6 @@ const RestoreModal = ({ const values = await validateForm(form); await backrestService.restore({ planId, - repoId, snapshotId, path, target: values.target, @@ -281,9 +277,7 @@ const RestoreModal = ({ "Restore " + path + " from snapshot " + - normalizeSnapshotId(snapshotId) + - " in " + - repoId + normalizeSnapshotId(snapshotId) } width="40vw" footer={[ diff --git a/webui/src/state/oplog.ts b/webui/src/state/oplog.ts index 117929d9..10bcfbf0 100644 --- a/webui/src/state/oplog.ts +++ b/webui/src/state/oplog.ts @@ -102,6 +102,7 @@ export enum DisplayType { FORGET, PRUNE, RESTORE, + STATS, } export interface BackupInfo { @@ -152,7 +153,7 @@ export class BackupInfoCollector { // use the latest status that is not cancelled. let statusIdx = operations.length - 1; let status = OperationStatus.STATUS_SYSTEM_CANCELLED; - while (statusIdx !== -1 && shouldHideStatus(status)) { + while (statusIdx !== -1 && (shouldHideStatus(status) || status === OperationStatus.STATUS_PENDING)) { status = operations[statusIdx].status!; statusIdx--; } @@ -263,6 +264,10 @@ export class BackupInfoCollector { } } + +export const shouldHideOperation = (operation: Operation) => { + return operation.op.case === "operationStats" || shouldHideStatus(operation.status); +} export const shouldHideStatus = (status: OperationStatus) => { return status === OperationStatus.STATUS_SYSTEM_CANCELLED; }; @@ -279,6 +284,8 @@ export const getTypeForDisplay = (op: Operation) => { return DisplayType.PRUNE; case "operationRestore": return DisplayType.RESTORE; + case "operationStats": + return DisplayType.STATS; default: return DisplayType.UNKNOWN; } @@ -296,6 +303,8 @@ export const displayTypeToString = (type: DisplayType) => { return "Prune"; case DisplayType.RESTORE: return "Restore"; + case DisplayType.STATS: + return "Stats"; default: return "Unknown"; } diff --git a/webui/src/views/RepoView.tsx b/webui/src/views/RepoView.tsx index 31daf159..4fda2abc 100644 --- a/webui/src/views/RepoView.tsx +++ b/webui/src/views/RepoView.tsx @@ -1,6 +1,6 @@ -import React from "react"; +import React, { useEffect, useState } from "react"; import { Repo } from "../../gen/ts/v1/config_pb"; -import { Flex, Tabs, Typography } from "antd"; +import { Col, Empty, Flex, Row, Tabs, Typography } from "antd"; import { useRecoilValue } from "recoil"; import { configState } from "../state/config"; import { useAlertApi } from "../components/Alerts"; @@ -8,9 +8,30 @@ import { OperationList } from "../components/OperationList"; import { OperationTree } from "../components/OperationTree"; import { MAX_OPERATION_HISTORY } from "../constants"; import { GetOperationsRequest } from "../../gen/ts/v1/service_pb"; +import { getOperations } from "../state/oplog"; +import { RepoStats } from "../../gen/ts/v1/restic_pb"; +import { formatBytes } from "../lib/formatting"; export const RepoView = ({ repo }: React.PropsWithChildren<{ repo: Repo }>) => { const alertsApi = useAlertApi()!; + const [stats, setStats] = useState(null); + + useEffect(() => { + setStats(null); + getOperations(new GetOperationsRequest({ repoId: repo.id!, lastN: BigInt(10) })).then((operations) => { + for (const op of operations) { + if (op.op.case === "operationStats") { + const stats = op.op.value.stats; + if (stats) { + setStats(stats); + } + } + } + }).catch((e) => { + console.error(e); + }); + }, [repo.id]); + // Gracefully handle deletions by checking if the plan is still in the config. const config = useRecoilValue(configState); @@ -20,6 +41,62 @@ export const RepoView = ({ repo }: React.PropsWithChildren<{ repo: Repo }>) => { } repo = repoInConfig; + if (!stats) { + return ; + } + + const items = [ + { + key: "1", + label: "Stats", + children: ( + <> +

Repo Stats

+ + +

Total Size:

+

Total Size Uncompressed:

+

Blob Count:

+

Snapshot Count:

+

Compression Ratio:

+ + +

{formatBytes(Number(stats.totalSize))}

+

{formatBytes(Number(stats.totalUncompressedSize))}

+

{Number(stats.totalBlobCount)} blobs

+

{Number(stats.snapshotCount)} snapshots

+

{Math.round(stats.compressionRatio * 1000) / 1000}

+ +
+ + ), + }, + { + key: "2", + label: "Tree View", + children: ( + <> +

Browse Backups

+ + + ), + }, + { + key: "3", + label: "Operation List", + children: ( + <> +

Backup Action History

+ + + ), + }, + ] return ( <> @@ -28,33 +105,8 @@ export const RepoView = ({ repo }: React.PropsWithChildren<{ repo: Repo }>) => { - - - ), - }, - { - key: "2", - label: "Operation List", - children: ( - <> -

Backup Action History

- - - ), - }, - ]} + defaultActiveKey={items[0].key} + items={items} /> );