feat: implement snapshot browsing

This commit is contained in:
garethgeorge
2023-11-16 19:38:37 -08:00
parent 8488d461bd
commit 8ffffa05e4
12 changed files with 882 additions and 97 deletions
+389 -77
View File
@@ -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,
},
+85
View File
@@ -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
+37
View File
@@ -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,
+17
View File
@@ -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)
+18 -1
View File
@@ -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
+1 -2
View File
@@ -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)
+15
View File
@@ -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"`
-1
View File
@@ -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)
+31
View File
@@ -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;
}
+27
View File
@@ -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)})
}
+103 -16
View File
@@ -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);
};
+159
View File
@@ -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;
};