mirror of
https://github.com/garethgeorge/backrest.git
synced 2026-08-22 08:56:38 +00:00
chore: use JWTs for signed download URLs instead of bespoke signatures
This commit is contained in:
@@ -256,7 +256,6 @@ func newRootMux(
|
||||
authedMux.Handle(backrestPath, backrestHandler)
|
||||
syncStatePath, syncStateHandlerUnauthed := v1syncconnect.NewBackrestSyncStateServiceHandler(syncStateHandler)
|
||||
authedMux.Handle(syncStatePath, syncStateHandlerUnauthed)
|
||||
authedMux.Handle("/download/", http.StripPrefix("/download", downloadHandler))
|
||||
authedMux.Handle("/metrics", metric.GetRegistry().Handler())
|
||||
|
||||
// Unauthenticated routes
|
||||
@@ -265,6 +264,7 @@ func newRootMux(
|
||||
unauthedMux.Handle(authPath, authHandler)
|
||||
syncPath, syncHandlerUnauthed := v1syncconnect.NewBackrestSyncServiceHandler(syncHandler)
|
||||
unauthedMux.Handle(syncPath, syncHandlerUnauthed)
|
||||
unauthedMux.Handle("/download/", http.StripPrefix("/download", downloadHandler))
|
||||
|
||||
// Root mux to dispatch to authenticated or unauthenticated handlers
|
||||
rootMux := http.NewServeMux()
|
||||
|
||||
+130
-74
@@ -683,6 +683,58 @@ func (x *LogDataRequest) GetRef() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
type GetDownloadURLRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
OpId int64 `protobuf:"varint,1,opt,name=op_id,json=opId,proto3" json:"op_id,omitempty"`
|
||||
FilePath string `protobuf:"bytes,2,opt,name=file_path,json=filePath,proto3" json:"file_path,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *GetDownloadURLRequest) Reset() {
|
||||
*x = GetDownloadURLRequest{}
|
||||
mi := &file_v1_service_proto_msgTypes[10]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *GetDownloadURLRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GetDownloadURLRequest) ProtoMessage() {}
|
||||
|
||||
func (x *GetDownloadURLRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_v1_service_proto_msgTypes[10]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GetDownloadURLRequest.ProtoReflect.Descriptor instead.
|
||||
func (*GetDownloadURLRequest) Descriptor() ([]byte, []int) {
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{10}
|
||||
}
|
||||
|
||||
func (x *GetDownloadURLRequest) GetOpId() int64 {
|
||||
if x != nil {
|
||||
return x.OpId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *GetDownloadURLRequest) GetFilePath() string {
|
||||
if x != nil {
|
||||
return x.FilePath
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type LsEntry struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||
@@ -701,7 +753,7 @@ type LsEntry struct {
|
||||
|
||||
func (x *LsEntry) Reset() {
|
||||
*x = LsEntry{}
|
||||
mi := &file_v1_service_proto_msgTypes[10]
|
||||
mi := &file_v1_service_proto_msgTypes[11]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -713,7 +765,7 @@ func (x *LsEntry) String() string {
|
||||
func (*LsEntry) ProtoMessage() {}
|
||||
|
||||
func (x *LsEntry) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_v1_service_proto_msgTypes[10]
|
||||
mi := &file_v1_service_proto_msgTypes[11]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -726,7 +778,7 @@ func (x *LsEntry) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use LsEntry.ProtoReflect.Descriptor instead.
|
||||
func (*LsEntry) Descriptor() ([]byte, []int) {
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{10}
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{11}
|
||||
}
|
||||
|
||||
func (x *LsEntry) GetName() string {
|
||||
@@ -809,7 +861,7 @@ type RunCommandRequest struct {
|
||||
|
||||
func (x *RunCommandRequest) Reset() {
|
||||
*x = RunCommandRequest{}
|
||||
mi := &file_v1_service_proto_msgTypes[11]
|
||||
mi := &file_v1_service_proto_msgTypes[12]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -821,7 +873,7 @@ func (x *RunCommandRequest) String() string {
|
||||
func (*RunCommandRequest) ProtoMessage() {}
|
||||
|
||||
func (x *RunCommandRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_v1_service_proto_msgTypes[11]
|
||||
mi := &file_v1_service_proto_msgTypes[12]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -834,7 +886,7 @@ func (x *RunCommandRequest) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use RunCommandRequest.ProtoReflect.Descriptor instead.
|
||||
func (*RunCommandRequest) Descriptor() ([]byte, []int) {
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{11}
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{12}
|
||||
}
|
||||
|
||||
func (x *RunCommandRequest) GetRepoId() string {
|
||||
@@ -863,7 +915,7 @@ type SummaryDashboardResponse struct {
|
||||
|
||||
func (x *SummaryDashboardResponse) Reset() {
|
||||
*x = SummaryDashboardResponse{}
|
||||
mi := &file_v1_service_proto_msgTypes[12]
|
||||
mi := &file_v1_service_proto_msgTypes[13]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -875,7 +927,7 @@ func (x *SummaryDashboardResponse) String() string {
|
||||
func (*SummaryDashboardResponse) ProtoMessage() {}
|
||||
|
||||
func (x *SummaryDashboardResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_v1_service_proto_msgTypes[12]
|
||||
mi := &file_v1_service_proto_msgTypes[13]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -888,7 +940,7 @@ func (x *SummaryDashboardResponse) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use SummaryDashboardResponse.ProtoReflect.Descriptor instead.
|
||||
func (*SummaryDashboardResponse) Descriptor() ([]byte, []int) {
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{12}
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{13}
|
||||
}
|
||||
|
||||
func (x *SummaryDashboardResponse) GetRepoSummaries() []*SummaryDashboardResponse_Summary {
|
||||
@@ -939,7 +991,7 @@ type SummaryDashboardResponse_Summary struct {
|
||||
|
||||
func (x *SummaryDashboardResponse_Summary) Reset() {
|
||||
*x = SummaryDashboardResponse_Summary{}
|
||||
mi := &file_v1_service_proto_msgTypes[13]
|
||||
mi := &file_v1_service_proto_msgTypes[14]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -951,7 +1003,7 @@ func (x *SummaryDashboardResponse_Summary) String() string {
|
||||
func (*SummaryDashboardResponse_Summary) ProtoMessage() {}
|
||||
|
||||
func (x *SummaryDashboardResponse_Summary) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_v1_service_proto_msgTypes[13]
|
||||
mi := &file_v1_service_proto_msgTypes[14]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -964,7 +1016,7 @@ func (x *SummaryDashboardResponse_Summary) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use SummaryDashboardResponse_Summary.ProtoReflect.Descriptor instead.
|
||||
func (*SummaryDashboardResponse_Summary) Descriptor() ([]byte, []int) {
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{12, 0}
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{13, 0}
|
||||
}
|
||||
|
||||
func (x *SummaryDashboardResponse_Summary) GetId() string {
|
||||
@@ -1057,7 +1109,7 @@ type SummaryDashboardResponse_BackupChart struct {
|
||||
|
||||
func (x *SummaryDashboardResponse_BackupChart) Reset() {
|
||||
*x = SummaryDashboardResponse_BackupChart{}
|
||||
mi := &file_v1_service_proto_msgTypes[14]
|
||||
mi := &file_v1_service_proto_msgTypes[15]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -1069,7 +1121,7 @@ func (x *SummaryDashboardResponse_BackupChart) String() string {
|
||||
func (*SummaryDashboardResponse_BackupChart) ProtoMessage() {}
|
||||
|
||||
func (x *SummaryDashboardResponse_BackupChart) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_v1_service_proto_msgTypes[14]
|
||||
mi := &file_v1_service_proto_msgTypes[15]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -1082,7 +1134,7 @@ func (x *SummaryDashboardResponse_BackupChart) ProtoReflect() protoreflect.Messa
|
||||
|
||||
// Deprecated: Use SummaryDashboardResponse_BackupChart.ProtoReflect.Descriptor instead.
|
||||
func (*SummaryDashboardResponse_BackupChart) Descriptor() ([]byte, []int) {
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{12, 1}
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{13, 1}
|
||||
}
|
||||
|
||||
func (x *SummaryDashboardResponse_BackupChart) GetFlowId() []int64 {
|
||||
@@ -1192,7 +1244,10 @@ const file_v1_service_proto_rawDesc = "" +
|
||||
"\x04path\x18\x01 \x01(\tR\x04path\x12%\n" +
|
||||
"\aentries\x18\x02 \x03(\v2\v.v1.LsEntryR\aentries\"\"\n" +
|
||||
"\x0eLogDataRequest\x12\x10\n" +
|
||||
"\x03ref\x18\x01 \x01(\tR\x03ref\"\xd3\x01\n" +
|
||||
"\x03ref\x18\x01 \x01(\tR\x03ref\"I\n" +
|
||||
"\x15GetDownloadURLRequest\x12\x13\n" +
|
||||
"\x05op_id\x18\x01 \x01(\x03R\x04opId\x12\x1b\n" +
|
||||
"\tfile_path\x18\x02 \x01(\tR\bfilePath\"\xd3\x01\n" +
|
||||
"\aLsEntry\x12\x12\n" +
|
||||
"\x04name\x18\x01 \x01(\tR\x04name\x12\x12\n" +
|
||||
"\x04type\x18\x02 \x01(\tR\x04type\x12\x12\n" +
|
||||
@@ -1235,7 +1290,7 @@ const file_v1_service_proto_rawDesc = "" +
|
||||
"durationMs\x12+\n" +
|
||||
"\x06status\x18\x04 \x03(\x0e2\x13.v1.OperationStatusR\x06status\x12\x1f\n" +
|
||||
"\vbytes_added\x18\x05 \x03(\x03R\n" +
|
||||
"bytesAdded2\xa7\t\n" +
|
||||
"bytesAdded2\xaf\t\n" +
|
||||
"\bBackrest\x121\n" +
|
||||
"\tGetConfig\x12\x16.google.protobuf.Empty\x1a\n" +
|
||||
".v1.Config\"\x00\x12%\n" +
|
||||
@@ -1260,8 +1315,8 @@ const file_v1_service_proto_rawDesc = "" +
|
||||
"\x06Cancel\x12\x11.types.Int64Value\x1a\x16.google.protobuf.Empty\"\x00\x124\n" +
|
||||
"\aGetLogs\x12\x12.v1.LogDataRequest\x1a\x11.types.BytesValue\"\x000\x01\x128\n" +
|
||||
"\n" +
|
||||
"RunCommand\x12\x15.v1.RunCommandRequest\x1a\x11.types.Int64Value\"\x00\x129\n" +
|
||||
"\x0eGetDownloadURL\x12\x11.types.Int64Value\x1a\x12.types.StringValue\"\x00\x12A\n" +
|
||||
"RunCommand\x12\x15.v1.RunCommandRequest\x1a\x11.types.Int64Value\"\x00\x12A\n" +
|
||||
"\x0eGetDownloadURL\x12\x19.v1.GetDownloadURLRequest\x1a\x12.types.StringValue\"\x00\x12A\n" +
|
||||
"\fClearHistory\x12\x17.v1.ClearHistoryRequest\x1a\x16.google.protobuf.Empty\"\x00\x12;\n" +
|
||||
"\x10PathAutocomplete\x12\x12.types.StringValue\x1a\x11.types.StringList\"\x00\x12M\n" +
|
||||
"\x13GetSummaryDashboard\x12\x16.google.protobuf.Empty\x1a\x1c.v1.SummaryDashboardResponse\"\x00B,Z*github.com/garethgeorge/backrest/gen/go/v1b\x06proto3"
|
||||
@@ -1279,7 +1334,7 @@ func file_v1_service_proto_rawDescGZIP() []byte {
|
||||
}
|
||||
|
||||
var file_v1_service_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
||||
var file_v1_service_proto_msgTypes = make([]protoimpl.MessageInfo, 15)
|
||||
var file_v1_service_proto_msgTypes = make([]protoimpl.MessageInfo, 16)
|
||||
var file_v1_service_proto_goTypes = []any{
|
||||
(DoRepoTaskRequest_Task)(0), // 0: v1.DoRepoTaskRequest.Task
|
||||
(*OpSelector)(nil), // 1: v1.OpSelector
|
||||
@@ -1292,73 +1347,74 @@ var file_v1_service_proto_goTypes = []any{
|
||||
(*ListSnapshotFilesRequest)(nil), // 8: v1.ListSnapshotFilesRequest
|
||||
(*ListSnapshotFilesResponse)(nil), // 9: v1.ListSnapshotFilesResponse
|
||||
(*LogDataRequest)(nil), // 10: v1.LogDataRequest
|
||||
(*LsEntry)(nil), // 11: v1.LsEntry
|
||||
(*RunCommandRequest)(nil), // 12: v1.RunCommandRequest
|
||||
(*SummaryDashboardResponse)(nil), // 13: v1.SummaryDashboardResponse
|
||||
(*SummaryDashboardResponse_Summary)(nil), // 14: v1.SummaryDashboardResponse.Summary
|
||||
(*SummaryDashboardResponse_BackupChart)(nil), // 15: v1.SummaryDashboardResponse.BackupChart
|
||||
(OperationStatus)(0), // 16: v1.OperationStatus
|
||||
(*emptypb.Empty)(nil), // 17: google.protobuf.Empty
|
||||
(*Config)(nil), // 18: v1.Config
|
||||
(*Repo)(nil), // 19: v1.Repo
|
||||
(*types.StringValue)(nil), // 20: types.StringValue
|
||||
(*types.Int64Value)(nil), // 21: types.Int64Value
|
||||
(*types.BoolValue)(nil), // 22: types.BoolValue
|
||||
(*OperationEvent)(nil), // 23: v1.OperationEvent
|
||||
(*OperationList)(nil), // 24: v1.OperationList
|
||||
(*ResticSnapshotList)(nil), // 25: v1.ResticSnapshotList
|
||||
(*types.BytesValue)(nil), // 26: types.BytesValue
|
||||
(*types.StringList)(nil), // 27: types.StringList
|
||||
(*GetDownloadURLRequest)(nil), // 11: v1.GetDownloadURLRequest
|
||||
(*LsEntry)(nil), // 12: v1.LsEntry
|
||||
(*RunCommandRequest)(nil), // 13: v1.RunCommandRequest
|
||||
(*SummaryDashboardResponse)(nil), // 14: v1.SummaryDashboardResponse
|
||||
(*SummaryDashboardResponse_Summary)(nil), // 15: v1.SummaryDashboardResponse.Summary
|
||||
(*SummaryDashboardResponse_BackupChart)(nil), // 16: v1.SummaryDashboardResponse.BackupChart
|
||||
(OperationStatus)(0), // 17: v1.OperationStatus
|
||||
(*emptypb.Empty)(nil), // 18: google.protobuf.Empty
|
||||
(*Config)(nil), // 19: v1.Config
|
||||
(*Repo)(nil), // 20: v1.Repo
|
||||
(*types.StringValue)(nil), // 21: types.StringValue
|
||||
(*types.Int64Value)(nil), // 22: types.Int64Value
|
||||
(*types.BoolValue)(nil), // 23: types.BoolValue
|
||||
(*OperationEvent)(nil), // 24: v1.OperationEvent
|
||||
(*OperationList)(nil), // 25: v1.OperationList
|
||||
(*ResticSnapshotList)(nil), // 26: v1.ResticSnapshotList
|
||||
(*types.BytesValue)(nil), // 27: types.BytesValue
|
||||
(*types.StringList)(nil), // 28: types.StringList
|
||||
}
|
||||
var file_v1_service_proto_depIdxs = []int32{
|
||||
0, // 0: v1.DoRepoTaskRequest.task:type_name -> v1.DoRepoTaskRequest.Task
|
||||
1, // 1: v1.ClearHistoryRequest.selector:type_name -> v1.OpSelector
|
||||
1, // 2: v1.GetOperationsRequest.selector:type_name -> v1.OpSelector
|
||||
11, // 3: v1.ListSnapshotFilesResponse.entries:type_name -> v1.LsEntry
|
||||
14, // 4: v1.SummaryDashboardResponse.repo_summaries:type_name -> v1.SummaryDashboardResponse.Summary
|
||||
14, // 5: v1.SummaryDashboardResponse.plan_summaries:type_name -> v1.SummaryDashboardResponse.Summary
|
||||
15, // 6: v1.SummaryDashboardResponse.Summary.recent_backups:type_name -> v1.SummaryDashboardResponse.BackupChart
|
||||
16, // 7: v1.SummaryDashboardResponse.BackupChart.status:type_name -> v1.OperationStatus
|
||||
17, // 8: v1.Backrest.GetConfig:input_type -> google.protobuf.Empty
|
||||
18, // 9: v1.Backrest.SetConfig:input_type -> v1.Config
|
||||
19, // 10: v1.Backrest.CheckRepoExists:input_type -> v1.Repo
|
||||
19, // 11: v1.Backrest.AddRepo:input_type -> v1.Repo
|
||||
20, // 12: v1.Backrest.RemoveRepo:input_type -> types.StringValue
|
||||
17, // 13: v1.Backrest.GetOperationEvents:input_type -> google.protobuf.Empty
|
||||
12, // 3: v1.ListSnapshotFilesResponse.entries:type_name -> v1.LsEntry
|
||||
15, // 4: v1.SummaryDashboardResponse.repo_summaries:type_name -> v1.SummaryDashboardResponse.Summary
|
||||
15, // 5: v1.SummaryDashboardResponse.plan_summaries:type_name -> v1.SummaryDashboardResponse.Summary
|
||||
16, // 6: v1.SummaryDashboardResponse.Summary.recent_backups:type_name -> v1.SummaryDashboardResponse.BackupChart
|
||||
17, // 7: v1.SummaryDashboardResponse.BackupChart.status:type_name -> v1.OperationStatus
|
||||
18, // 8: v1.Backrest.GetConfig:input_type -> google.protobuf.Empty
|
||||
19, // 9: v1.Backrest.SetConfig:input_type -> v1.Config
|
||||
20, // 10: v1.Backrest.CheckRepoExists:input_type -> v1.Repo
|
||||
20, // 11: v1.Backrest.AddRepo:input_type -> v1.Repo
|
||||
21, // 12: v1.Backrest.RemoveRepo:input_type -> types.StringValue
|
||||
18, // 13: v1.Backrest.GetOperationEvents:input_type -> google.protobuf.Empty
|
||||
6, // 14: v1.Backrest.GetOperations:input_type -> v1.GetOperationsRequest
|
||||
5, // 15: v1.Backrest.ListSnapshots:input_type -> v1.ListSnapshotsRequest
|
||||
8, // 16: v1.Backrest.ListSnapshotFiles:input_type -> v1.ListSnapshotFilesRequest
|
||||
20, // 17: v1.Backrest.Backup:input_type -> types.StringValue
|
||||
21, // 17: v1.Backrest.Backup:input_type -> types.StringValue
|
||||
2, // 18: v1.Backrest.DoRepoTask:input_type -> v1.DoRepoTaskRequest
|
||||
4, // 19: v1.Backrest.Forget:input_type -> v1.ForgetRequest
|
||||
7, // 20: v1.Backrest.Restore:input_type -> v1.RestoreSnapshotRequest
|
||||
21, // 21: v1.Backrest.Cancel:input_type -> types.Int64Value
|
||||
22, // 21: v1.Backrest.Cancel:input_type -> types.Int64Value
|
||||
10, // 22: v1.Backrest.GetLogs:input_type -> v1.LogDataRequest
|
||||
12, // 23: v1.Backrest.RunCommand:input_type -> v1.RunCommandRequest
|
||||
21, // 24: v1.Backrest.GetDownloadURL:input_type -> types.Int64Value
|
||||
13, // 23: v1.Backrest.RunCommand:input_type -> v1.RunCommandRequest
|
||||
11, // 24: v1.Backrest.GetDownloadURL:input_type -> v1.GetDownloadURLRequest
|
||||
3, // 25: v1.Backrest.ClearHistory:input_type -> v1.ClearHistoryRequest
|
||||
20, // 26: v1.Backrest.PathAutocomplete:input_type -> types.StringValue
|
||||
17, // 27: v1.Backrest.GetSummaryDashboard:input_type -> google.protobuf.Empty
|
||||
18, // 28: v1.Backrest.GetConfig:output_type -> v1.Config
|
||||
18, // 29: v1.Backrest.SetConfig:output_type -> v1.Config
|
||||
22, // 30: v1.Backrest.CheckRepoExists:output_type -> types.BoolValue
|
||||
18, // 31: v1.Backrest.AddRepo:output_type -> v1.Config
|
||||
18, // 32: v1.Backrest.RemoveRepo:output_type -> v1.Config
|
||||
23, // 33: v1.Backrest.GetOperationEvents:output_type -> v1.OperationEvent
|
||||
24, // 34: v1.Backrest.GetOperations:output_type -> v1.OperationList
|
||||
25, // 35: v1.Backrest.ListSnapshots:output_type -> v1.ResticSnapshotList
|
||||
21, // 26: v1.Backrest.PathAutocomplete:input_type -> types.StringValue
|
||||
18, // 27: v1.Backrest.GetSummaryDashboard:input_type -> google.protobuf.Empty
|
||||
19, // 28: v1.Backrest.GetConfig:output_type -> v1.Config
|
||||
19, // 29: v1.Backrest.SetConfig:output_type -> v1.Config
|
||||
23, // 30: v1.Backrest.CheckRepoExists:output_type -> types.BoolValue
|
||||
19, // 31: v1.Backrest.AddRepo:output_type -> v1.Config
|
||||
19, // 32: v1.Backrest.RemoveRepo:output_type -> v1.Config
|
||||
24, // 33: v1.Backrest.GetOperationEvents:output_type -> v1.OperationEvent
|
||||
25, // 34: v1.Backrest.GetOperations:output_type -> v1.OperationList
|
||||
26, // 35: v1.Backrest.ListSnapshots:output_type -> v1.ResticSnapshotList
|
||||
9, // 36: v1.Backrest.ListSnapshotFiles:output_type -> v1.ListSnapshotFilesResponse
|
||||
17, // 37: v1.Backrest.Backup:output_type -> google.protobuf.Empty
|
||||
17, // 38: v1.Backrest.DoRepoTask:output_type -> google.protobuf.Empty
|
||||
17, // 39: v1.Backrest.Forget:output_type -> google.protobuf.Empty
|
||||
17, // 40: v1.Backrest.Restore:output_type -> google.protobuf.Empty
|
||||
17, // 41: v1.Backrest.Cancel:output_type -> google.protobuf.Empty
|
||||
26, // 42: v1.Backrest.GetLogs:output_type -> types.BytesValue
|
||||
21, // 43: v1.Backrest.RunCommand:output_type -> types.Int64Value
|
||||
20, // 44: v1.Backrest.GetDownloadURL:output_type -> types.StringValue
|
||||
17, // 45: v1.Backrest.ClearHistory:output_type -> google.protobuf.Empty
|
||||
27, // 46: v1.Backrest.PathAutocomplete:output_type -> types.StringList
|
||||
13, // 47: v1.Backrest.GetSummaryDashboard:output_type -> v1.SummaryDashboardResponse
|
||||
18, // 37: v1.Backrest.Backup:output_type -> google.protobuf.Empty
|
||||
18, // 38: v1.Backrest.DoRepoTask:output_type -> google.protobuf.Empty
|
||||
18, // 39: v1.Backrest.Forget:output_type -> google.protobuf.Empty
|
||||
18, // 40: v1.Backrest.Restore:output_type -> google.protobuf.Empty
|
||||
18, // 41: v1.Backrest.Cancel:output_type -> google.protobuf.Empty
|
||||
27, // 42: v1.Backrest.GetLogs:output_type -> types.BytesValue
|
||||
22, // 43: v1.Backrest.RunCommand:output_type -> types.Int64Value
|
||||
21, // 44: v1.Backrest.GetDownloadURL:output_type -> types.StringValue
|
||||
18, // 45: v1.Backrest.ClearHistory:output_type -> google.protobuf.Empty
|
||||
28, // 46: v1.Backrest.PathAutocomplete:output_type -> types.StringList
|
||||
14, // 47: v1.Backrest.GetSummaryDashboard:output_type -> v1.SummaryDashboardResponse
|
||||
28, // [28:48] is the sub-list for method output_type
|
||||
8, // [8:28] is the sub-list for method input_type
|
||||
8, // [8:8] is the sub-list for extension type_name
|
||||
@@ -1381,7 +1437,7 @@ func file_v1_service_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_v1_service_proto_rawDesc), len(file_v1_service_proto_rawDesc)),
|
||||
NumEnums: 1,
|
||||
NumMessages: 15,
|
||||
NumMessages: 16,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
|
||||
@@ -70,8 +70,8 @@ type BackrestClient interface {
|
||||
GetLogs(ctx context.Context, in *LogDataRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[types.BytesValue], error)
|
||||
// RunCommand executes a generic restic command on the repository.
|
||||
RunCommand(ctx context.Context, in *RunCommandRequest, opts ...grpc.CallOption) (*types.Int64Value, error)
|
||||
// GetDownloadURL returns a signed download URL given a forget operation ID.
|
||||
GetDownloadURL(ctx context.Context, in *types.Int64Value, opts ...grpc.CallOption) (*types.StringValue, error)
|
||||
// GetDownloadURL returns a signed download URL given an operation ID and file path.
|
||||
GetDownloadURL(ctx context.Context, in *GetDownloadURLRequest, opts ...grpc.CallOption) (*types.StringValue, error)
|
||||
// Clears the history of operations
|
||||
ClearHistory(ctx context.Context, in *ClearHistoryRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
|
||||
// PathAutocomplete provides path autocompletion options for a given filesystem path.
|
||||
@@ -266,7 +266,7 @@ func (c *backrestClient) RunCommand(ctx context.Context, in *RunCommandRequest,
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *backrestClient) GetDownloadURL(ctx context.Context, in *types.Int64Value, opts ...grpc.CallOption) (*types.StringValue, error) {
|
||||
func (c *backrestClient) GetDownloadURL(ctx context.Context, in *GetDownloadURLRequest, opts ...grpc.CallOption) (*types.StringValue, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(types.StringValue)
|
||||
err := c.cc.Invoke(ctx, Backrest_GetDownloadURL_FullMethodName, in, out, cOpts...)
|
||||
@@ -333,8 +333,8 @@ type BackrestServer interface {
|
||||
GetLogs(*LogDataRequest, grpc.ServerStreamingServer[types.BytesValue]) error
|
||||
// RunCommand executes a generic restic command on the repository.
|
||||
RunCommand(context.Context, *RunCommandRequest) (*types.Int64Value, error)
|
||||
// GetDownloadURL returns a signed download URL given a forget operation ID.
|
||||
GetDownloadURL(context.Context, *types.Int64Value) (*types.StringValue, error)
|
||||
// GetDownloadURL returns a signed download URL given an operation ID and file path.
|
||||
GetDownloadURL(context.Context, *GetDownloadURLRequest) (*types.StringValue, error)
|
||||
// Clears the history of operations
|
||||
ClearHistory(context.Context, *ClearHistoryRequest) (*emptypb.Empty, error)
|
||||
// PathAutocomplete provides path autocompletion options for a given filesystem path.
|
||||
@@ -399,7 +399,7 @@ func (UnimplementedBackrestServer) GetLogs(*LogDataRequest, grpc.ServerStreaming
|
||||
func (UnimplementedBackrestServer) RunCommand(context.Context, *RunCommandRequest) (*types.Int64Value, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method RunCommand not implemented")
|
||||
}
|
||||
func (UnimplementedBackrestServer) GetDownloadURL(context.Context, *types.Int64Value) (*types.StringValue, error) {
|
||||
func (UnimplementedBackrestServer) GetDownloadURL(context.Context, *GetDownloadURLRequest) (*types.StringValue, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetDownloadURL not implemented")
|
||||
}
|
||||
func (UnimplementedBackrestServer) ClearHistory(context.Context, *ClearHistoryRequest) (*emptypb.Empty, error) {
|
||||
@@ -707,7 +707,7 @@ func _Backrest_RunCommand_Handler(srv interface{}, ctx context.Context, dec func
|
||||
}
|
||||
|
||||
func _Backrest_GetDownloadURL_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(types.Int64Value)
|
||||
in := new(GetDownloadURLRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -719,7 +719,7 @@ func _Backrest_GetDownloadURL_Handler(srv interface{}, ctx context.Context, dec
|
||||
FullMethod: Backrest_GetDownloadURL_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(BackrestServer).GetDownloadURL(ctx, req.(*types.Int64Value))
|
||||
return srv.(BackrestServer).GetDownloadURL(ctx, req.(*GetDownloadURLRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
@@ -107,8 +107,8 @@ type BackrestClient interface {
|
||||
GetLogs(context.Context, *connect.Request[v1.LogDataRequest]) (*connect.ServerStreamForClient[types.BytesValue], error)
|
||||
// RunCommand executes a generic restic command on the repository.
|
||||
RunCommand(context.Context, *connect.Request[v1.RunCommandRequest]) (*connect.Response[types.Int64Value], error)
|
||||
// GetDownloadURL returns a signed download URL given a forget operation ID.
|
||||
GetDownloadURL(context.Context, *connect.Request[types.Int64Value]) (*connect.Response[types.StringValue], error)
|
||||
// GetDownloadURL returns a signed download URL given an operation ID and file path.
|
||||
GetDownloadURL(context.Context, *connect.Request[v1.GetDownloadURLRequest]) (*connect.Response[types.StringValue], error)
|
||||
// Clears the history of operations
|
||||
ClearHistory(context.Context, *connect.Request[v1.ClearHistoryRequest]) (*connect.Response[emptypb.Empty], error)
|
||||
// PathAutocomplete provides path autocompletion options for a given filesystem path.
|
||||
@@ -224,7 +224,7 @@ func NewBackrestClient(httpClient connect.HTTPClient, baseURL string, opts ...co
|
||||
connect.WithSchema(backrestMethods.ByName("RunCommand")),
|
||||
connect.WithClientOptions(opts...),
|
||||
),
|
||||
getDownloadURL: connect.NewClient[types.Int64Value, types.StringValue](
|
||||
getDownloadURL: connect.NewClient[v1.GetDownloadURLRequest, types.StringValue](
|
||||
httpClient,
|
||||
baseURL+BackrestGetDownloadURLProcedure,
|
||||
connect.WithSchema(backrestMethods.ByName("GetDownloadURL")),
|
||||
@@ -269,7 +269,7 @@ type backrestClient struct {
|
||||
cancel *connect.Client[types.Int64Value, emptypb.Empty]
|
||||
getLogs *connect.Client[v1.LogDataRequest, types.BytesValue]
|
||||
runCommand *connect.Client[v1.RunCommandRequest, types.Int64Value]
|
||||
getDownloadURL *connect.Client[types.Int64Value, types.StringValue]
|
||||
getDownloadURL *connect.Client[v1.GetDownloadURLRequest, types.StringValue]
|
||||
clearHistory *connect.Client[v1.ClearHistoryRequest, emptypb.Empty]
|
||||
pathAutocomplete *connect.Client[types.StringValue, types.StringList]
|
||||
getSummaryDashboard *connect.Client[emptypb.Empty, v1.SummaryDashboardResponse]
|
||||
@@ -356,7 +356,7 @@ func (c *backrestClient) RunCommand(ctx context.Context, req *connect.Request[v1
|
||||
}
|
||||
|
||||
// GetDownloadURL calls v1.Backrest.GetDownloadURL.
|
||||
func (c *backrestClient) GetDownloadURL(ctx context.Context, req *connect.Request[types.Int64Value]) (*connect.Response[types.StringValue], error) {
|
||||
func (c *backrestClient) GetDownloadURL(ctx context.Context, req *connect.Request[v1.GetDownloadURLRequest]) (*connect.Response[types.StringValue], error) {
|
||||
return c.getDownloadURL.CallUnary(ctx, req)
|
||||
}
|
||||
|
||||
@@ -400,8 +400,8 @@ type BackrestHandler interface {
|
||||
GetLogs(context.Context, *connect.Request[v1.LogDataRequest], *connect.ServerStream[types.BytesValue]) error
|
||||
// RunCommand executes a generic restic command on the repository.
|
||||
RunCommand(context.Context, *connect.Request[v1.RunCommandRequest]) (*connect.Response[types.Int64Value], error)
|
||||
// GetDownloadURL returns a signed download URL given a forget operation ID.
|
||||
GetDownloadURL(context.Context, *connect.Request[types.Int64Value]) (*connect.Response[types.StringValue], error)
|
||||
// GetDownloadURL returns a signed download URL given an operation ID and file path.
|
||||
GetDownloadURL(context.Context, *connect.Request[v1.GetDownloadURLRequest]) (*connect.Response[types.StringValue], error)
|
||||
// Clears the history of operations
|
||||
ClearHistory(context.Context, *connect.Request[v1.ClearHistoryRequest]) (*connect.Response[emptypb.Empty], error)
|
||||
// PathAutocomplete provides path autocompletion options for a given filesystem path.
|
||||
@@ -652,7 +652,7 @@ func (UnimplementedBackrestHandler) RunCommand(context.Context, *connect.Request
|
||||
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("v1.Backrest.RunCommand is not implemented"))
|
||||
}
|
||||
|
||||
func (UnimplementedBackrestHandler) GetDownloadURL(context.Context, *connect.Request[types.Int64Value]) (*connect.Response[types.StringValue], error) {
|
||||
func (UnimplementedBackrestHandler) GetDownloadURL(context.Context, *connect.Request[v1.GetDownloadURLRequest]) (*connect.Response[types.StringValue], error) {
|
||||
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("v1.Backrest.GetDownloadURL is not implemented"))
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ package api
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -750,25 +749,32 @@ func (s *BackrestHandler) GetLogs(ctx context.Context, req *connect.Request[v1.L
|
||||
|
||||
}
|
||||
|
||||
func (s *BackrestHandler) GetDownloadURL(ctx context.Context, req *connect.Request[types.Int64Value]) (*connect.Response[types.StringValue], error) {
|
||||
op, err := s.oplog.Get(req.Msg.Value)
|
||||
func (s *BackrestHandler) GetDownloadURL(ctx context.Context, req *connect.Request[v1.GetDownloadURLRequest]) (*connect.Response[types.StringValue], error) {
|
||||
op, err := s.oplog.Get(req.Msg.OpId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get operation %v: %w", req.Msg.Value, err)
|
||||
return nil, fmt.Errorf("failed to get operation %v: %w", req.Msg.OpId, err)
|
||||
}
|
||||
|
||||
var opType string
|
||||
switch op.Op.(type) {
|
||||
case *v1.Operation_OperationIndexSnapshot:
|
||||
opType = "snapshot"
|
||||
case *v1.Operation_OperationRestore:
|
||||
opType = "restore"
|
||||
default:
|
||||
return nil, fmt.Errorf("operation %v is not a restore or snapshot operation", req.Msg.Value)
|
||||
return nil, fmt.Errorf("operation %v is not a restore or snapshot operation", req.Msg.OpId)
|
||||
}
|
||||
|
||||
signature, err := signInt64(op.Id) // the signature authenticates the download URL. Note that the shared URL will be valid for any downloader.
|
||||
token, err := signDownloadToken(DownloadTokenPayload{
|
||||
OpID: op.Id,
|
||||
Type: opType,
|
||||
FilePath: req.Msg.FilePath,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate signature: %w", err)
|
||||
return nil, fmt.Errorf("failed to generate token: %w", err)
|
||||
}
|
||||
return connect.NewResponse(&types.StringValue{
|
||||
Value: fmt.Sprintf("./download/%x-%s/", op.Id, hex.EncodeToString(signature)),
|
||||
Value: fmt.Sprintf("./download/%s/", token),
|
||||
}), nil
|
||||
}
|
||||
|
||||
|
||||
@@ -4,8 +4,6 @@ import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"crypto/hmac"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -13,7 +11,6 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -25,20 +22,20 @@ import (
|
||||
|
||||
func NewDownloadHandler(oplog *oplog.OpLog, orchestrator *orchestrator.Orchestrator) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
p := r.URL.Path[1:]
|
||||
|
||||
opID, signature, filePath, err := parseDownloadPath(p)
|
||||
if err != nil {
|
||||
http.Error(w, "invalid path", http.StatusBadRequest)
|
||||
return
|
||||
tokenStr := strings.TrimSuffix(r.URL.Path[1:], "/")
|
||||
// The URL might have trailing path which we ignore for token verification but might use for validation.
|
||||
if sep := strings.Index(tokenStr, "/"); sep != -1 {
|
||||
tokenStr = tokenStr[:sep]
|
||||
}
|
||||
|
||||
if ok, err := checkDownloadURLSignature(opID, signature); err != nil || !ok {
|
||||
payload, err := verifyDownloadToken(tokenStr)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("invalid signature: %v", err), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
op, err := oplog.Get(int64(opID))
|
||||
filePath := payload.FilePath
|
||||
op, err := oplog.Get(payload.OpID)
|
||||
if err != nil {
|
||||
http.Error(w, "restore not found", http.StatusNotFound)
|
||||
return
|
||||
@@ -144,38 +141,6 @@ func handleRestoreDownload(w http.ResponseWriter, r *http.Request, op *v1.Operat
|
||||
}
|
||||
}
|
||||
|
||||
func parseDownloadPath(p string) (int64, string, string, error) {
|
||||
sep := strings.Index(p, "/")
|
||||
if sep == -1 {
|
||||
return 0, "", "", fmt.Errorf("invalid path")
|
||||
}
|
||||
restoreID := p[:sep]
|
||||
filePath := p[sep+1:]
|
||||
|
||||
dash := strings.Index(restoreID, "-")
|
||||
if dash == -1 {
|
||||
return 0, "", "", fmt.Errorf("invalid restore ID")
|
||||
}
|
||||
opID, err := strconv.ParseInt(restoreID[:dash], 16, 64)
|
||||
if err != nil {
|
||||
return 0, "", "", fmt.Errorf("invalid restore ID: %w", err)
|
||||
}
|
||||
signature := restoreID[dash+1:]
|
||||
return opID, signature, filePath, nil
|
||||
}
|
||||
|
||||
func checkDownloadURLSignature(id int64, signature string) (bool, error) {
|
||||
wantSignatureBytes, err := signInt64(id)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
signatureBytes, err := hex.DecodeString(signature)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return hmac.Equal(wantSignatureBytes, signatureBytes), nil
|
||||
}
|
||||
|
||||
func tarDirectory(w io.Writer, dirpath string) error {
|
||||
tw := tar.NewWriter(w)
|
||||
defer tw.Close()
|
||||
|
||||
+43
-13
@@ -1,10 +1,12 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -18,16 +20,44 @@ func init() {
|
||||
}
|
||||
}
|
||||
|
||||
func sign(data []byte) ([]byte, error) {
|
||||
h := hmac.New(crypto.SHA256.New, secret)
|
||||
if n, err := h.Write(data); n != len(data) || err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return h.Sum(nil), nil
|
||||
type DownloadTokenPayload struct {
|
||||
OpID int64 `json:"op_id"`
|
||||
Type string `json:"type"` // "snapshot" or "restore"
|
||||
FilePath string `json:"file_path"`
|
||||
}
|
||||
|
||||
func signInt64(data int64) ([]byte, error) {
|
||||
dataBytes := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(dataBytes, uint64(data))
|
||||
return sign(dataBytes)
|
||||
func signDownloadToken(payload DownloadTokenPayload) (string, error) {
|
||||
jsonPayload, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to marshal payload: %w", err)
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims{
|
||||
Subject: string(jsonPayload),
|
||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),
|
||||
})
|
||||
|
||||
return token.SignedString(secret)
|
||||
}
|
||||
|
||||
func verifyDownloadToken(tokenString string) (*DownloadTokenPayload, error) {
|
||||
token, err := jwt.ParseWithClaims(tokenString, &jwt.RegisteredClaims{}, func(token *jwt.Token) (interface{}, error) {
|
||||
return secret, nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse token: %w", err)
|
||||
}
|
||||
|
||||
claims, ok := token.Claims.(*jwt.RegisteredClaims)
|
||||
if !ok || !token.Valid {
|
||||
return nil, fmt.Errorf("invalid token claims")
|
||||
}
|
||||
|
||||
var payload DownloadTokenPayload
|
||||
if err := json.Unmarshal([]byte(claims.Subject), &payload); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal payload from subject: %w", err)
|
||||
}
|
||||
|
||||
return &payload, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDownloadToken(t *testing.T) {
|
||||
payload := DownloadTokenPayload{
|
||||
OpID: 12345,
|
||||
Type: "snapshot",
|
||||
FilePath: "/path/to/file",
|
||||
}
|
||||
|
||||
token, err := signDownloadToken(payload)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, token)
|
||||
|
||||
verified, err := verifyDownloadToken(token)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, payload.OpID, verified.OpID)
|
||||
assert.Equal(t, payload.Type, verified.Type)
|
||||
assert.Equal(t, payload.FilePath, verified.FilePath)
|
||||
}
|
||||
|
||||
func TestVerifyInvalidToken(t *testing.T) {
|
||||
_, err := verifyDownloadToken("invalid.token.here")
|
||||
assert.Error(t, err)
|
||||
}
|
||||
@@ -51,8 +51,8 @@ service Backrest {
|
||||
// RunCommand executes a generic restic command on the repository.
|
||||
rpc RunCommand(RunCommandRequest) returns (types.Int64Value) {}
|
||||
|
||||
// GetDownloadURL returns a signed download URL given a forget operation ID.
|
||||
rpc GetDownloadURL(types.Int64Value) returns (types.StringValue) {}
|
||||
// GetDownloadURL returns a signed download URL given an operation ID and file path.
|
||||
rpc GetDownloadURL(GetDownloadURLRequest) returns (types.StringValue) {}
|
||||
|
||||
// Clears the history of operations
|
||||
rpc ClearHistory(ClearHistoryRequest) returns (google.protobuf.Empty) {}
|
||||
@@ -133,6 +133,11 @@ message LogDataRequest {
|
||||
string ref = 1;
|
||||
}
|
||||
|
||||
message GetDownloadURLRequest {
|
||||
int64 op_id = 1;
|
||||
string file_path = 2;
|
||||
}
|
||||
|
||||
message LsEntry {
|
||||
string name = 1;
|
||||
string type = 2;
|
||||
|
||||
@@ -21,7 +21,7 @@ import type { Message } from "@bufbuild/protobuf";
|
||||
* Describes the file v1/service.proto.
|
||||
*/
|
||||
export const file_v1_service: GenFile = /*@__PURE__*/
|
||||
fileDesc("ChB2MS9zZXJ2aWNlLnByb3RvEgJ2MSK/AgoKT3BTZWxlY3RvchILCgNpZHMYASADKAMSGAoLaW5zdGFuY2VfaWQYBiABKAlIAIgBARIkChdvcmlnaW5hbF9pbnN0YW5jZV9rZXlpZBgIIAEoCUgBiAEBEhYKCXJlcG9fZ3VpZBgHIAEoCUgCiAEBEhQKB3BsYW5faWQYAyABKAlIA4gBARIYCgtzbmFwc2hvdF9pZBgEIAEoCUgEiAEBEhQKB2Zsb3dfaWQYBSABKANIBYgBARIWCgltb2Rub19ndGUYCSABKANIBogBAUIOCgxfaW5zdGFuY2VfaWRCGgoYX29yaWdpbmFsX2luc3RhbmNlX2tleWlkQgwKCl9yZXBvX2d1aWRCCgoIX3BsYW5faWRCDgoMX3NuYXBzaG90X2lkQgoKCF9mbG93X2lkQgwKCl9tb2Rub19ndGUiwAEKEURvUmVwb1Rhc2tSZXF1ZXN0Eg8KB3JlcG9faWQYASABKAkSKAoEdGFzaxgCIAEoDjIaLnYxLkRvUmVwb1Rhc2tSZXF1ZXN0LlRhc2sicAoEVGFzaxINCglUQVNLX05PTkUQABIYChRUQVNLX0lOREVYX1NOQVBTSE9UUxABEg4KClRBU0tfUFJVTkUQAhIOCgpUQVNLX0NIRUNLEAMSDgoKVEFTS19TVEFUUxAEEg8KC1RBU0tfVU5MT0NLEAUiTAoTQ2xlYXJIaXN0b3J5UmVxdWVzdBIgCghzZWxlY3RvchgBIAEoCzIOLnYxLk9wU2VsZWN0b3ISEwoLb25seV9mYWlsZWQYAiABKAgiRgoNRm9yZ2V0UmVxdWVzdBIPCgdyZXBvX2lkGAEgASgJEg8KB3BsYW5faWQYAiABKAkSEwoLc25hcHNob3RfaWQYAyABKAkiOAoUTGlzdFNuYXBzaG90c1JlcXVlc3QSDwoHcmVwb19pZBgBIAEoCRIPCgdwbGFuX2lkGAIgASgJIkgKFEdldE9wZXJhdGlvbnNSZXF1ZXN0EiAKCHNlbGVjdG9yGAEgASgLMg4udjEuT3BTZWxlY3RvchIOCgZsYXN0X24YAiABKAMibQoWUmVzdG9yZVNuYXBzaG90UmVxdWVzdBIPCgdwbGFuX2lkGAEgASgJEg8KB3JlcG9faWQYBSABKAkSEwoLc25hcHNob3RfaWQYAiABKAkSDAoEcGF0aBgDIAEoCRIOCgZ0YXJnZXQYBCABKAkiUAoYTGlzdFNuYXBzaG90RmlsZXNSZXF1ZXN0EhEKCXJlcG9fZ3VpZBgBIAEoCRITCgtzbmFwc2hvdF9pZBgCIAEoCRIMCgRwYXRoGAMgASgJIkcKGUxpc3RTbmFwc2hvdEZpbGVzUmVzcG9uc2USDAoEcGF0aBgBIAEoCRIcCgdlbnRyaWVzGAIgAygLMgsudjEuTHNFbnRyeSIdCg5Mb2dEYXRhUmVxdWVzdBILCgNyZWYYASABKAkilgEKB0xzRW50cnkSDAoEbmFtZRgBIAEoCRIMCgR0eXBlGAIgASgJEgwKBHBhdGgYAyABKAkSCwoDdWlkGAQgASgDEgsKA2dpZBgFIAEoAxIMCgRzaXplGAYgASgDEgwKBG1vZGUYByABKAMSDQoFbXRpbWUYCCABKAkSDQoFYXRpbWUYCSABKAkSDQoFY3RpbWUYCiABKAkiNQoRUnVuQ29tbWFuZFJlcXVlc3QSDwoHcmVwb19pZBgBIAEoCRIPCgdjb21tYW5kGAIgASgJIrUFChhTdW1tYXJ5RGFzaGJvYXJkUmVzcG9uc2USPAoOcmVwb19zdW1tYXJpZXMYASADKAsyJC52MS5TdW1tYXJ5RGFzaGJvYXJkUmVzcG9uc2UuU3VtbWFyeRI8Cg5wbGFuX3N1bW1hcmllcxgCIAMoCzIkLnYxLlN1bW1hcnlEYXNoYm9hcmRSZXNwb25zZS5TdW1tYXJ5EhMKC2NvbmZpZ19wYXRoGAogASgJEhEKCWRhdGFfcGF0aBgLIAEoCRruAgoHU3VtbWFyeRIKCgJpZBgBIAEoCRIdChViYWNrdXBzX2ZhaWxlZF8zMGRheXMYAiABKAMSIwobYmFja3Vwc193YXJuaW5nX2xhc3RfMzBkYXlzGAMgASgDEiMKG2JhY2t1cHNfc3VjY2Vzc19sYXN0XzMwZGF5cxgEIAEoAxIhChlieXRlc19zY2FubmVkX2xhc3RfMzBkYXlzGAUgASgDEh8KF2J5dGVzX2FkZGVkX2xhc3RfMzBkYXlzGAYgASgDEhcKD3RvdGFsX3NuYXBzaG90cxgHIAEoAxIZChFieXRlc19zY2FubmVkX2F2ZxgIIAEoAxIXCg9ieXRlc19hZGRlZF9hdmcYCSABKAMSGwoTbmV4dF9iYWNrdXBfdGltZV9tcxgKIAEoAxJACg5yZWNlbnRfYmFja3VwcxgLIAEoCzIoLnYxLlN1bW1hcnlEYXNoYm9hcmRSZXNwb25zZS5CYWNrdXBDaGFydBqDAQoLQmFja3VwQ2hhcnQSDwoHZmxvd19pZBgBIAMoAxIUCgx0aW1lc3RhbXBfbXMYAiADKAMSEwoLZHVyYXRpb25fbXMYAyADKAMSIwoGc3RhdHVzGAQgAygOMhMudjEuT3BlcmF0aW9uU3RhdHVzEhMKC2J5dGVzX2FkZGVkGAUgAygDMqcJCghCYWNrcmVzdBIxCglHZXRDb25maWcSFi5nb29nbGUucHJvdG9idWYuRW1wdHkaCi52MS5Db25maWciABIlCglTZXRDb25maWcSCi52MS5Db25maWcaCi52MS5Db25maWciABIvCg9DaGVja1JlcG9FeGlzdHMSCC52MS5SZXBvGhAudHlwZXMuQm9vbFZhbHVlIgASIQoHQWRkUmVwbxIILnYxLlJlcG8aCi52MS5Db25maWciABIuCgpSZW1vdmVSZXBvEhIudHlwZXMuU3RyaW5nVmFsdWUaCi52MS5Db25maWciABJEChJHZXRPcGVyYXRpb25FdmVudHMSFi5nb29nbGUucHJvdG9idWYuRW1wdHkaEi52MS5PcGVyYXRpb25FdmVudCIAMAESPgoNR2V0T3BlcmF0aW9ucxIYLnYxLkdldE9wZXJhdGlvbnNSZXF1ZXN0GhEudjEuT3BlcmF0aW9uTGlzdCIAEkMKDUxpc3RTbmFwc2hvdHMSGC52MS5MaXN0U25hcHNob3RzUmVxdWVzdBoWLnYxLlJlc3RpY1NuYXBzaG90TGlzdCIAElIKEUxpc3RTbmFwc2hvdEZpbGVzEhwudjEuTGlzdFNuYXBzaG90RmlsZXNSZXF1ZXN0Gh0udjEuTGlzdFNuYXBzaG90RmlsZXNSZXNwb25zZSIAEjYKBkJhY2t1cBISLnR5cGVzLlN0cmluZ1ZhbHVlGhYuZ29vZ2xlLnByb3RvYnVmLkVtcHR5IgASPQoKRG9SZXBvVGFzaxIVLnYxLkRvUmVwb1Rhc2tSZXF1ZXN0GhYuZ29vZ2xlLnByb3RvYnVmLkVtcHR5IgASNQoGRm9yZ2V0EhEudjEuRm9yZ2V0UmVxdWVzdBoWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eSIAEj8KB1Jlc3RvcmUSGi52MS5SZXN0b3JlU25hcHNob3RSZXF1ZXN0GhYuZ29vZ2xlLnByb3RvYnVmLkVtcHR5IgASNQoGQ2FuY2VsEhEudHlwZXMuSW50NjRWYWx1ZRoWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eSIAEjQKB0dldExvZ3MSEi52MS5Mb2dEYXRhUmVxdWVzdBoRLnR5cGVzLkJ5dGVzVmFsdWUiADABEjgKClJ1bkNvbW1hbmQSFS52MS5SdW5Db21tYW5kUmVxdWVzdBoRLnR5cGVzLkludDY0VmFsdWUiABI5Cg5HZXREb3dubG9hZFVSTBIRLnR5cGVzLkludDY0VmFsdWUaEi50eXBlcy5TdHJpbmdWYWx1ZSIAEkEKDENsZWFySGlzdG9yeRIXLnYxLkNsZWFySGlzdG9yeVJlcXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkiABI7ChBQYXRoQXV0b2NvbXBsZXRlEhIudHlwZXMuU3RyaW5nVmFsdWUaES50eXBlcy5TdHJpbmdMaXN0IgASTQoTR2V0U3VtbWFyeURhc2hib2FyZBIWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eRocLnYxLlN1bW1hcnlEYXNoYm9hcmRSZXNwb25zZSIAQixaKmdpdGh1Yi5jb20vZ2FyZXRoZ2VvcmdlL2JhY2tyZXN0L2dlbi9nby92MWIGcHJvdG8z", [file_v1_config, file_v1_restic, file_v1_operations, file_types_value, file_google_protobuf_empty, file_google_api_annotations]);
|
||||
fileDesc("ChB2MS9zZXJ2aWNlLnByb3RvEgJ2MSK/AgoKT3BTZWxlY3RvchILCgNpZHMYASADKAMSGAoLaW5zdGFuY2VfaWQYBiABKAlIAIgBARIkChdvcmlnaW5hbF9pbnN0YW5jZV9rZXlpZBgIIAEoCUgBiAEBEhYKCXJlcG9fZ3VpZBgHIAEoCUgCiAEBEhQKB3BsYW5faWQYAyABKAlIA4gBARIYCgtzbmFwc2hvdF9pZBgEIAEoCUgEiAEBEhQKB2Zsb3dfaWQYBSABKANIBYgBARIWCgltb2Rub19ndGUYCSABKANIBogBAUIOCgxfaW5zdGFuY2VfaWRCGgoYX29yaWdpbmFsX2luc3RhbmNlX2tleWlkQgwKCl9yZXBvX2d1aWRCCgoIX3BsYW5faWRCDgoMX3NuYXBzaG90X2lkQgoKCF9mbG93X2lkQgwKCl9tb2Rub19ndGUiwAEKEURvUmVwb1Rhc2tSZXF1ZXN0Eg8KB3JlcG9faWQYASABKAkSKAoEdGFzaxgCIAEoDjIaLnYxLkRvUmVwb1Rhc2tSZXF1ZXN0LlRhc2sicAoEVGFzaxINCglUQVNLX05PTkUQABIYChRUQVNLX0lOREVYX1NOQVBTSE9UUxABEg4KClRBU0tfUFJVTkUQAhIOCgpUQVNLX0NIRUNLEAMSDgoKVEFTS19TVEFUUxAEEg8KC1RBU0tfVU5MT0NLEAUiTAoTQ2xlYXJIaXN0b3J5UmVxdWVzdBIgCghzZWxlY3RvchgBIAEoCzIOLnYxLk9wU2VsZWN0b3ISEwoLb25seV9mYWlsZWQYAiABKAgiRgoNRm9yZ2V0UmVxdWVzdBIPCgdyZXBvX2lkGAEgASgJEg8KB3BsYW5faWQYAiABKAkSEwoLc25hcHNob3RfaWQYAyABKAkiOAoUTGlzdFNuYXBzaG90c1JlcXVlc3QSDwoHcmVwb19pZBgBIAEoCRIPCgdwbGFuX2lkGAIgASgJIkgKFEdldE9wZXJhdGlvbnNSZXF1ZXN0EiAKCHNlbGVjdG9yGAEgASgLMg4udjEuT3BTZWxlY3RvchIOCgZsYXN0X24YAiABKAMibQoWUmVzdG9yZVNuYXBzaG90UmVxdWVzdBIPCgdwbGFuX2lkGAEgASgJEg8KB3JlcG9faWQYBSABKAkSEwoLc25hcHNob3RfaWQYAiABKAkSDAoEcGF0aBgDIAEoCRIOCgZ0YXJnZXQYBCABKAkiUAoYTGlzdFNuYXBzaG90RmlsZXNSZXF1ZXN0EhEKCXJlcG9fZ3VpZBgBIAEoCRITCgtzbmFwc2hvdF9pZBgCIAEoCRIMCgRwYXRoGAMgASgJIkcKGUxpc3RTbmFwc2hvdEZpbGVzUmVzcG9uc2USDAoEcGF0aBgBIAEoCRIcCgdlbnRyaWVzGAIgAygLMgsudjEuTHNFbnRyeSIdCg5Mb2dEYXRhUmVxdWVzdBILCgNyZWYYASABKAkiOQoVR2V0RG93bmxvYWRVUkxSZXF1ZXN0Eg0KBW9wX2lkGAEgASgDEhEKCWZpbGVfcGF0aBgCIAEoCSKWAQoHTHNFbnRyeRIMCgRuYW1lGAEgASgJEgwKBHR5cGUYAiABKAkSDAoEcGF0aBgDIAEoCRILCgN1aWQYBCABKAMSCwoDZ2lkGAUgASgDEgwKBHNpemUYBiABKAMSDAoEbW9kZRgHIAEoAxINCgVtdGltZRgIIAEoCRINCgVhdGltZRgJIAEoCRINCgVjdGltZRgKIAEoCSI1ChFSdW5Db21tYW5kUmVxdWVzdBIPCgdyZXBvX2lkGAEgASgJEg8KB2NvbW1hbmQYAiABKAkitQUKGFN1bW1hcnlEYXNoYm9hcmRSZXNwb25zZRI8Cg5yZXBvX3N1bW1hcmllcxgBIAMoCzIkLnYxLlN1bW1hcnlEYXNoYm9hcmRSZXNwb25zZS5TdW1tYXJ5EjwKDnBsYW5fc3VtbWFyaWVzGAIgAygLMiQudjEuU3VtbWFyeURhc2hib2FyZFJlc3BvbnNlLlN1bW1hcnkSEwoLY29uZmlnX3BhdGgYCiABKAkSEQoJZGF0YV9wYXRoGAsgASgJGu4CCgdTdW1tYXJ5EgoKAmlkGAEgASgJEh0KFWJhY2t1cHNfZmFpbGVkXzMwZGF5cxgCIAEoAxIjChtiYWNrdXBzX3dhcm5pbmdfbGFzdF8zMGRheXMYAyABKAMSIwobYmFja3Vwc19zdWNjZXNzX2xhc3RfMzBkYXlzGAQgASgDEiEKGWJ5dGVzX3NjYW5uZWRfbGFzdF8zMGRheXMYBSABKAMSHwoXYnl0ZXNfYWRkZWRfbGFzdF8zMGRheXMYBiABKAMSFwoPdG90YWxfc25hcHNob3RzGAcgASgDEhkKEWJ5dGVzX3NjYW5uZWRfYXZnGAggASgDEhcKD2J5dGVzX2FkZGVkX2F2ZxgJIAEoAxIbChNuZXh0X2JhY2t1cF90aW1lX21zGAogASgDEkAKDnJlY2VudF9iYWNrdXBzGAsgASgLMigudjEuU3VtbWFyeURhc2hib2FyZFJlc3BvbnNlLkJhY2t1cENoYXJ0GoMBCgtCYWNrdXBDaGFydBIPCgdmbG93X2lkGAEgAygDEhQKDHRpbWVzdGFtcF9tcxgCIAMoAxITCgtkdXJhdGlvbl9tcxgDIAMoAxIjCgZzdGF0dXMYBCADKA4yEy52MS5PcGVyYXRpb25TdGF0dXMSEwoLYnl0ZXNfYWRkZWQYBSADKAMyrwkKCEJhY2tyZXN0EjEKCUdldENvbmZpZxIWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eRoKLnYxLkNvbmZpZyIAEiUKCVNldENvbmZpZxIKLnYxLkNvbmZpZxoKLnYxLkNvbmZpZyIAEi8KD0NoZWNrUmVwb0V4aXN0cxIILnYxLlJlcG8aEC50eXBlcy5Cb29sVmFsdWUiABIhCgdBZGRSZXBvEggudjEuUmVwbxoKLnYxLkNvbmZpZyIAEi4KClJlbW92ZVJlcG8SEi50eXBlcy5TdHJpbmdWYWx1ZRoKLnYxLkNvbmZpZyIAEkQKEkdldE9wZXJhdGlvbkV2ZW50cxIWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eRoSLnYxLk9wZXJhdGlvbkV2ZW50IgAwARI+Cg1HZXRPcGVyYXRpb25zEhgudjEuR2V0T3BlcmF0aW9uc1JlcXVlc3QaES52MS5PcGVyYXRpb25MaXN0IgASQwoNTGlzdFNuYXBzaG90cxIYLnYxLkxpc3RTbmFwc2hvdHNSZXF1ZXN0GhYudjEuUmVzdGljU25hcHNob3RMaXN0IgASUgoRTGlzdFNuYXBzaG90RmlsZXMSHC52MS5MaXN0U25hcHNob3RGaWxlc1JlcXVlc3QaHS52MS5MaXN0U25hcHNob3RGaWxlc1Jlc3BvbnNlIgASNgoGQmFja3VwEhIudHlwZXMuU3RyaW5nVmFsdWUaFi5nb29nbGUucHJvdG9idWYuRW1wdHkiABI9CgpEb1JlcG9UYXNrEhUudjEuRG9SZXBvVGFza1JlcXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkiABI1CgZGb3JnZXQSES52MS5Gb3JnZXRSZXF1ZXN0GhYuZ29vZ2xlLnByb3RvYnVmLkVtcHR5IgASPwoHUmVzdG9yZRIaLnYxLlJlc3RvcmVTbmFwc2hvdFJlcXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkiABI1CgZDYW5jZWwSES50eXBlcy5JbnQ2NFZhbHVlGhYuZ29vZ2xlLnByb3RvYnVmLkVtcHR5IgASNAoHR2V0TG9ncxISLnYxLkxvZ0RhdGFSZXF1ZXN0GhEudHlwZXMuQnl0ZXNWYWx1ZSIAMAESOAoKUnVuQ29tbWFuZBIVLnYxLlJ1bkNvbW1hbmRSZXF1ZXN0GhEudHlwZXMuSW50NjRWYWx1ZSIAEkEKDkdldERvd25sb2FkVVJMEhkudjEuR2V0RG93bmxvYWRVUkxSZXF1ZXN0GhIudHlwZXMuU3RyaW5nVmFsdWUiABJBCgxDbGVhckhpc3RvcnkSFy52MS5DbGVhckhpc3RvcnlSZXF1ZXN0GhYuZ29vZ2xlLnByb3RvYnVmLkVtcHR5IgASOwoQUGF0aEF1dG9jb21wbGV0ZRISLnR5cGVzLlN0cmluZ1ZhbHVlGhEudHlwZXMuU3RyaW5nTGlzdCIAEk0KE0dldFN1bW1hcnlEYXNoYm9hcmQSFi5nb29nbGUucHJvdG9idWYuRW1wdHkaHC52MS5TdW1tYXJ5RGFzaGJvYXJkUmVzcG9uc2UiAEIsWipnaXRodWIuY29tL2dhcmV0aGdlb3JnZS9iYWNrcmVzdC9nZW4vZ28vdjFiBnByb3RvMw", [file_v1_config, file_v1_restic, file_v1_operations, file_types_value, file_google_protobuf_empty, file_google_api_annotations]);
|
||||
|
||||
/**
|
||||
* OpSelector is a message that can be used to select operations e.g. by query.
|
||||
@@ -338,6 +338,28 @@ export type LogDataRequest = Message<"v1.LogDataRequest"> & {
|
||||
export const LogDataRequestSchema: GenMessage<LogDataRequest> = /*@__PURE__*/
|
||||
messageDesc(file_v1_service, 9);
|
||||
|
||||
/**
|
||||
* @generated from message v1.GetDownloadURLRequest
|
||||
*/
|
||||
export type GetDownloadURLRequest = Message<"v1.GetDownloadURLRequest"> & {
|
||||
/**
|
||||
* @generated from field: int64 op_id = 1;
|
||||
*/
|
||||
opId: bigint;
|
||||
|
||||
/**
|
||||
* @generated from field: string file_path = 2;
|
||||
*/
|
||||
filePath: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes the message v1.GetDownloadURLRequest.
|
||||
* Use `create(GetDownloadURLRequestSchema)` to create a new message.
|
||||
*/
|
||||
export const GetDownloadURLRequestSchema: GenMessage<GetDownloadURLRequest> = /*@__PURE__*/
|
||||
messageDesc(file_v1_service, 10);
|
||||
|
||||
/**
|
||||
* @generated from message v1.LsEntry
|
||||
*/
|
||||
@@ -398,7 +420,7 @@ export type LsEntry = Message<"v1.LsEntry"> & {
|
||||
* Use `create(LsEntrySchema)` to create a new message.
|
||||
*/
|
||||
export const LsEntrySchema: GenMessage<LsEntry> = /*@__PURE__*/
|
||||
messageDesc(file_v1_service, 10);
|
||||
messageDesc(file_v1_service, 11);
|
||||
|
||||
/**
|
||||
* @generated from message v1.RunCommandRequest
|
||||
@@ -420,7 +442,7 @@ export type RunCommandRequest = Message<"v1.RunCommandRequest"> & {
|
||||
* Use `create(RunCommandRequestSchema)` to create a new message.
|
||||
*/
|
||||
export const RunCommandRequestSchema: GenMessage<RunCommandRequest> = /*@__PURE__*/
|
||||
messageDesc(file_v1_service, 11);
|
||||
messageDesc(file_v1_service, 12);
|
||||
|
||||
/**
|
||||
* @generated from message v1.SummaryDashboardResponse
|
||||
@@ -452,7 +474,7 @@ export type SummaryDashboardResponse = Message<"v1.SummaryDashboardResponse"> &
|
||||
* Use `create(SummaryDashboardResponseSchema)` to create a new message.
|
||||
*/
|
||||
export const SummaryDashboardResponseSchema: GenMessage<SummaryDashboardResponse> = /*@__PURE__*/
|
||||
messageDesc(file_v1_service, 12);
|
||||
messageDesc(file_v1_service, 13);
|
||||
|
||||
/**
|
||||
* @generated from message v1.SummaryDashboardResponse.Summary
|
||||
@@ -523,7 +545,7 @@ export type SummaryDashboardResponse_Summary = Message<"v1.SummaryDashboardRespo
|
||||
* Use `create(SummaryDashboardResponse_SummarySchema)` to create a new message.
|
||||
*/
|
||||
export const SummaryDashboardResponse_SummarySchema: GenMessage<SummaryDashboardResponse_Summary> = /*@__PURE__*/
|
||||
messageDesc(file_v1_service, 12, 0);
|
||||
messageDesc(file_v1_service, 13, 0);
|
||||
|
||||
/**
|
||||
* @generated from message v1.SummaryDashboardResponse.BackupChart
|
||||
@@ -560,7 +582,7 @@ export type SummaryDashboardResponse_BackupChart = Message<"v1.SummaryDashboardR
|
||||
* Use `create(SummaryDashboardResponse_BackupChartSchema)` to create a new message.
|
||||
*/
|
||||
export const SummaryDashboardResponse_BackupChartSchema: GenMessage<SummaryDashboardResponse_BackupChart> = /*@__PURE__*/
|
||||
messageDesc(file_v1_service, 12, 1);
|
||||
messageDesc(file_v1_service, 13, 1);
|
||||
|
||||
/**
|
||||
* @generated from service v1.Backrest
|
||||
@@ -709,13 +731,13 @@ export const Backrest: GenService<{
|
||||
output: typeof Int64ValueSchema;
|
||||
},
|
||||
/**
|
||||
* GetDownloadURL returns a signed download URL given a forget operation ID.
|
||||
* GetDownloadURL returns a signed download URL given an operation ID and file path.
|
||||
*
|
||||
* @generated from rpc v1.Backrest.GetDownloadURL
|
||||
*/
|
||||
getDownloadURL: {
|
||||
methodKind: "unary";
|
||||
input: typeof Int64ValueSchema;
|
||||
input: typeof GetDownloadURLRequestSchema;
|
||||
output: typeof StringValueSchema;
|
||||
},
|
||||
/**
|
||||
|
||||
Generated
+11
-2
@@ -151,13 +151,15 @@
|
||||
"version": "2.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.10.0.tgz",
|
||||
"integrity": "sha512-fdRs9PSrBF7QUntpZpq6BTw58fhgGJojgg39m9oFOJGZT+nip9b0so5cYY1oWl5pvemDLr0cPPsH46vwThEbpQ==",
|
||||
"license": "(Apache-2.0 AND BSD-3-Clause)"
|
||||
"license": "(Apache-2.0 AND BSD-3-Clause)",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@connectrpc/connect": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@connectrpc/connect/-/connect-2.1.0.tgz",
|
||||
"integrity": "sha512-xhiwnYlJNHzmFsRw+iSPIwXR/xweTvTw8x5HiwWp10sbVtd4OpOXbRgE7V58xs1EC17fzusF1f5uOAy24OkBuA==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"peerDependencies": {
|
||||
"@bufbuild/protobuf": "^2.7.0"
|
||||
}
|
||||
@@ -515,6 +517,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@parcel/core/-/core-2.16.0.tgz",
|
||||
"integrity": "sha512-erH9GdLe8Boie0mCO8hXn8Qt/pCACsOFlKp8UHNMlPaizUtCDkCOQqwmSi+VyrJ3dMMCOc/qBwTSGAJaJE8/Kw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@mischnic/json-sourcemap": "^0.1.1",
|
||||
"@parcel/cache": "2.16.0",
|
||||
@@ -2487,6 +2490,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.26.tgz",
|
||||
"integrity": "sha512-RFA/bURkcKzx/X9oumPG9Vp3D3JUgus/d0b67KB0t5S/raciymilkOa66olh78MUI92QLbEJevO7rvqU/kjwKA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/prop-types": "*",
|
||||
"csstype": "^3.0.2"
|
||||
@@ -2543,6 +2547,7 @@
|
||||
"resolved": "https://registry.npmjs.org/antd/-/antd-5.27.6.tgz",
|
||||
"integrity": "sha512-70HrjVbzDXvtiUQ5MP1XdNudr/wGAk9Ivaemk6f36yrAeJurJSmZ8KngOIilolLRHdGuNc6/Vk+4T1OZpSjpag==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@ant-design/colors": "^7.2.1",
|
||||
"@ant-design/cssinjs": "^1.23.0",
|
||||
@@ -2693,6 +2698,7 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.8.19",
|
||||
"caniuse-lite": "^1.0.30001751",
|
||||
@@ -3094,7 +3100,8 @@
|
||||
"version": "1.11.18",
|
||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.18.tgz",
|
||||
"integrity": "sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/decimal.js-light": {
|
||||
"version": "2.5.1",
|
||||
@@ -4677,6 +4684,7 @@
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
||||
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
},
|
||||
@@ -4689,6 +4697,7 @@
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
|
||||
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"scheduler": "^0.23.2"
|
||||
|
||||
@@ -458,7 +458,7 @@ const RestoreOperationStatus = ({ operation }: { operation: Operation }) => {
|
||||
type="link"
|
||||
onClick={() => {
|
||||
backrestService
|
||||
.getDownloadURL({ value: operation.id })
|
||||
.getDownloadURL({ opId: operation.id!, filePath: "" })
|
||||
.then((resp) => {
|
||||
window.open(resp.value, "_blank");
|
||||
})
|
||||
|
||||
@@ -133,13 +133,19 @@ export const SnapshotBrowser = ({
|
||||
path += "/";
|
||||
}
|
||||
|
||||
const resp = await backrestService.listSnapshotFiles(
|
||||
create(ListSnapshotFilesRequestSchema, {
|
||||
path,
|
||||
repoGuid,
|
||||
snapshotId,
|
||||
})
|
||||
);
|
||||
let resp: ListSnapshotFilesResponse;
|
||||
try {
|
||||
resp = await backrestService.listSnapshotFiles(
|
||||
create(ListSnapshotFilesRequestSchema, {
|
||||
path,
|
||||
repoGuid,
|
||||
snapshotId,
|
||||
})
|
||||
);
|
||||
} catch (e: any) {
|
||||
alertApi?.error("Failed to load snapshot files: " + e.message);
|
||||
return;
|
||||
}
|
||||
|
||||
setTreeData((treeData) => {
|
||||
let toUpdate: DataNode | null = null;
|
||||
@@ -234,15 +240,10 @@ const FileNode = ({
|
||||
label: "Download",
|
||||
onClick: () => {
|
||||
backrestService
|
||||
.getDownloadURL({ value: snapshotOpId })
|
||||
.getDownloadURL({ opId: snapshotOpId!, filePath: entry.path! })
|
||||
.then((resp) => {
|
||||
const encodePathKeepSlashes = (p: string) =>
|
||||
p
|
||||
.split("/")
|
||||
.map((seg) => encodeURIComponent(seg))
|
||||
.join("/");
|
||||
window.open(
|
||||
resp.value + encodePathKeepSlashes(entry.path!),
|
||||
resp.value,
|
||||
"_blank"
|
||||
);
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user