mirror of
https://github.com/garethgeorge/backrest.git
synced 2026-08-23 09:26:31 +00:00
feat: implement snapshot browsing
This commit is contained in:
+389
-77
@@ -141,6 +141,243 @@ func (x *GetOperationsRequest) GetLastN() int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
type ListSnapshotFilesRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
RepoId string `protobuf:"bytes,1,opt,name=repo_id,json=repoId,proto3" json:"repo_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"`
|
||||
}
|
||||
|
||||
func (x *ListSnapshotFilesRequest) Reset() {
|
||||
*x = ListSnapshotFilesRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_v1_service_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ListSnapshotFilesRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ListSnapshotFilesRequest) ProtoMessage() {}
|
||||
|
||||
func (x *ListSnapshotFilesRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_v1_service_proto_msgTypes[2]
|
||||
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 ListSnapshotFilesRequest.ProtoReflect.Descriptor instead.
|
||||
func (*ListSnapshotFilesRequest) Descriptor() ([]byte, []int) {
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *ListSnapshotFilesRequest) GetRepoId() string {
|
||||
if x != nil {
|
||||
return x.RepoId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ListSnapshotFilesRequest) GetSnapshotId() string {
|
||||
if x != nil {
|
||||
return x.SnapshotId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ListSnapshotFilesRequest) GetPath() string {
|
||||
if x != nil {
|
||||
return x.Path
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type ListSnapshotFilesResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"`
|
||||
Entries []*LsEntry `protobuf:"bytes,2,rep,name=entries,proto3" json:"entries,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ListSnapshotFilesResponse) Reset() {
|
||||
*x = ListSnapshotFilesResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_v1_service_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ListSnapshotFilesResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ListSnapshotFilesResponse) ProtoMessage() {}
|
||||
|
||||
func (x *ListSnapshotFilesResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_v1_service_proto_msgTypes[3]
|
||||
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 ListSnapshotFilesResponse.ProtoReflect.Descriptor instead.
|
||||
func (*ListSnapshotFilesResponse) Descriptor() ([]byte, []int) {
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *ListSnapshotFilesResponse) GetPath() string {
|
||||
if x != nil {
|
||||
return x.Path
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ListSnapshotFilesResponse) GetEntries() []*LsEntry {
|
||||
if x != nil {
|
||||
return x.Entries
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type LsEntry struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||
Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"`
|
||||
Path string `protobuf:"bytes,3,opt,name=path,proto3" json:"path,omitempty"`
|
||||
Uid int64 `protobuf:"varint,4,opt,name=uid,proto3" json:"uid,omitempty"`
|
||||
Gid int64 `protobuf:"varint,5,opt,name=gid,proto3" json:"gid,omitempty"`
|
||||
Size int64 `protobuf:"varint,6,opt,name=size,proto3" json:"size,omitempty"`
|
||||
Mode int64 `protobuf:"varint,7,opt,name=mode,proto3" json:"mode,omitempty"`
|
||||
Mtime string `protobuf:"bytes,8,opt,name=mtime,proto3" json:"mtime,omitempty"`
|
||||
Atime string `protobuf:"bytes,9,opt,name=atime,proto3" json:"atime,omitempty"`
|
||||
Ctime string `protobuf:"bytes,10,opt,name=ctime,proto3" json:"ctime,omitempty"`
|
||||
}
|
||||
|
||||
func (x *LsEntry) Reset() {
|
||||
*x = LsEntry{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_v1_service_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *LsEntry) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*LsEntry) ProtoMessage() {}
|
||||
|
||||
func (x *LsEntry) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_v1_service_proto_msgTypes[4]
|
||||
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 LsEntry.ProtoReflect.Descriptor instead.
|
||||
func (*LsEntry) Descriptor() ([]byte, []int) {
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
func (x *LsEntry) GetName() string {
|
||||
if x != nil {
|
||||
return x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *LsEntry) GetType() string {
|
||||
if x != nil {
|
||||
return x.Type
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *LsEntry) GetPath() string {
|
||||
if x != nil {
|
||||
return x.Path
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *LsEntry) GetUid() int64 {
|
||||
if x != nil {
|
||||
return x.Uid
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *LsEntry) GetGid() int64 {
|
||||
if x != nil {
|
||||
return x.Gid
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *LsEntry) GetSize() int64 {
|
||||
if x != nil {
|
||||
return x.Size
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *LsEntry) GetMode() int64 {
|
||||
if x != nil {
|
||||
return x.Mode
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *LsEntry) GetMtime() string {
|
||||
if x != nil {
|
||||
return x.Mtime
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *LsEntry) GetAtime() string {
|
||||
if x != nil {
|
||||
return x.Atime
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *LsEntry) GetCtime() string {
|
||||
if x != nil {
|
||||
return x.Ctime
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_v1_service_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_v1_service_proto_rawDesc = []byte{
|
||||
@@ -164,51 +401,84 @@ var file_v1_service_proto_rawDesc = []byte{
|
||||
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,
|
||||
0x15, 0x0a, 0x06, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52,
|
||||
0x05, 0x6c, 0x61, 0x73, 0x74, 0x4e, 0x32, 0x8f, 0x05, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x74, 0x69,
|
||||
0x63, 0x55, 0x49, 0x12, 0x43, 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, 0x12, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0c, 0x12, 0x0a, 0x2f, 0x76,
|
||||
0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3a, 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, 0x15, 0x82,
|
||||
0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x3a, 0x01, 0x2a, 0x22, 0x0a, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f,
|
||||
0x6e, 0x66, 0x69, 0x67, 0x12, 0x3b, 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, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x3a, 0x01, 0x2a,
|
||||
0x22, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x72, 0x65, 0x70,
|
||||
0x6f, 0x12, 0x61, 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, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31,
|
||||
0x2f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x73, 0x30, 0x01, 0x12, 0x57, 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, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x3a, 0x01, 0x2a, 0x22, 0x0e, 0x2f,
|
||||
0x76, 0x31, 0x2f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x5b, 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, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x3a, 0x01, 0x2a, 0x22, 0x0d, 0x2f, 0x76, 0x31,
|
||||
0x2f, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x73, 0x12, 0x4f, 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,
|
||||
0x05, 0x6c, 0x61, 0x73, 0x74, 0x4e, 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, 0x81,
|
||||
0x06, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x74, 0x69, 0x63, 0x55, 0x49, 0x12, 0x43, 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,
|
||||
0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x3a, 0x01, 0x2a, 0x22, 0x0e, 0x2f, 0x76, 0x31,
|
||||
0x2f, 0x63, 0x6d, 0x64, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0x5b, 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, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x3a, 0x01,
|
||||
0x2a, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x6c,
|
||||
0x65, 0x74, 0x65, 0x2f, 0x70, 0x61, 0x74, 0x68, 0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68,
|
||||
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x61, 0x72, 0x65, 0x74, 0x68, 0x67, 0x65, 0x6f,
|
||||
0x72, 0x67, 0x65, 0x2f, 0x72, 0x65, 0x73, 0x74, 0x69, 0x63, 0x75, 0x69, 0x2f, 0x67, 0x6f, 0x2f,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x1a, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x12, 0x82, 0xd3,
|
||||
0xe4, 0x93, 0x02, 0x0c, 0x12, 0x0a, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
||||
0x12, 0x3a, 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, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x3a, 0x01, 0x2a,
|
||||
0x22, 0x0a, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3b, 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, 0x1a, 0x82,
|
||||
0xd3, 0xe4, 0x93, 0x02, 0x14, 0x3a, 0x01, 0x2a, 0x22, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f,
|
||||
0x6e, 0x66, 0x69, 0x67, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x12, 0x61, 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, 0x1d, 0x82, 0xd3, 0xe4,
|
||||
0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2f,
|
||||
0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x30, 0x01, 0x12, 0x57, 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, 0x19, 0x82, 0xd3, 0xe4, 0x93,
|
||||
0x02, 0x13, 0x3a, 0x01, 0x2a, 0x22, 0x0e, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x70, 0x65, 0x72, 0x61,
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x5b, 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, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12,
|
||||
0x3a, 0x01, 0x2a, 0x22, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f,
|
||||
0x74, 0x73, 0x12, 0x70, 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, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x3a, 0x01, 0x2a, 0x22,
|
||||
0x13, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x73, 0x2f, 0x66,
|
||||
0x69, 0x6c, 0x65, 0x73, 0x12, 0x4f, 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, 0x19, 0x82, 0xd3, 0xe4, 0x93,
|
||||
0x02, 0x13, 0x3a, 0x01, 0x2a, 0x22, 0x0e, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6d, 0x64, 0x2f, 0x62,
|
||||
0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0x5b, 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, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x3a, 0x01, 0x2a, 0x22, 0x15, 0x2f, 0x76, 0x31,
|
||||
0x2f, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x2f, 0x70, 0x61,
|
||||
0x74, 0x68, 0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
|
||||
0x2f, 0x67, 0x61, 0x72, 0x65, 0x74, 0x68, 0x67, 0x65, 0x6f, 0x72, 0x67, 0x65, 0x2f, 0x72, 0x65,
|
||||
0x73, 0x74, 0x69, 0x63, 0x75, 0x69, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f,
|
||||
0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -223,41 +493,47 @@ func file_v1_service_proto_rawDescGZIP() []byte {
|
||||
return file_v1_service_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_v1_service_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||
var file_v1_service_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
|
||||
var file_v1_service_proto_goTypes = []interface{}{
|
||||
(*ListSnapshotsRequest)(nil), // 0: v1.ListSnapshotsRequest
|
||||
(*GetOperationsRequest)(nil), // 1: v1.GetOperationsRequest
|
||||
(*emptypb.Empty)(nil), // 2: google.protobuf.Empty
|
||||
(*Config)(nil), // 3: v1.Config
|
||||
(*Repo)(nil), // 4: v1.Repo
|
||||
(*types.StringValue)(nil), // 5: types.StringValue
|
||||
(*OperationEvent)(nil), // 6: v1.OperationEvent
|
||||
(*OperationList)(nil), // 7: v1.OperationList
|
||||
(*ResticSnapshotList)(nil), // 8: v1.ResticSnapshotList
|
||||
(*types.StringList)(nil), // 9: types.StringList
|
||||
(*ListSnapshotsRequest)(nil), // 0: v1.ListSnapshotsRequest
|
||||
(*GetOperationsRequest)(nil), // 1: v1.GetOperationsRequest
|
||||
(*ListSnapshotFilesRequest)(nil), // 2: v1.ListSnapshotFilesRequest
|
||||
(*ListSnapshotFilesResponse)(nil), // 3: v1.ListSnapshotFilesResponse
|
||||
(*LsEntry)(nil), // 4: v1.LsEntry
|
||||
(*emptypb.Empty)(nil), // 5: google.protobuf.Empty
|
||||
(*Config)(nil), // 6: v1.Config
|
||||
(*Repo)(nil), // 7: v1.Repo
|
||||
(*types.StringValue)(nil), // 8: types.StringValue
|
||||
(*OperationEvent)(nil), // 9: v1.OperationEvent
|
||||
(*OperationList)(nil), // 10: v1.OperationList
|
||||
(*ResticSnapshotList)(nil), // 11: v1.ResticSnapshotList
|
||||
(*types.StringList)(nil), // 12: types.StringList
|
||||
}
|
||||
var file_v1_service_proto_depIdxs = []int32{
|
||||
2, // 0: v1.ResticUI.GetConfig:input_type -> google.protobuf.Empty
|
||||
3, // 1: v1.ResticUI.SetConfig:input_type -> v1.Config
|
||||
4, // 2: v1.ResticUI.AddRepo:input_type -> v1.Repo
|
||||
2, // 3: v1.ResticUI.GetOperationEvents:input_type -> google.protobuf.Empty
|
||||
1, // 4: v1.ResticUI.GetOperations:input_type -> v1.GetOperationsRequest
|
||||
0, // 5: v1.ResticUI.ListSnapshots:input_type -> v1.ListSnapshotsRequest
|
||||
5, // 6: v1.ResticUI.Backup:input_type -> types.StringValue
|
||||
5, // 7: v1.ResticUI.PathAutocomplete:input_type -> types.StringValue
|
||||
3, // 8: v1.ResticUI.GetConfig:output_type -> v1.Config
|
||||
3, // 9: v1.ResticUI.SetConfig:output_type -> v1.Config
|
||||
3, // 10: v1.ResticUI.AddRepo:output_type -> v1.Config
|
||||
6, // 11: v1.ResticUI.GetOperationEvents:output_type -> v1.OperationEvent
|
||||
7, // 12: v1.ResticUI.GetOperations:output_type -> v1.OperationList
|
||||
8, // 13: v1.ResticUI.ListSnapshots:output_type -> v1.ResticSnapshotList
|
||||
2, // 14: v1.ResticUI.Backup:output_type -> google.protobuf.Empty
|
||||
9, // 15: v1.ResticUI.PathAutocomplete:output_type -> types.StringList
|
||||
8, // [8:16] is the sub-list for method output_type
|
||||
0, // [0:8] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
4, // 0: v1.ListSnapshotFilesResponse.entries:type_name -> v1.LsEntry
|
||||
5, // 1: v1.ResticUI.GetConfig:input_type -> google.protobuf.Empty
|
||||
6, // 2: v1.ResticUI.SetConfig:input_type -> v1.Config
|
||||
7, // 3: v1.ResticUI.AddRepo:input_type -> v1.Repo
|
||||
5, // 4: v1.ResticUI.GetOperationEvents:input_type -> google.protobuf.Empty
|
||||
1, // 5: v1.ResticUI.GetOperations:input_type -> v1.GetOperationsRequest
|
||||
0, // 6: v1.ResticUI.ListSnapshots:input_type -> v1.ListSnapshotsRequest
|
||||
2, // 7: v1.ResticUI.ListSnapshotFiles:input_type -> v1.ListSnapshotFilesRequest
|
||||
8, // 8: v1.ResticUI.Backup:input_type -> types.StringValue
|
||||
8, // 9: v1.ResticUI.PathAutocomplete:input_type -> types.StringValue
|
||||
6, // 10: v1.ResticUI.GetConfig:output_type -> v1.Config
|
||||
6, // 11: v1.ResticUI.SetConfig:output_type -> v1.Config
|
||||
6, // 12: v1.ResticUI.AddRepo:output_type -> v1.Config
|
||||
9, // 13: v1.ResticUI.GetOperationEvents:output_type -> v1.OperationEvent
|
||||
10, // 14: v1.ResticUI.GetOperations:output_type -> v1.OperationList
|
||||
11, // 15: v1.ResticUI.ListSnapshots:output_type -> v1.ResticSnapshotList
|
||||
3, // 16: v1.ResticUI.ListSnapshotFiles:output_type -> v1.ListSnapshotFilesResponse
|
||||
5, // 17: v1.ResticUI.Backup:output_type -> google.protobuf.Empty
|
||||
12, // 18: v1.ResticUI.PathAutocomplete:output_type -> types.StringList
|
||||
10, // [10:19] is the sub-list for method output_type
|
||||
1, // [1:10] 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
|
||||
}
|
||||
|
||||
func init() { file_v1_service_proto_init() }
|
||||
@@ -293,6 +569,42 @@ func file_v1_service_proto_init() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_v1_service_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ListSnapshotFilesRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_v1_service_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ListSnapshotFilesResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_v1_service_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*LsEntry); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
@@ -300,7 +612,7 @@ func file_v1_service_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_v1_service_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 2,
|
||||
NumMessages: 5,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
|
||||
@@ -204,6 +204,40 @@ func local_request_ResticUI_ListSnapshots_0(ctx context.Context, marshaler runti
|
||||
|
||||
}
|
||||
|
||||
func request_ResticUI_ListSnapshotFiles_0(ctx context.Context, marshaler runtime.Marshaler, client ResticUIClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq ListSnapshotFilesRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
newReader, berr := utilities.IOReaderFactory(req.Body)
|
||||
if berr != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
|
||||
}
|
||||
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
msg, err := client.ListSnapshotFiles(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func local_request_ResticUI_ListSnapshotFiles_0(ctx context.Context, marshaler runtime.Marshaler, server ResticUIServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq ListSnapshotFilesRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
newReader, berr := utilities.IOReaderFactory(req.Body)
|
||||
if berr != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
|
||||
}
|
||||
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
msg, err := server.ListSnapshotFiles(ctx, &protoReq)
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func request_ResticUI_Backup_0(ctx context.Context, marshaler runtime.Marshaler, client ResticUIClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq types.StringValue
|
||||
var metadata runtime.ServerMetadata
|
||||
@@ -410,6 +444,31 @@ func RegisterResticUIHandlerServer(ctx context.Context, mux *runtime.ServeMux, s
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("POST", pattern_ResticUI_ListSnapshotFiles_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
var stream runtime.ServerTransportStream
|
||||
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
var err error
|
||||
var annotatedContext context.Context
|
||||
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/v1.ResticUI/ListSnapshotFiles", runtime.WithHTTPPathPattern("/v1/snapshots/files"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := local_request_ResticUI_ListSnapshotFiles_0(annotatedContext, inboundMarshaler, server, req, pathParams)
|
||||
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_ResticUI_ListSnapshotFiles_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("POST", pattern_ResticUI_Backup_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
@@ -633,6 +692,28 @@ func RegisterResticUIHandlerClient(ctx context.Context, mux *runtime.ServeMux, c
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("POST", pattern_ResticUI_ListSnapshotFiles_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
var err error
|
||||
var annotatedContext context.Context
|
||||
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/v1.ResticUI/ListSnapshotFiles", runtime.WithHTTPPathPattern("/v1/snapshots/files"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_ResticUI_ListSnapshotFiles_0(annotatedContext, inboundMarshaler, client, req, pathParams)
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_ResticUI_ListSnapshotFiles_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("POST", pattern_ResticUI_Backup_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
@@ -693,6 +774,8 @@ var (
|
||||
|
||||
pattern_ResticUI_ListSnapshots_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "snapshots"}, ""))
|
||||
|
||||
pattern_ResticUI_ListSnapshotFiles_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "snapshots", "files"}, ""))
|
||||
|
||||
pattern_ResticUI_Backup_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "cmd", "backup"}, ""))
|
||||
|
||||
pattern_ResticUI_PathAutocomplete_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "autocomplete", "path"}, ""))
|
||||
@@ -711,6 +794,8 @@ var (
|
||||
|
||||
forward_ResticUI_ListSnapshots_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_ResticUI_ListSnapshotFiles_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_ResticUI_Backup_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_ResticUI_PathAutocomplete_0 = runtime.ForwardResponseMessage
|
||||
|
||||
@@ -27,6 +27,7 @@ const (
|
||||
ResticUI_GetOperationEvents_FullMethodName = "/v1.ResticUI/GetOperationEvents"
|
||||
ResticUI_GetOperations_FullMethodName = "/v1.ResticUI/GetOperations"
|
||||
ResticUI_ListSnapshots_FullMethodName = "/v1.ResticUI/ListSnapshots"
|
||||
ResticUI_ListSnapshotFiles_FullMethodName = "/v1.ResticUI/ListSnapshotFiles"
|
||||
ResticUI_Backup_FullMethodName = "/v1.ResticUI/Backup"
|
||||
ResticUI_PathAutocomplete_FullMethodName = "/v1.ResticUI/PathAutocomplete"
|
||||
)
|
||||
@@ -41,6 +42,7 @@ type ResticUIClient interface {
|
||||
GetOperationEvents(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (ResticUI_GetOperationEventsClient, error)
|
||||
GetOperations(ctx context.Context, in *GetOperationsRequest, opts ...grpc.CallOption) (*OperationList, error)
|
||||
ListSnapshots(ctx context.Context, in *ListSnapshotsRequest, opts ...grpc.CallOption) (*ResticSnapshotList, error)
|
||||
ListSnapshotFiles(ctx context.Context, in *ListSnapshotFilesRequest, opts ...grpc.CallOption) (*ListSnapshotFilesResponse, error)
|
||||
// Backup schedules a backup operation. It accepts a plan id and returns empty if the task is enqueued.
|
||||
Backup(ctx context.Context, in *types.StringValue, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||
// PathAutocomplete provides path autocompletion options for a given filesystem path.
|
||||
@@ -132,6 +134,15 @@ func (c *resticUIClient) ListSnapshots(ctx context.Context, in *ListSnapshotsReq
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *resticUIClient) ListSnapshotFiles(ctx context.Context, in *ListSnapshotFilesRequest, opts ...grpc.CallOption) (*ListSnapshotFilesResponse, error) {
|
||||
out := new(ListSnapshotFilesResponse)
|
||||
err := c.cc.Invoke(ctx, ResticUI_ListSnapshotFiles_FullMethodName, in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *resticUIClient) Backup(ctx context.Context, in *types.StringValue, opts ...grpc.CallOption) (*emptypb.Empty, error) {
|
||||
out := new(emptypb.Empty)
|
||||
err := c.cc.Invoke(ctx, ResticUI_Backup_FullMethodName, in, out, opts...)
|
||||
@@ -160,6 +171,7 @@ type ResticUIServer interface {
|
||||
GetOperationEvents(*emptypb.Empty, ResticUI_GetOperationEventsServer) error
|
||||
GetOperations(context.Context, *GetOperationsRequest) (*OperationList, error)
|
||||
ListSnapshots(context.Context, *ListSnapshotsRequest) (*ResticSnapshotList, error)
|
||||
ListSnapshotFiles(context.Context, *ListSnapshotFilesRequest) (*ListSnapshotFilesResponse, error)
|
||||
// Backup schedules a backup operation. It accepts a plan id and returns empty if the task is enqueued.
|
||||
Backup(context.Context, *types.StringValue) (*emptypb.Empty, error)
|
||||
// PathAutocomplete provides path autocompletion options for a given filesystem path.
|
||||
@@ -189,6 +201,9 @@ func (UnimplementedResticUIServer) GetOperations(context.Context, *GetOperations
|
||||
func (UnimplementedResticUIServer) ListSnapshots(context.Context, *ListSnapshotsRequest) (*ResticSnapshotList, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ListSnapshots not implemented")
|
||||
}
|
||||
func (UnimplementedResticUIServer) ListSnapshotFiles(context.Context, *ListSnapshotFilesRequest) (*ListSnapshotFilesResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ListSnapshotFiles not implemented")
|
||||
}
|
||||
func (UnimplementedResticUIServer) Backup(context.Context, *types.StringValue) (*emptypb.Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Backup not implemented")
|
||||
}
|
||||
@@ -319,6 +334,24 @@ func _ResticUI_ListSnapshots_Handler(srv interface{}, ctx context.Context, dec f
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ResticUI_ListSnapshotFiles_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ListSnapshotFilesRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ResticUIServer).ListSnapshotFiles(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: ResticUI_ListSnapshotFiles_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ResticUIServer).ListSnapshotFiles(ctx, req.(*ListSnapshotFilesRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ResticUI_Backup_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 {
|
||||
@@ -382,6 +415,10 @@ var ResticUI_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "ListSnapshots",
|
||||
Handler: _ResticUI_ListSnapshots_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ListSnapshotFiles",
|
||||
Handler: _ResticUI_ListSnapshotFiles_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Backup",
|
||||
Handler: _ResticUI_Backup_Handler,
|
||||
|
||||
@@ -130,6 +130,23 @@ func (s *Server) ListSnapshots(ctx context.Context, query *v1.ListSnapshotsReque
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) ListSnapshotFiles(ctx context.Context, query *v1.ListSnapshotFilesRequest) (*v1.ListSnapshotFilesResponse, error) {
|
||||
repo, err := s.orchestrator.GetRepo(query.RepoId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get repo: %w", err)
|
||||
}
|
||||
|
||||
entries, err := repo.ListSnapshotFiles(ctx, query.SnapshotId, query.Path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list snapshot files: %w", err)
|
||||
}
|
||||
|
||||
return &v1.ListSnapshotFilesResponse{
|
||||
Path: query.Path,
|
||||
Entries: entries,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetOperationEvents implements GET /v1/events/operations
|
||||
func (s *Server) GetOperationEvents(_ *emptypb.Empty, stream v1.ResticUI_GetOperationEventsServer) error {
|
||||
errorChan := make(chan error)
|
||||
|
||||
@@ -29,7 +29,7 @@ func newRepoOrchestrator(repoConfig *v1.Repo, repo *restic.Repo) *RepoOrchestrat
|
||||
}
|
||||
|
||||
func (r *RepoOrchestrator) Snapshots(ctx context.Context) ([]*restic.Snapshot, error) {
|
||||
snapshots, err := r.repo.Snapshots(ctx, restic.WithPropagatedEnvVars(restic.EnvToPropagate...), restic.WithFlags("--latest", "1000"))
|
||||
snapshots, err := r.repo.Snapshots(ctx, restic.WithFlags("--latest", "1000"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("restic.Snapshots: %w", err)
|
||||
}
|
||||
@@ -83,6 +83,23 @@ func (r *RepoOrchestrator) Backup(ctx context.Context, plan *v1.Plan, progressCa
|
||||
return summary, nil
|
||||
}
|
||||
|
||||
func (r *RepoOrchestrator) ListSnapshotFiles(ctx context.Context, snapshotId string, path string) ([]*v1.LsEntry, error) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
_, entries, err := r.repo.ListDirectory(ctx, snapshotId, path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list snapshot files: %w", err)
|
||||
}
|
||||
|
||||
lsEnts := make([]*v1.LsEntry, 0, len(entries))
|
||||
for _, entry := range entries {
|
||||
lsEnts = append(lsEnts, entry.ToProto())
|
||||
}
|
||||
|
||||
return lsEnts, nil
|
||||
}
|
||||
|
||||
func filterSnapshotsForPlan(snapshots []*restic.Snapshot, plan *v1.Plan) []*restic.Snapshot {
|
||||
wantTag := tagForPlan(plan)
|
||||
var filtered []*restic.Snapshot
|
||||
|
||||
@@ -30,7 +30,7 @@ type ScheduledBackupTask struct {
|
||||
var _ Task = &ScheduledBackupTask{}
|
||||
|
||||
func NewScheduledBackupTask(orchestrator *Orchestrator, plan *v1.Plan) (*ScheduledBackupTask, error) {
|
||||
sched, err := cronexpr.Parse(plan.Cron)
|
||||
sched, err := cronexpr.ParseInLocation(plan.Cron, time.Now().Location().String())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse schedule %q: %w", plan.Cron, err)
|
||||
}
|
||||
@@ -160,7 +160,6 @@ func indexSnapshotsHelper(ctx context.Context, orchestrator *Orchestrator, plan
|
||||
opTime := curTimeMillis()
|
||||
var indexOps []*v1.Operation
|
||||
for _, snapshot := range snapshots {
|
||||
zap.L().Debug("checking if snapshot has been indexed", zap.String("snapshot", snapshot.Id))
|
||||
opid, err := orchestrator.oplog.HasIndexedSnapshot(snapshot.Id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("HasIndexSnapshot for snapshot %q: %w", snapshot.Id, err)
|
||||
|
||||
@@ -26,6 +26,21 @@ type LsEntry struct {
|
||||
Ctime string `json:"ctime"`
|
||||
}
|
||||
|
||||
func (e *LsEntry) ToProto() *v1.LsEntry {
|
||||
return &v1.LsEntry{
|
||||
Name: e.Name,
|
||||
Type: e.Type,
|
||||
Path: e.Path,
|
||||
Uid: int64(e.Uid),
|
||||
Gid: int64(e.Gid),
|
||||
Size: int64(e.Size),
|
||||
Mode: int64(e.Mode),
|
||||
Mtime: e.Mtime,
|
||||
Atime: e.Atime,
|
||||
Ctime: e.Ctime,
|
||||
}
|
||||
}
|
||||
|
||||
type Snapshot struct {
|
||||
Id string `json:"id"`
|
||||
Time string `json:"time"`
|
||||
|
||||
@@ -208,7 +208,6 @@ func (r *Repo) ListDirectory(ctx context.Context, snapshot string, path string,
|
||||
return nil, nil, NewCmdError(cmd, output, err)
|
||||
}
|
||||
|
||||
|
||||
snapshots, entries, err := readLs(bytes.NewBuffer(output))
|
||||
if err != nil {
|
||||
return nil, nil, NewCmdError(cmd, output, err)
|
||||
|
||||
@@ -51,6 +51,13 @@ service ResticUI {
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
rpc ListSnapshotFiles(ListSnapshotFilesRequest) returns (ListSnapshotFilesResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v1/snapshots/files"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
// Backup schedules a backup operation. It accepts a plan id and returns empty if the task is enqueued.
|
||||
rpc Backup(types.StringValue) returns (google.protobuf.Empty) {
|
||||
@@ -79,3 +86,27 @@ message GetOperationsRequest {
|
||||
string plan_id = 2;
|
||||
int64 last_n = 3; // limit to the last n operations
|
||||
}
|
||||
|
||||
message ListSnapshotFilesRequest {
|
||||
string repo_id = 1;
|
||||
string snapshot_id = 2;
|
||||
string path = 3;
|
||||
}
|
||||
|
||||
message ListSnapshotFilesResponse {
|
||||
string path = 1;
|
||||
repeated LsEntry entries = 2;
|
||||
}
|
||||
|
||||
message LsEntry {
|
||||
string name = 1;
|
||||
string type = 2;
|
||||
string path = 3;
|
||||
int64 uid = 4;
|
||||
int64 gid = 5;
|
||||
int64 size = 6;
|
||||
int64 mode = 7;
|
||||
string mtime = 8;
|
||||
string atime = 9;
|
||||
string ctime = 10;
|
||||
}
|
||||
@@ -21,6 +21,30 @@ export type GetOperationsRequest = {
|
||||
lastN?: string
|
||||
}
|
||||
|
||||
export type ListSnapshotFilesRequest = {
|
||||
repoId?: string
|
||||
snapshotId?: string
|
||||
path?: string
|
||||
}
|
||||
|
||||
export type ListSnapshotFilesResponse = {
|
||||
path?: string
|
||||
entries?: LsEntry[]
|
||||
}
|
||||
|
||||
export type LsEntry = {
|
||||
name?: string
|
||||
type?: string
|
||||
path?: string
|
||||
uid?: string
|
||||
gid?: string
|
||||
size?: string
|
||||
mode?: string
|
||||
mtime?: string
|
||||
atime?: string
|
||||
ctime?: string
|
||||
}
|
||||
|
||||
export class ResticUI {
|
||||
static GetConfig(req: GoogleProtobufEmpty.Empty, initReq?: fm.InitReq): Promise<V1Config.Config> {
|
||||
return fm.fetchReq<GoogleProtobufEmpty.Empty, V1Config.Config>(`/v1/config?${fm.renderURLSearchParams(req, [])}`, {...initReq, method: "GET"})
|
||||
@@ -40,6 +64,9 @@ export class ResticUI {
|
||||
static ListSnapshots(req: ListSnapshotsRequest, initReq?: fm.InitReq): Promise<V1Restic.ResticSnapshotList> {
|
||||
return fm.fetchReq<ListSnapshotsRequest, V1Restic.ResticSnapshotList>(`/v1/snapshots`, {...initReq, method: "POST", body: JSON.stringify(req, fm.replacer)})
|
||||
}
|
||||
static ListSnapshotFiles(req: ListSnapshotFilesRequest, initReq?: fm.InitReq): Promise<ListSnapshotFilesResponse> {
|
||||
return fm.fetchReq<ListSnapshotFilesRequest, ListSnapshotFilesResponse>(`/v1/snapshots/files`, {...initReq, method: "POST", body: JSON.stringify(req, fm.replacer)})
|
||||
}
|
||||
static Backup(req: TypesValue.StringValue, initReq?: fm.InitReq): Promise<GoogleProtobufEmpty.Empty> {
|
||||
return fm.fetchReq<TypesValue.StringValue, GoogleProtobufEmpty.Empty>(`/v1/cmd/backup`, {...initReq, method: "POST", body: JSON.stringify(req, fm.replacer)})
|
||||
}
|
||||
|
||||
@@ -1,16 +1,23 @@
|
||||
import React from "react";
|
||||
import { Operation, OperationStatus } from "../../gen/ts/v1/operations.pb";
|
||||
import { Col, Collapse, Empty, List, Progress, Row, Typography } from "antd";
|
||||
import {
|
||||
AlertOutlined,
|
||||
DatabaseOutlined,
|
||||
Card,
|
||||
Col,
|
||||
Collapse,
|
||||
Empty,
|
||||
List,
|
||||
Progress,
|
||||
Row,
|
||||
Typography,
|
||||
} from "antd";
|
||||
import {
|
||||
ExclamationCircleOutlined,
|
||||
ExclamationOutlined,
|
||||
PaperClipOutlined,
|
||||
SaveOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { BackupProgressEntry, ResticSnapshot } from "../../gen/ts/v1/restic.pb";
|
||||
import { EOperation } from "../state/oplog";
|
||||
import { SnapshotBrowser } from "./SnapshotBrowser";
|
||||
|
||||
export const OperationList = ({
|
||||
operations,
|
||||
@@ -26,14 +33,60 @@ export const OperationList = ({
|
||||
);
|
||||
}
|
||||
|
||||
const groupBy = (ops: EOperation[], keyFunc: (op: EOperation) => string) => {
|
||||
const groups: { [key: string]: EOperation[] } = {};
|
||||
|
||||
ops.forEach((op) => {
|
||||
const key = keyFunc(op);
|
||||
if (!(key in groups)) {
|
||||
groups[key] = [];
|
||||
}
|
||||
groups[key].push(op);
|
||||
});
|
||||
|
||||
return Object.values(groups);
|
||||
};
|
||||
|
||||
// snapshotKey is a heuristic that tries to find a snapshot ID to group the operation by,
|
||||
// if one can not be found the operation ID is the key.
|
||||
const snapshotKey = (op: EOperation) => {
|
||||
if (
|
||||
op.operationBackup &&
|
||||
op.operationBackup.lastStatus &&
|
||||
op.operationBackup.lastStatus.summary
|
||||
) {
|
||||
return normalizeSnapshotId(
|
||||
op.operationBackup.lastStatus.summary.snapshotId!
|
||||
);
|
||||
} else if (op.operationIndexSnapshot) {
|
||||
return normalizeSnapshotId(op.operationIndexSnapshot.snapshot!.id!);
|
||||
}
|
||||
return op.id!;
|
||||
};
|
||||
|
||||
const groupedItems = groupBy(operations, snapshotKey);
|
||||
groupedItems.sort((a, b) => {
|
||||
return b[0].parsedTime - a[0].parsedTime;
|
||||
});
|
||||
|
||||
return (
|
||||
<List
|
||||
itemLayout="horizontal"
|
||||
size="small"
|
||||
dataSource={operations}
|
||||
renderItem={(item, index) => (
|
||||
<OperationRow key={item.parsedId} operation={item} />
|
||||
)}
|
||||
dataSource={groupedItems}
|
||||
renderItem={(group, index) => {
|
||||
if (group.length === 1) {
|
||||
return <OperationRow key={group[0].parsedId} operation={group[0]} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Card size="small" style={{ margin: "5px" }}>
|
||||
{group.map((op) => (
|
||||
<OperationRow key={op.parsedId} operation={op} />
|
||||
))}
|
||||
</Card>
|
||||
);
|
||||
}}
|
||||
pagination={
|
||||
operations.length > 50
|
||||
? { position: "both", align: "center", defaultPageSize: 50 }
|
||||
@@ -55,11 +108,25 @@ export const OperationRow = ({
|
||||
color = "blue";
|
||||
}
|
||||
|
||||
if (operation.displayMessage) {
|
||||
if (
|
||||
operation.displayMessage &&
|
||||
operation.status === OperationStatus.STATUS_ERROR
|
||||
) {
|
||||
let opType = "Message";
|
||||
if (operation.operationBackup) {
|
||||
opType = "Backup";
|
||||
} else if (operation.operationIndexSnapshot) {
|
||||
opType = "Snapshot";
|
||||
}
|
||||
|
||||
return (
|
||||
<List.Item>
|
||||
<List.Item.Meta
|
||||
title={<>Message</>}
|
||||
title={
|
||||
<>
|
||||
{opType} Error at {formatTime(operation.unixTimeStartMs!)}
|
||||
</>
|
||||
}
|
||||
avatar={<ExclamationCircleOutlined style={{ color }} />}
|
||||
description={operation.displayMessage}
|
||||
/>
|
||||
@@ -120,14 +187,25 @@ export const OperationRow = ({
|
||||
<>Snapshot at {formatTime(snapshotOp.snapshot!.unixTimeMs!)}</>
|
||||
}
|
||||
avatar={<PaperClipOutlined style={{ color }} />}
|
||||
description={<SnapshotInfo snapshot={snapshotOp.snapshot!} />}
|
||||
description={
|
||||
<SnapshotInfo
|
||||
snapshot={snapshotOp.snapshot!}
|
||||
repoId={operation.repoId!}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</List.Item>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const SnapshotInfo = ({ snapshot }: { snapshot: ResticSnapshot }) => {
|
||||
const SnapshotInfo = ({
|
||||
snapshot,
|
||||
repoId,
|
||||
}: {
|
||||
snapshot: ResticSnapshot;
|
||||
repoId: string;
|
||||
}) => {
|
||||
return (
|
||||
<Collapse
|
||||
size="small"
|
||||
@@ -139,7 +217,7 @@ const SnapshotInfo = ({ snapshot }: { snapshot: ResticSnapshot }) => {
|
||||
<>
|
||||
<Typography.Text>
|
||||
<Typography.Text strong>Snapshot ID: </Typography.Text>
|
||||
{snapshot.id?.substring(0, 8)}
|
||||
{normalizeSnapshotId(snapshot.id!)}
|
||||
</Typography.Text>
|
||||
<Row gutter={16}>
|
||||
<Col span={8}>
|
||||
@@ -164,7 +242,9 @@ const SnapshotInfo = ({ snapshot }: { snapshot: ResticSnapshot }) => {
|
||||
{
|
||||
key: 2,
|
||||
label: "Browse",
|
||||
children: null,
|
||||
children: (
|
||||
<SnapshotBrowser snapshotId={snapshot.id!} repoId={repoId} />
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
@@ -277,7 +357,10 @@ const formatTime = (time: number | string) => {
|
||||
}
|
||||
const d = new Date();
|
||||
d.setTime(time);
|
||||
return d.toISOString();
|
||||
return d.toLocaleString(undefined, {
|
||||
dateStyle: "short",
|
||||
timeStyle: "long",
|
||||
});
|
||||
};
|
||||
|
||||
const formatDuration = (ms: number) => {
|
||||
@@ -290,4 +373,8 @@ const formatDuration = (ms: number) => {
|
||||
return `${minutes}m${seconds % 60}s`;
|
||||
}
|
||||
return `${hours}h${minutes % 60}m${seconds % 60}s`;
|
||||
};
|
||||
};
|
||||
|
||||
const normalizeSnapshotId = (id: string) => {
|
||||
return id.substring(0, 8);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import { Input, Tree } from "antd";
|
||||
import type { DataNode, EventDataNode } from "antd/es/tree";
|
||||
import {
|
||||
ListSnapshotFilesResponse,
|
||||
LsEntry,
|
||||
ResticUI,
|
||||
} from "../../gen/ts/v1/service.pb";
|
||||
import { useAlertApi } from "./Alerts";
|
||||
import { FileOutlined, FolderOutlined } from "@ant-design/icons";
|
||||
|
||||
type ELsEntry = LsEntry & { children?: ELsEntry[] };
|
||||
|
||||
// replaceKeyInTree returns a value only if changes are made.
|
||||
const replaceKeyInTree = (
|
||||
curNode: DataNode,
|
||||
setKey: string,
|
||||
setValue: DataNode
|
||||
): DataNode | null => {
|
||||
if (curNode.key === setKey) {
|
||||
return setValue;
|
||||
}
|
||||
if (!curNode.children) {
|
||||
return null;
|
||||
}
|
||||
for (const idx in curNode.children!) {
|
||||
const child = curNode.children![idx];
|
||||
const newChild = replaceKeyInTree(child, setKey, setValue);
|
||||
if (newChild) {
|
||||
const curNodeCopy = { ...curNode };
|
||||
curNodeCopy.children = [...curNode.children!];
|
||||
curNodeCopy.children[idx] = newChild;
|
||||
return curNodeCopy;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
const findInTree = (curNode: DataNode, key: string): DataNode | null => {
|
||||
if (curNode.key === key) {
|
||||
return curNode;
|
||||
}
|
||||
|
||||
if (!curNode.children) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (const child of curNode.children) {
|
||||
const found = findInTree(child, key);
|
||||
if (found) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export const SnapshotBrowser = ({
|
||||
repoId,
|
||||
snapshotId,
|
||||
}: React.PropsWithoutRef<{ snapshotId: string; repoId: string }>) => {
|
||||
const alertApi = useAlertApi();
|
||||
const [treeData, setTreeData] = useState<DataNode[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
try {
|
||||
const resp = await ResticUI.ListSnapshotFiles(
|
||||
{
|
||||
path: "/",
|
||||
repoId,
|
||||
snapshotId,
|
||||
},
|
||||
{ pathPrefix: "/api" }
|
||||
);
|
||||
setTreeData(respToNodes(resp));
|
||||
} catch (e: any) {
|
||||
alertApi?.error("Failed to list snapshot files: " + e.message);
|
||||
}
|
||||
})();
|
||||
}, [repoId, snapshotId]);
|
||||
|
||||
const onLoadData = async ({ key, children }: EventDataNode<DataNode>) => {
|
||||
if (children) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("Loading data for key: " + key);
|
||||
|
||||
const resp = await ResticUI.ListSnapshotFiles(
|
||||
{
|
||||
path: (key + "/") as string,
|
||||
repoId,
|
||||
snapshotId,
|
||||
},
|
||||
{ pathPrefix: "/api" }
|
||||
);
|
||||
|
||||
let toUpdate: DataNode | null = null;
|
||||
for (const node of treeData) {
|
||||
toUpdate = findInTree(node, key as string);
|
||||
if (toUpdate) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!toUpdate) {
|
||||
console.log("No node to update found!");
|
||||
return;
|
||||
}
|
||||
|
||||
const toUpdateCopy = { ...toUpdate };
|
||||
toUpdateCopy.children = respToNodes(resp);
|
||||
|
||||
console.log(
|
||||
"Replacing key: " +
|
||||
key +
|
||||
" with: " +
|
||||
JSON.stringify(toUpdateCopy, null, 2)
|
||||
);
|
||||
console.log("In tree: " + JSON.stringify(treeData, null, 2));
|
||||
|
||||
const newTree = treeData.map((node) => {
|
||||
console.log("trying replace in tree...");
|
||||
const didUpdate = replaceKeyInTree(node, key as string, toUpdateCopy);
|
||||
if (didUpdate) {
|
||||
console.log("Replaced in tree successfully!");
|
||||
return didUpdate;
|
||||
}
|
||||
return node;
|
||||
});
|
||||
|
||||
console.log("New tree: ", JSON.stringify(newTree, null, 2));
|
||||
|
||||
setTreeData(newTree);
|
||||
};
|
||||
|
||||
return <Tree<DataNode> loadData={onLoadData} treeData={treeData} />;
|
||||
};
|
||||
|
||||
const respToNodes = (resp: ListSnapshotFilesResponse): DataNode[] => {
|
||||
const nodes = resp
|
||||
.entries!.filter((entry) => entry.path!.length > resp.path!.length)
|
||||
.map((entry) => {
|
||||
const lastSlash = entry.path!.lastIndexOf("/");
|
||||
const title =
|
||||
lastSlash === -1 ? entry.path : entry.path!.slice(lastSlash + 1);
|
||||
|
||||
const node: DataNode = {
|
||||
key: entry.path!,
|
||||
title: title,
|
||||
isLeaf: entry.type === "file",
|
||||
icon: entry.type === "file" ? <FileOutlined /> : <FolderOutlined />,
|
||||
};
|
||||
return node;
|
||||
});
|
||||
|
||||
console.log(JSON.stringify(nodes, null, 2));
|
||||
|
||||
return nodes;
|
||||
};
|
||||
Reference in New Issue
Block a user