From 25924b6197c870f9dfc1e04f5be39377251e7f2d Mon Sep 17 00:00:00 2001 From: garethgeorge Date: Mon, 29 Jan 2024 20:33:22 -0800 Subject: [PATCH] feat: implement discord hook type --- gen/go/v1/config.pb.go | 43 +++++--- gen/go/v1/service.pb.go | 40 +++---- gen/go/v1/service_grpc.pb.go | 64 +++++------ gen/go/v1/v1connect/service.connect.go | 116 ++++++++++---------- go.mod | 3 + go.sum | 6 + internal/api/server.go | 2 +- internal/hook/commandhook.go | 4 +- internal/hook/discordhook.go | 30 +++++ internal/hook/hook.go | 18 ++- internal/hook/hookvars.go | 72 +++++++++--- internal/hook/httputil.go | 25 +++++ internal/orchestrator/taskbackup.go | 2 +- internal/orchestrator/taskcollectgarbage.go | 2 +- proto/v1/config.proto | 2 + proto/v1/service.proto | 4 +- webui/gen/ts/v1/config_pb.ts | 8 ++ webui/gen/ts/v1/service_connect.ts | 8 +- webui/src/components/HooksFormList.tsx | 62 +++++++---- webui/src/components/OperationList.tsx | 11 +- 20 files changed, 339 insertions(+), 183 deletions(-) create mode 100644 internal/hook/discordhook.go create mode 100644 internal/hook/httputil.go diff --git a/gen/go/v1/config.pb.go b/gen/go/v1/config.pb.go index 83fac855..773c4a80 100644 --- a/gen/go/v1/config.pb.go +++ b/gen/go/v1/config.pb.go @@ -792,6 +792,7 @@ type Hook_Discord struct { unknownFields protoimpl.UnknownFields WebhookUrl string `protobuf:"bytes,1,opt,name=webhook_url,json=webhookUrl,proto3" json:"webhook_url,omitempty"` + Template string `protobuf:"bytes,2,opt,name=template,proto3" json:"template,omitempty"` // template for the webhook payload. } func (x *Hook_Discord) Reset() { @@ -833,6 +834,13 @@ func (x *Hook_Discord) GetWebhookUrl() string { return "" } +func (x *Hook_Discord) GetTemplate() string { + if x != nil { + return x.Template + } + return "" +} + var File_v1_config_proto protoreflect.FileDescriptor var file_v1_config_proto_rawDesc = []byte{ @@ -905,7 +913,7 @@ var file_v1_config_proto_rawDesc = []byte{ 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x62, 0x63, 0x72, 0x79, 0x70, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0e, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x42, 0x63, 0x72, 0x79, 0x70, 0x74, 0x42, 0x0a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, - 0x64, 0x22, 0x88, 0x04, 0x0a, 0x04, 0x48, 0x6f, 0x6f, 0x6b, 0x12, 0x32, 0x0a, 0x0a, 0x63, 0x6f, + 0x64, 0x22, 0xa4, 0x04, 0x0a, 0x04, 0x48, 0x6f, 0x6f, 0x6b, 0x12, 0x32, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x6f, 0x6b, 0x2e, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x39, @@ -925,23 +933,24 @@ var file_v1_config_proto_rawDesc = []byte{ 0x6d, 0x61, 0x6e, 0x64, 0x1a, 0x2a, 0x0a, 0x07, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x12, 0x1f, 0x0a, 0x0b, 0x77, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x77, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x55, 0x72, 0x6c, - 0x1a, 0x2a, 0x0a, 0x07, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x77, + 0x1a, 0x46, 0x0a, 0x07, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x77, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0a, 0x77, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x55, 0x72, 0x6c, 0x22, 0x93, 0x01, 0x0a, - 0x09, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x15, 0x0a, 0x11, 0x43, 0x4f, - 0x4e, 0x44, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, - 0x00, 0x12, 0x17, 0x0a, 0x13, 0x43, 0x4f, 0x4e, 0x44, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x41, - 0x4e, 0x59, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, 0x43, 0x4f, - 0x4e, 0x44, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x4e, 0x41, 0x50, 0x53, 0x48, 0x4f, 0x54, - 0x5f, 0x53, 0x54, 0x41, 0x52, 0x54, 0x10, 0x02, 0x12, 0x1a, 0x0a, 0x16, 0x43, 0x4f, 0x4e, 0x44, - 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x4e, 0x41, 0x50, 0x53, 0x48, 0x4f, 0x54, 0x5f, 0x45, - 0x4e, 0x44, 0x10, 0x03, 0x12, 0x1c, 0x0a, 0x18, 0x43, 0x4f, 0x4e, 0x44, 0x49, 0x54, 0x49, 0x4f, - 0x4e, 0x5f, 0x53, 0x4e, 0x41, 0x50, 0x53, 0x48, 0x4f, 0x54, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, - 0x10, 0x04, 0x42, 0x08, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x2c, 0x5a, 0x2a, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x61, 0x72, 0x65, 0x74, - 0x68, 0x67, 0x65, 0x6f, 0x72, 0x67, 0x65, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x72, 0x65, 0x73, 0x74, - 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x52, 0x0a, 0x77, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x55, 0x72, 0x6c, 0x12, 0x1a, 0x0a, 0x08, + 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x22, 0x93, 0x01, 0x0a, 0x09, 0x43, 0x6f, 0x6e, + 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x15, 0x0a, 0x11, 0x43, 0x4f, 0x4e, 0x44, 0x49, 0x54, + 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x17, 0x0a, + 0x13, 0x43, 0x4f, 0x4e, 0x44, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x41, 0x4e, 0x59, 0x5f, 0x45, + 0x52, 0x52, 0x4f, 0x52, 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, 0x43, 0x4f, 0x4e, 0x44, 0x49, 0x54, + 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x4e, 0x41, 0x50, 0x53, 0x48, 0x4f, 0x54, 0x5f, 0x53, 0x54, 0x41, + 0x52, 0x54, 0x10, 0x02, 0x12, 0x1a, 0x0a, 0x16, 0x43, 0x4f, 0x4e, 0x44, 0x49, 0x54, 0x49, 0x4f, + 0x4e, 0x5f, 0x53, 0x4e, 0x41, 0x50, 0x53, 0x48, 0x4f, 0x54, 0x5f, 0x45, 0x4e, 0x44, 0x10, 0x03, + 0x12, 0x1c, 0x0a, 0x18, 0x43, 0x4f, 0x4e, 0x44, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x4e, + 0x41, 0x50, 0x53, 0x48, 0x4f, 0x54, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x04, 0x42, 0x08, + 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x2c, 0x5a, 0x2a, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x61, 0x72, 0x65, 0x74, 0x68, 0x67, 0x65, 0x6f, + 0x72, 0x67, 0x65, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x72, 0x65, 0x73, 0x74, 0x2f, 0x67, 0x65, 0x6e, + 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/gen/go/v1/service.pb.go b/gen/go/v1/service.pb.go index be0284ad..229007b2 100644 --- a/gen/go/v1/service.pb.go +++ b/gen/go/v1/service.pb.go @@ -762,7 +762,7 @@ var file_v1_service_proto_rawDesc = []byte{ 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, 0xb7, 0x08, 0x0a, 0x08, 0x42, 0x61, 0x63, 0x6b, 0x72, 0x65, 0x73, 0x74, 0x12, 0x31, + 0x65, 0x32, 0xba, 0x08, 0x0a, 0x08, 0x42, 0x61, 0x63, 0x6b, 0x72, 0x65, 0x73, 0x74, 0x12, 0x31, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, @@ -817,23 +817,23 @@ var file_v1_service_proto_rawDesc = []byte{ 0x00, 0x12, 0x35, 0x0a, 0x06, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x12, 0x11, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4f, - 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x12, 0x18, 0x2e, 0x76, - 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x42, - 0x79, 0x74, 0x65, 0x73, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x0c, 0x43, - 0x6c, 0x65, 0x61, 0x72, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x17, 0x2e, 0x76, 0x31, - 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x3b, - 0x0a, 0x10, 0x50, 0x61, 0x74, 0x68, 0x41, 0x75, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, - 0x74, 0x65, 0x12, 0x12, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, - 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x11, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x53, - 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x00, 0x42, 0x2c, 0x5a, 0x2a, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x61, 0x72, 0x65, 0x74, 0x68, - 0x67, 0x65, 0x6f, 0x72, 0x67, 0x65, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x72, 0x65, 0x73, 0x74, 0x2f, - 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x44, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x42, + 0x69, 0x67, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x12, + 0x18, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, + 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x74, 0x79, 0x70, 0x65, + 0x73, 0x2e, 0x42, 0x79, 0x74, 0x65, 0x73, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x00, 0x12, 0x41, + 0x0a, 0x0c, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x17, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, + 0x00, 0x12, 0x3b, 0x0a, 0x10, 0x50, 0x61, 0x74, 0x68, 0x41, 0x75, 0x74, 0x6f, 0x63, 0x6f, 0x6d, + 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x12, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x53, 0x74, + 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x11, 0x2e, 0x74, 0x79, 0x70, 0x65, + 0x73, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x00, 0x42, 0x2c, + 0x5a, 0x2a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x61, 0x72, + 0x65, 0x74, 0x68, 0x67, 0x65, 0x6f, 0x72, 0x67, 0x65, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x72, 0x65, + 0x73, 0x74, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -888,7 +888,7 @@ var file_v1_service_proto_depIdxs = []int32{ 13, // 13: v1.Backrest.Unlock:input_type -> types.StringValue 13, // 14: v1.Backrest.Stats:input_type -> types.StringValue 14, // 15: v1.Backrest.Cancel:input_type -> types.Int64Value - 8, // 16: v1.Backrest.GetOperationData:input_type -> v1.OperationDataRequest + 8, // 16: v1.Backrest.GetBigOperationData:input_type -> v1.OperationDataRequest 2, // 17: v1.Backrest.ClearHistory:input_type -> v1.ClearHistoryRequest 13, // 18: v1.Backrest.PathAutocomplete:input_type -> types.StringValue 11, // 19: v1.Backrest.GetConfig:output_type -> v1.Config @@ -906,7 +906,7 @@ var file_v1_service_proto_depIdxs = []int32{ 10, // 31: v1.Backrest.Unlock:output_type -> google.protobuf.Empty 10, // 32: v1.Backrest.Stats:output_type -> google.protobuf.Empty 10, // 33: v1.Backrest.Cancel:output_type -> google.protobuf.Empty - 18, // 34: v1.Backrest.GetOperationData:output_type -> types.BytesValue + 18, // 34: v1.Backrest.GetBigOperationData:output_type -> types.BytesValue 10, // 35: v1.Backrest.ClearHistory:output_type -> google.protobuf.Empty 19, // 36: v1.Backrest.PathAutocomplete:output_type -> types.StringList 19, // [19:37] is the sub-list for method output_type diff --git a/gen/go/v1/service_grpc.pb.go b/gen/go/v1/service_grpc.pb.go index 0abcb530..311a0d8d 100644 --- a/gen/go/v1/service_grpc.pb.go +++ b/gen/go/v1/service_grpc.pb.go @@ -21,24 +21,24 @@ import ( const _ = grpc.SupportPackageIsVersion7 const ( - Backrest_GetConfig_FullMethodName = "/v1.Backrest/GetConfig" - Backrest_SetConfig_FullMethodName = "/v1.Backrest/SetConfig" - Backrest_AddRepo_FullMethodName = "/v1.Backrest/AddRepo" - Backrest_GetOperationEvents_FullMethodName = "/v1.Backrest/GetOperationEvents" - Backrest_GetOperations_FullMethodName = "/v1.Backrest/GetOperations" - Backrest_ListSnapshots_FullMethodName = "/v1.Backrest/ListSnapshots" - Backrest_ListSnapshotFiles_FullMethodName = "/v1.Backrest/ListSnapshotFiles" - Backrest_IndexSnapshots_FullMethodName = "/v1.Backrest/IndexSnapshots" - Backrest_Backup_FullMethodName = "/v1.Backrest/Backup" - Backrest_Prune_FullMethodName = "/v1.Backrest/Prune" - Backrest_Forget_FullMethodName = "/v1.Backrest/Forget" - Backrest_Restore_FullMethodName = "/v1.Backrest/Restore" - Backrest_Unlock_FullMethodName = "/v1.Backrest/Unlock" - Backrest_Stats_FullMethodName = "/v1.Backrest/Stats" - Backrest_Cancel_FullMethodName = "/v1.Backrest/Cancel" - Backrest_GetOperationData_FullMethodName = "/v1.Backrest/GetOperationData" - Backrest_ClearHistory_FullMethodName = "/v1.Backrest/ClearHistory" - Backrest_PathAutocomplete_FullMethodName = "/v1.Backrest/PathAutocomplete" + Backrest_GetConfig_FullMethodName = "/v1.Backrest/GetConfig" + Backrest_SetConfig_FullMethodName = "/v1.Backrest/SetConfig" + Backrest_AddRepo_FullMethodName = "/v1.Backrest/AddRepo" + Backrest_GetOperationEvents_FullMethodName = "/v1.Backrest/GetOperationEvents" + Backrest_GetOperations_FullMethodName = "/v1.Backrest/GetOperations" + Backrest_ListSnapshots_FullMethodName = "/v1.Backrest/ListSnapshots" + Backrest_ListSnapshotFiles_FullMethodName = "/v1.Backrest/ListSnapshotFiles" + Backrest_IndexSnapshots_FullMethodName = "/v1.Backrest/IndexSnapshots" + Backrest_Backup_FullMethodName = "/v1.Backrest/Backup" + Backrest_Prune_FullMethodName = "/v1.Backrest/Prune" + Backrest_Forget_FullMethodName = "/v1.Backrest/Forget" + Backrest_Restore_FullMethodName = "/v1.Backrest/Restore" + Backrest_Unlock_FullMethodName = "/v1.Backrest/Unlock" + Backrest_Stats_FullMethodName = "/v1.Backrest/Stats" + Backrest_Cancel_FullMethodName = "/v1.Backrest/Cancel" + Backrest_GetBigOperationData_FullMethodName = "/v1.Backrest/GetBigOperationData" + Backrest_ClearHistory_FullMethodName = "/v1.Backrest/ClearHistory" + Backrest_PathAutocomplete_FullMethodName = "/v1.Backrest/PathAutocomplete" ) // BackrestClient is the client API for Backrest service. @@ -68,8 +68,8 @@ type BackrestClient interface { Stats(ctx context.Context, in *types.StringValue, opts ...grpc.CallOption) (*emptypb.Empty, error) // Cancel attempts to cancel a task with the given operation ID. Not guaranteed to succeed. Cancel(ctx context.Context, in *types.Int64Value, opts ...grpc.CallOption) (*emptypb.Empty, error) - // GetOperationData returns the keyed large data for the given operation. - GetOperationData(ctx context.Context, in *OperationDataRequest, opts ...grpc.CallOption) (*types.BytesValue, error) + // GetBigOperationData returns the keyed large data for the given operation. + GetBigOperationData(ctx context.Context, in *OperationDataRequest, opts ...grpc.CallOption) (*types.BytesValue, 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. @@ -242,9 +242,9 @@ func (c *backrestClient) Cancel(ctx context.Context, in *types.Int64Value, opts return out, nil } -func (c *backrestClient) GetOperationData(ctx context.Context, in *OperationDataRequest, opts ...grpc.CallOption) (*types.BytesValue, error) { +func (c *backrestClient) GetBigOperationData(ctx context.Context, in *OperationDataRequest, opts ...grpc.CallOption) (*types.BytesValue, error) { out := new(types.BytesValue) - err := c.cc.Invoke(ctx, Backrest_GetOperationData_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, Backrest_GetBigOperationData_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -296,8 +296,8 @@ type BackrestServer interface { Stats(context.Context, *types.StringValue) (*emptypb.Empty, error) // Cancel attempts to cancel a task with the given operation ID. Not guaranteed to succeed. Cancel(context.Context, *types.Int64Value) (*emptypb.Empty, error) - // GetOperationData returns the keyed large data for the given operation. - GetOperationData(context.Context, *OperationDataRequest) (*types.BytesValue, error) + // GetBigOperationData returns the keyed large data for the given operation. + GetBigOperationData(context.Context, *OperationDataRequest) (*types.BytesValue, error) // Clears the history of operations ClearHistory(context.Context, *ClearHistoryRequest) (*emptypb.Empty, error) // PathAutocomplete provides path autocompletion options for a given filesystem path. @@ -354,8 +354,8 @@ func (UnimplementedBackrestServer) Stats(context.Context, *types.StringValue) (* func (UnimplementedBackrestServer) Cancel(context.Context, *types.Int64Value) (*emptypb.Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method Cancel not implemented") } -func (UnimplementedBackrestServer) GetOperationData(context.Context, *OperationDataRequest) (*types.BytesValue, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetOperationData not implemented") +func (UnimplementedBackrestServer) GetBigOperationData(context.Context, *OperationDataRequest) (*types.BytesValue, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetBigOperationData not implemented") } func (UnimplementedBackrestServer) ClearHistory(context.Context, *ClearHistoryRequest) (*emptypb.Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method ClearHistory not implemented") @@ -649,20 +649,20 @@ func _Backrest_Cancel_Handler(srv interface{}, ctx context.Context, dec func(int return interceptor(ctx, in, info, handler) } -func _Backrest_GetOperationData_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { +func _Backrest_GetBigOperationData_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(OperationDataRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(BackrestServer).GetOperationData(ctx, in) + return srv.(BackrestServer).GetBigOperationData(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: Backrest_GetOperationData_FullMethodName, + FullMethod: Backrest_GetBigOperationData_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(BackrestServer).GetOperationData(ctx, req.(*OperationDataRequest)) + return srv.(BackrestServer).GetBigOperationData(ctx, req.(*OperationDataRequest)) } return interceptor(ctx, in, info, handler) } @@ -767,8 +767,8 @@ var Backrest_ServiceDesc = grpc.ServiceDesc{ Handler: _Backrest_Cancel_Handler, }, { - MethodName: "GetOperationData", - Handler: _Backrest_GetOperationData_Handler, + MethodName: "GetBigOperationData", + Handler: _Backrest_GetBigOperationData_Handler, }, { MethodName: "ClearHistory", diff --git a/gen/go/v1/v1connect/service.connect.go b/gen/go/v1/v1connect/service.connect.go index 880170ac..f18d457b 100644 --- a/gen/go/v1/v1connect/service.connect.go +++ b/gen/go/v1/v1connect/service.connect.go @@ -67,9 +67,9 @@ const ( BackrestStatsProcedure = "/v1.Backrest/Stats" // BackrestCancelProcedure is the fully-qualified name of the Backrest's Cancel RPC. BackrestCancelProcedure = "/v1.Backrest/Cancel" - // BackrestGetOperationDataProcedure is the fully-qualified name of the Backrest's GetOperationData - // RPC. - BackrestGetOperationDataProcedure = "/v1.Backrest/GetOperationData" + // BackrestGetBigOperationDataProcedure is the fully-qualified name of the Backrest's + // GetBigOperationData RPC. + BackrestGetBigOperationDataProcedure = "/v1.Backrest/GetBigOperationData" // BackrestClearHistoryProcedure is the fully-qualified name of the Backrest's ClearHistory RPC. BackrestClearHistoryProcedure = "/v1.Backrest/ClearHistory" // BackrestPathAutocompleteProcedure is the fully-qualified name of the Backrest's PathAutocomplete @@ -79,25 +79,25 @@ const ( // These variables are the protoreflect.Descriptor objects for the RPCs defined in this package. var ( - backrestServiceDescriptor = v1.File_v1_service_proto.Services().ByName("Backrest") - backrestGetConfigMethodDescriptor = backrestServiceDescriptor.Methods().ByName("GetConfig") - backrestSetConfigMethodDescriptor = backrestServiceDescriptor.Methods().ByName("SetConfig") - backrestAddRepoMethodDescriptor = backrestServiceDescriptor.Methods().ByName("AddRepo") - backrestGetOperationEventsMethodDescriptor = backrestServiceDescriptor.Methods().ByName("GetOperationEvents") - backrestGetOperationsMethodDescriptor = backrestServiceDescriptor.Methods().ByName("GetOperations") - backrestListSnapshotsMethodDescriptor = backrestServiceDescriptor.Methods().ByName("ListSnapshots") - backrestListSnapshotFilesMethodDescriptor = backrestServiceDescriptor.Methods().ByName("ListSnapshotFiles") - backrestIndexSnapshotsMethodDescriptor = backrestServiceDescriptor.Methods().ByName("IndexSnapshots") - backrestBackupMethodDescriptor = backrestServiceDescriptor.Methods().ByName("Backup") - backrestPruneMethodDescriptor = backrestServiceDescriptor.Methods().ByName("Prune") - backrestForgetMethodDescriptor = backrestServiceDescriptor.Methods().ByName("Forget") - backrestRestoreMethodDescriptor = backrestServiceDescriptor.Methods().ByName("Restore") - backrestUnlockMethodDescriptor = backrestServiceDescriptor.Methods().ByName("Unlock") - backrestStatsMethodDescriptor = backrestServiceDescriptor.Methods().ByName("Stats") - backrestCancelMethodDescriptor = backrestServiceDescriptor.Methods().ByName("Cancel") - backrestGetOperationDataMethodDescriptor = backrestServiceDescriptor.Methods().ByName("GetOperationData") - backrestClearHistoryMethodDescriptor = backrestServiceDescriptor.Methods().ByName("ClearHistory") - backrestPathAutocompleteMethodDescriptor = backrestServiceDescriptor.Methods().ByName("PathAutocomplete") + backrestServiceDescriptor = v1.File_v1_service_proto.Services().ByName("Backrest") + backrestGetConfigMethodDescriptor = backrestServiceDescriptor.Methods().ByName("GetConfig") + backrestSetConfigMethodDescriptor = backrestServiceDescriptor.Methods().ByName("SetConfig") + backrestAddRepoMethodDescriptor = backrestServiceDescriptor.Methods().ByName("AddRepo") + backrestGetOperationEventsMethodDescriptor = backrestServiceDescriptor.Methods().ByName("GetOperationEvents") + backrestGetOperationsMethodDescriptor = backrestServiceDescriptor.Methods().ByName("GetOperations") + backrestListSnapshotsMethodDescriptor = backrestServiceDescriptor.Methods().ByName("ListSnapshots") + backrestListSnapshotFilesMethodDescriptor = backrestServiceDescriptor.Methods().ByName("ListSnapshotFiles") + backrestIndexSnapshotsMethodDescriptor = backrestServiceDescriptor.Methods().ByName("IndexSnapshots") + backrestBackupMethodDescriptor = backrestServiceDescriptor.Methods().ByName("Backup") + backrestPruneMethodDescriptor = backrestServiceDescriptor.Methods().ByName("Prune") + backrestForgetMethodDescriptor = backrestServiceDescriptor.Methods().ByName("Forget") + backrestRestoreMethodDescriptor = backrestServiceDescriptor.Methods().ByName("Restore") + backrestUnlockMethodDescriptor = backrestServiceDescriptor.Methods().ByName("Unlock") + backrestStatsMethodDescriptor = backrestServiceDescriptor.Methods().ByName("Stats") + backrestCancelMethodDescriptor = backrestServiceDescriptor.Methods().ByName("Cancel") + backrestGetBigOperationDataMethodDescriptor = backrestServiceDescriptor.Methods().ByName("GetBigOperationData") + backrestClearHistoryMethodDescriptor = backrestServiceDescriptor.Methods().ByName("ClearHistory") + backrestPathAutocompleteMethodDescriptor = backrestServiceDescriptor.Methods().ByName("PathAutocomplete") ) // BackrestClient is a client for the v1.Backrest service. @@ -125,8 +125,8 @@ type BackrestClient interface { Stats(context.Context, *connect.Request[types.StringValue]) (*connect.Response[emptypb.Empty], error) // Cancel attempts to cancel a task with the given operation ID. Not guaranteed to succeed. Cancel(context.Context, *connect.Request[types.Int64Value]) (*connect.Response[emptypb.Empty], error) - // GetOperationData returns the keyed large data for the given operation. - GetOperationData(context.Context, *connect.Request[v1.OperationDataRequest]) (*connect.Response[types.BytesValue], error) + // GetBigOperationData returns the keyed large data for the given operation. + GetBigOperationData(context.Context, *connect.Request[v1.OperationDataRequest]) (*connect.Response[types.BytesValue], 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. @@ -233,10 +233,10 @@ func NewBackrestClient(httpClient connect.HTTPClient, baseURL string, opts ...co connect.WithSchema(backrestCancelMethodDescriptor), connect.WithClientOptions(opts...), ), - getOperationData: connect.NewClient[v1.OperationDataRequest, types.BytesValue]( + getBigOperationData: connect.NewClient[v1.OperationDataRequest, types.BytesValue]( httpClient, - baseURL+BackrestGetOperationDataProcedure, - connect.WithSchema(backrestGetOperationDataMethodDescriptor), + baseURL+BackrestGetBigOperationDataProcedure, + connect.WithSchema(backrestGetBigOperationDataMethodDescriptor), connect.WithClientOptions(opts...), ), clearHistory: connect.NewClient[v1.ClearHistoryRequest, emptypb.Empty]( @@ -256,24 +256,24 @@ func NewBackrestClient(httpClient connect.HTTPClient, baseURL string, opts ...co // backrestClient implements BackrestClient. type backrestClient struct { - getConfig *connect.Client[emptypb.Empty, v1.Config] - setConfig *connect.Client[v1.Config, v1.Config] - addRepo *connect.Client[v1.Repo, v1.Config] - getOperationEvents *connect.Client[emptypb.Empty, v1.OperationEvent] - getOperations *connect.Client[v1.GetOperationsRequest, v1.OperationList] - listSnapshots *connect.Client[v1.ListSnapshotsRequest, v1.ResticSnapshotList] - listSnapshotFiles *connect.Client[v1.ListSnapshotFilesRequest, v1.ListSnapshotFilesResponse] - indexSnapshots *connect.Client[types.StringValue, emptypb.Empty] - backup *connect.Client[types.StringValue, emptypb.Empty] - prune *connect.Client[types.StringValue, emptypb.Empty] - forget *connect.Client[types.StringValue, emptypb.Empty] - restore *connect.Client[v1.RestoreSnapshotRequest, emptypb.Empty] - unlock *connect.Client[types.StringValue, emptypb.Empty] - stats *connect.Client[types.StringValue, emptypb.Empty] - cancel *connect.Client[types.Int64Value, emptypb.Empty] - getOperationData *connect.Client[v1.OperationDataRequest, types.BytesValue] - clearHistory *connect.Client[v1.ClearHistoryRequest, emptypb.Empty] - pathAutocomplete *connect.Client[types.StringValue, types.StringList] + getConfig *connect.Client[emptypb.Empty, v1.Config] + setConfig *connect.Client[v1.Config, v1.Config] + addRepo *connect.Client[v1.Repo, v1.Config] + getOperationEvents *connect.Client[emptypb.Empty, v1.OperationEvent] + getOperations *connect.Client[v1.GetOperationsRequest, v1.OperationList] + listSnapshots *connect.Client[v1.ListSnapshotsRequest, v1.ResticSnapshotList] + listSnapshotFiles *connect.Client[v1.ListSnapshotFilesRequest, v1.ListSnapshotFilesResponse] + indexSnapshots *connect.Client[types.StringValue, emptypb.Empty] + backup *connect.Client[types.StringValue, emptypb.Empty] + prune *connect.Client[types.StringValue, emptypb.Empty] + forget *connect.Client[types.StringValue, emptypb.Empty] + restore *connect.Client[v1.RestoreSnapshotRequest, emptypb.Empty] + unlock *connect.Client[types.StringValue, emptypb.Empty] + stats *connect.Client[types.StringValue, emptypb.Empty] + cancel *connect.Client[types.Int64Value, emptypb.Empty] + getBigOperationData *connect.Client[v1.OperationDataRequest, types.BytesValue] + clearHistory *connect.Client[v1.ClearHistoryRequest, emptypb.Empty] + pathAutocomplete *connect.Client[types.StringValue, types.StringList] } // GetConfig calls v1.Backrest.GetConfig. @@ -351,9 +351,9 @@ func (c *backrestClient) Cancel(ctx context.Context, req *connect.Request[types. return c.cancel.CallUnary(ctx, req) } -// GetOperationData calls v1.Backrest.GetOperationData. -func (c *backrestClient) GetOperationData(ctx context.Context, req *connect.Request[v1.OperationDataRequest]) (*connect.Response[types.BytesValue], error) { - return c.getOperationData.CallUnary(ctx, req) +// GetBigOperationData calls v1.Backrest.GetBigOperationData. +func (c *backrestClient) GetBigOperationData(ctx context.Context, req *connect.Request[v1.OperationDataRequest]) (*connect.Response[types.BytesValue], error) { + return c.getBigOperationData.CallUnary(ctx, req) } // ClearHistory calls v1.Backrest.ClearHistory. @@ -391,8 +391,8 @@ type BackrestHandler interface { Stats(context.Context, *connect.Request[types.StringValue]) (*connect.Response[emptypb.Empty], error) // Cancel attempts to cancel a task with the given operation ID. Not guaranteed to succeed. Cancel(context.Context, *connect.Request[types.Int64Value]) (*connect.Response[emptypb.Empty], error) - // GetOperationData returns the keyed large data for the given operation. - GetOperationData(context.Context, *connect.Request[v1.OperationDataRequest]) (*connect.Response[types.BytesValue], error) + // GetBigOperationData returns the keyed large data for the given operation. + GetBigOperationData(context.Context, *connect.Request[v1.OperationDataRequest]) (*connect.Response[types.BytesValue], 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. @@ -495,10 +495,10 @@ func NewBackrestHandler(svc BackrestHandler, opts ...connect.HandlerOption) (str connect.WithSchema(backrestCancelMethodDescriptor), connect.WithHandlerOptions(opts...), ) - backrestGetOperationDataHandler := connect.NewUnaryHandler( - BackrestGetOperationDataProcedure, - svc.GetOperationData, - connect.WithSchema(backrestGetOperationDataMethodDescriptor), + backrestGetBigOperationDataHandler := connect.NewUnaryHandler( + BackrestGetBigOperationDataProcedure, + svc.GetBigOperationData, + connect.WithSchema(backrestGetBigOperationDataMethodDescriptor), connect.WithHandlerOptions(opts...), ) backrestClearHistoryHandler := connect.NewUnaryHandler( @@ -545,8 +545,8 @@ func NewBackrestHandler(svc BackrestHandler, opts ...connect.HandlerOption) (str backrestStatsHandler.ServeHTTP(w, r) case BackrestCancelProcedure: backrestCancelHandler.ServeHTTP(w, r) - case BackrestGetOperationDataProcedure: - backrestGetOperationDataHandler.ServeHTTP(w, r) + case BackrestGetBigOperationDataProcedure: + backrestGetBigOperationDataHandler.ServeHTTP(w, r) case BackrestClearHistoryProcedure: backrestClearHistoryHandler.ServeHTTP(w, r) case BackrestPathAutocompleteProcedure: @@ -620,8 +620,8 @@ func (UnimplementedBackrestHandler) Cancel(context.Context, *connect.Request[typ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("v1.Backrest.Cancel is not implemented")) } -func (UnimplementedBackrestHandler) GetOperationData(context.Context, *connect.Request[v1.OperationDataRequest]) (*connect.Response[types.BytesValue], error) { - return nil, connect.NewError(connect.CodeUnimplemented, errors.New("v1.Backrest.GetOperationData is not implemented")) +func (UnimplementedBackrestHandler) GetBigOperationData(context.Context, *connect.Request[v1.OperationDataRequest]) (*connect.Response[types.BytesValue], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("v1.Backrest.GetBigOperationData is not implemented")) } func (UnimplementedBackrestHandler) ClearHistory(context.Context, *connect.Request[v1.ClearHistoryRequest]) (*connect.Response[emptypb.Empty], error) { diff --git a/go.mod b/go.mod index 7e2b5f9b..5c8a4374 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/hashicorp/go-multierror v1.1.1 github.com/mattn/go-colorable v0.1.13 github.com/natefinch/atomic v1.0.1 + github.com/nikoksr/notify v0.41.0 go.etcd.io/bbolt v1.3.8 go.uber.org/zap v1.26.0 golang.org/x/crypto v0.18.0 @@ -25,8 +26,10 @@ require ( github.com/google/go-cmp v0.6.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/stretchr/testify v1.8.4 // indirect go.uber.org/multierr v1.11.0 // indirect + golang.org/x/sync v0.6.0 // indirect golang.org/x/sys v0.16.0 // indirect golang.org/x/text v0.14.0 // indirect google.golang.org/genproto v0.0.0-20240125205218-1f4bbc51befe // indirect diff --git a/go.sum b/go.sum index 74fbf1fb..025c0938 100644 --- a/go.sum +++ b/go.sum @@ -31,7 +31,11 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/natefinch/atomic v1.0.1 h1:ZPYKxkqQOx3KZ+RsbnP/YsgvxWQPGxjC0oBt2AhwV0A= github.com/natefinch/atomic v1.0.1/go.mod h1:N/D/ELrljoqDyT3rZrsUmtsuzvHkeB/wWjHV22AZRbM= +github.com/nikoksr/notify v0.41.0 h1:4LGE41GpWdHX5M3Xo6DlWRwS2WLDbOq1Rk7IzY4vjmQ= +github.com/nikoksr/notify v0.41.0/go.mod h1:FoE0UVPeopz1Vy5nm9vQZ+JVmYjEIjQgbFstbkw+cRE= github.com/nkovacs/streamquote v1.0.0/go.mod h1:BN+NaZ2CmdKqUuTUXUEm9j95B2TRbpOWpxbJYzzgUsc= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= @@ -54,6 +58,8 @@ golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= diff --git a/internal/api/server.go b/internal/api/server.go index 15f5d3fd..3fdc75e4 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -397,7 +397,7 @@ func (s *Server) ClearHistory(ctx context.Context, req *connect.Request[v1.Clear return connect.NewResponse(&emptypb.Empty{}), err } -func (s *Server) GetOperationData(ctx context.Context, req *connect.Request[v1.OperationDataRequest]) (*connect.Response[types.BytesValue], error) { +func (s *Server) GetBigOperationData(ctx context.Context, req *connect.Request[v1.OperationDataRequest]) (*connect.Response[types.BytesValue], error) { data, err := s.oplog.GetBigData(req.Msg.Id, req.Msg.Key) if err != nil { return nil, fmt.Errorf("get operation data: %w", err) diff --git a/internal/hook/commandhook.go b/internal/hook/commandhook.go index e9b2cc19..e677a9de 100644 --- a/internal/hook/commandhook.go +++ b/internal/hook/commandhook.go @@ -10,9 +10,9 @@ import ( ) func (h *Hook) doCommand(cmd *v1.Hook_ActionCommand, vars HookVars, output io.Writer) error { - command, err := h.makeSubstitutions(cmd.ActionCommand.Command, vars) + command, err := h.renderTemplate(cmd.ActionCommand.Command, vars) if err != nil { - return fmt.Errorf("template formatting: %w", err) + return fmt.Errorf("template rendering: %w", err) } // Parse out the shell to use if a #! prefix is present diff --git a/internal/hook/discordhook.go b/internal/hook/discordhook.go new file mode 100644 index 00000000..23488a35 --- /dev/null +++ b/internal/hook/discordhook.go @@ -0,0 +1,30 @@ +package hook + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + + v1 "github.com/garethgeorge/backrest/gen/go/v1" +) + +func (h *Hook) doDiscord(cmd *v1.Hook_ActionDiscord, vars HookVars, output io.Writer) error { + payload, err := h.renderTemplateOrDefault(cmd.ActionDiscord.GetTemplate(), defaultTemplate, vars) + if err != nil { + return fmt.Errorf("template rendering: %w", err) + } + + type Message struct { + Content string `json:"content"` + } + + request := Message{ + Content: "Backrest Notification\n" + payload, // leading newline looks better in discord. + } + + requestBytes, _ := json.Marshal(request) + + _, err = post(cmd.ActionDiscord.GetWebhookUrl(), "application/json", bytes.NewReader(requestBytes)) + return err +} diff --git a/internal/hook/hook.go b/internal/hook/hook.go index 5f4c4030..95b664eb 100644 --- a/internal/hook/hook.go +++ b/internal/hook/hook.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "slices" + "strings" "text/template" "time" @@ -15,6 +16,10 @@ import ( "google.golang.org/protobuf/proto" ) +var ( + defaultTemplate = `{{.Summary}}` +) + // ExecuteHooks schedules tasks for the hooks subscribed to the given event. The vars map is used to substitute variables // Hooks are pulled both from the provided plan and from the repo config. func ExecuteHooks(oplog *oplog.OpLog, repo *v1.Repo, plan *v1.Plan, snapshotId string, events []v1.Hook_Condition, vars HookVars) { @@ -131,13 +136,15 @@ func (h *Hook) Do(event v1.Hook_Condition, vars HookVars, output io.Writer) erro switch action := h.Action.(type) { case *v1.Hook_ActionCommand: return h.doCommand(action, vars, output) + case *v1.Hook_ActionDiscord: + return h.doDiscord(action, vars, output) default: return fmt.Errorf("unknown hook action: %v", action) } } -func (h *Hook) makeSubstitutions(text string, vars HookVars) (string, error) { - template, err := template.New("command").Parse(text) +func (h *Hook) renderTemplate(text string, vars HookVars) (string, error) { + template, err := template.New("template").Parse(text) if err != nil { return "", fmt.Errorf("parse template: %w", err) } @@ -149,3 +156,10 @@ func (h *Hook) makeSubstitutions(text string, vars HookVars) (string, error) { return buf.String(), nil } + +func (h *Hook) renderTemplateOrDefault(template string, defaultTmpl string, vars HookVars) (string, error) { + if strings.Trim(template, " ") == "" { + return h.renderTemplate(defaultTmpl, vars) + } + return h.renderTemplate(template, vars) +} diff --git a/internal/hook/hookvars.go b/internal/hook/hookvars.go index ee784328..d997628e 100644 --- a/internal/hook/hookvars.go +++ b/internal/hook/hookvars.go @@ -3,6 +3,7 @@ package hook import ( "bytes" "encoding/json" + "fmt" "text/template" "time" @@ -44,6 +45,32 @@ func (v HookVars) FormatTime(t time.Time) string { return t.Format(time.RFC3339) } +func (v HookVars) number(n any) int { + switch n := n.(type) { + case int: + return n + case int32: + return int(n) + case int64: + return int(n) + default: + return 0 + } +} + +func (v HookVars) FormatSizeBytes(val any) string { + size := v.number(val) + sizes := []string{"B", "KB", "MB", "GB", "TB", "PB"} + i := 0 + prev := size + for size > 1000 { + size /= 1000 + prev = size + i++ + } + return fmt.Sprintf("%d.%03d %s", size, prev, sizes[i]) +} + func (v HookVars) IsError(cond v1.Hook_Condition) bool { return cond == v1.Hook_CONDITION_ANY_ERROR || cond == v1.Hook_CONDITION_SNAPSHOT_ERROR } @@ -52,7 +79,7 @@ func (v HookVars) ShellEscape(s string) string { return shellescape.Quote(s) } -func (v HookVars) JSONEscape(s string) string { +func (v HookVars) JsonMarshal(s any) string { b, err := json.Marshal(s) if err != nil { return "" @@ -90,34 +117,43 @@ func (v HookVars) renderTemplate(templ string) (string, error) { } var templateForSnapshotEnd = `Task: "{{ .Task }}" at {{ .FormatTime .CurTime }} -Repo: {{ .Repo.Id }} Plan: {{ .Plan.Id }} Snapshot: {{ .SnapshotId }} +Event: {{ .EventName .Event }} +Repo: {{ .Repo.Id }} +Plan: {{ .Plan.Id }} +Snapshot: {{ .SnapshotId }} {{ if .Error -}} Failed to create snapshot: {{ .Error }} {{ else -}} {{ if .SnapshotStats -}} -Stats: - - Files new: {{ .SnapshotStats.FilesNew }} - - Files changed: {{ .SnapshotStats.FilesChanged }} - - Files unmodified: {{ .SnapshotStats.FilesUnmodified }} - - Dirs new: {{ .SnapshotStats.DirsNew }} - - Dirs changed: {{ .SnapshotStats.DirsChanged }} - - Dirs unmodified: {{ .SnapshotStats.DirsUnmodified }} - - Data blobs: {{ .SnapshotStats.DataBlobs }} - - Tree blobs: {{ .SnapshotStats.TreeBlobs }} - - Data added: {{ .SnapshotStats.DataAdded }} bytes - - Total files processed: {{ .SnapshotStats.TotalFilesProcessed }} - - Total bytes processed: {{ .SnapshotStats.TotalBytesProcessed }} bytes - - Total duration: {{ .SnapshotStats.TotalDuration }}s + +Overview: +- Data added: {{ .FormatSizeBytes .SnapshotStats.DataAdded }} +- Total files processed: {{ .SnapshotStats.TotalFilesProcessed }} +- Total bytes processed: {{ .FormatSizeBytes .SnapshotStats.TotalBytesProcessed }} + +Backup Statistics: +- Files new: {{ .SnapshotStats.FilesNew }} +- Files changed: {{ .SnapshotStats.FilesChanged }} +- Files unmodified: {{ .SnapshotStats.FilesUnmodified }} +- Dirs new: {{ .SnapshotStats.DirsNew }} +- Dirs changed: {{ .SnapshotStats.DirsChanged }} +- Dirs unmodified: {{ .SnapshotStats.DirsUnmodified }} +- Data blobs: {{ .SnapshotStats.DataBlobs }} +- Tree blobs: {{ .SnapshotStats.TreeBlobs }} +- Total duration: {{ .SnapshotStats.TotalDuration }}s {{ end }} {{ end }}` var templateForError = `Task: "{{ .Task }}" at {{ .FormatTime .CurTime }} -{{ if .Error }} +{{ if .Error -}} Error: {{ .Error }} {{ end }}` var templateForSnapshotStart = `Task: "{{ .Task }}" at {{ .FormatTime .CurTime }} -Repo: {{ .Repo.Id }} Plan: {{ .Plan.Id }} -Paths: {{ range .Plan.Paths }} +Event: {{ .EventName .Event }} +Repo: {{ .Repo.Id }} +Plan: {{ .Plan.Id }} +Paths: +{{ range .Plan.Paths -}} - {{ . }} {{ end }}` diff --git a/internal/hook/httputil.go b/internal/hook/httputil.go new file mode 100644 index 00000000..72296b58 --- /dev/null +++ b/internal/hook/httputil.go @@ -0,0 +1,25 @@ +package hook + +import ( + "fmt" + "io" + "net/http" +) + +func post(url string, contentType string, body io.Reader) (string, error) { + r, err := http.Post(url, contentType, body) + if err != nil { + return "", fmt.Errorf("send request: %w", url, err) + } + if r.StatusCode == 204 { + return "", nil + } else if r.StatusCode != 200 { + return "", fmt.Errorf("unexpected status %v: %s", r.StatusCode, r.Status) + } + defer r.Body.Close() + bodyBytes, err := io.ReadAll(r.Body) + if err != nil { + return "", fmt.Errorf("read response: %w", err) + } + return string(bodyBytes), nil +} diff --git a/internal/orchestrator/taskbackup.go b/internal/orchestrator/taskbackup.go index e22eab42..446a7ea2 100644 --- a/internal/orchestrator/taskbackup.go +++ b/internal/orchestrator/taskbackup.go @@ -148,7 +148,7 @@ func backupHelper(ctx context.Context, t Task, orchestrator *Orchestrator, plan op.DisplayMessage = "Partial backup, some files may not have been read completely." } - hook.ExecuteHooks(orchestrator.OpLog, repo.Config(), plan, "", []v1.Hook_Condition{ + hook.ExecuteHooks(orchestrator.OpLog, repo.Config(), plan, summary.SnapshotId, []v1.Hook_Condition{ v1.Hook_CONDITION_SNAPSHOT_END, }, vars) diff --git a/internal/orchestrator/taskcollectgarbage.go b/internal/orchestrator/taskcollectgarbage.go index 3afb4280..7741af59 100644 --- a/internal/orchestrator/taskcollectgarbage.go +++ b/internal/orchestrator/taskcollectgarbage.go @@ -17,7 +17,7 @@ const ( // - it has no snapshot associated with it // - it has a forgotten snapshot associated with it gcHistoryAge = 30 * 24 * time.Hour - gcHistoryMaxCount = 100 + gcHistoryMaxCount = 200 ) type CollectGarbageTask struct { diff --git a/proto/v1/config.proto b/proto/v1/config.proto index 8348c91f..76c12274 100644 --- a/proto/v1/config.proto +++ b/proto/v1/config.proto @@ -90,5 +90,7 @@ message Hook { message Discord { string webhook_url = 1 [json_name="webhookUrl"]; + string template = 2; // template for the webhook payload. } } + diff --git a/proto/v1/service.proto b/proto/v1/service.proto index 4ce4e83e..1839f28e 100644 --- a/proto/v1/service.proto +++ b/proto/v1/service.proto @@ -50,8 +50,8 @@ service Backrest { // Cancel attempts to cancel a task with the given operation ID. Not guaranteed to succeed. rpc Cancel(types.Int64Value) returns (google.protobuf.Empty) {} - // GetOperationData returns the keyed large data for the given operation. - rpc GetOperationData(OperationDataRequest) returns (types.BytesValue) {} + // GetBigOperationData returns the keyed large data for the given operation. + rpc GetBigOperationData(OperationDataRequest) returns (types.BytesValue) {} // Clears the history of operations rpc ClearHistory(ClearHistoryRequest) returns (google.protobuf.Empty) {} diff --git a/webui/gen/ts/v1/config_pb.ts b/webui/gen/ts/v1/config_pb.ts index 7cbaf1ea..1f4ba83b 100644 --- a/webui/gen/ts/v1/config_pb.ts +++ b/webui/gen/ts/v1/config_pb.ts @@ -640,6 +640,13 @@ export class Hook_Discord extends Message { */ webhookUrl = ""; + /** + * template for the webhook payload. + * + * @generated from field: string template = 2; + */ + template = ""; + constructor(data?: PartialMessage) { super(); proto3.util.initPartial(data, this); @@ -649,6 +656,7 @@ export class Hook_Discord extends Message { static readonly typeName = "v1.Hook.Discord"; static readonly fields: FieldList = proto3.util.newFieldList(() => [ { no: 1, name: "webhook_url", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 2, name: "template", kind: "scalar", T: 9 /* ScalarType.STRING */ }, ]); static fromBinary(bytes: Uint8Array, options?: Partial): Hook_Discord { diff --git a/webui/gen/ts/v1/service_connect.ts b/webui/gen/ts/v1/service_connect.ts index 96b9194f..b65df58d 100644 --- a/webui/gen/ts/v1/service_connect.ts +++ b/webui/gen/ts/v1/service_connect.ts @@ -168,12 +168,12 @@ export const Backrest = { kind: MethodKind.Unary, }, /** - * GetOperationData returns the keyed large data for the given operation. + * GetBigOperationData returns the keyed large data for the given operation. * - * @generated from rpc v1.Backrest.GetOperationData + * @generated from rpc v1.Backrest.GetBigOperationData */ - getOperationData: { - name: "GetOperationData", + getBigOperationData: { + name: "GetBigOperationData", I: OperationDataRequest, O: BytesValue, kind: MethodKind.Unary, diff --git a/webui/src/components/HooksFormList.tsx b/webui/src/components/HooksFormList.tsx index 1c369d1d..f38f3cf8 100644 --- a/webui/src/components/HooksFormList.tsx +++ b/webui/src/components/HooksFormList.tsx @@ -23,6 +23,14 @@ export const hooksListTooltipText = <>
  • .CurTime - the time of the event.
  • .SnapshotId - the restic snapshot structure if this is finish snapshot operation and it completed successfully.
  • + Functions +
      +
    • .ShellEscape - escapes a string to be used in a shell command.
    • +
    • .JsonMarshal - serializes a value to be used in a json string.
    • +
    • .Summary - prints a formatted summary of the event.
    • +
    • .FormatTime - prints time formatted as RFC3339.
    • +
    • .FormatSizeBytes - prints a formatted size in bytes.
    • +
    /** @@ -31,18 +39,6 @@ export const hooksListTooltipText = <> export const HooksFormList = (props: { hooks: Hook[] }) => { const [hooks, _] = useState([...props.hooks] || []); - const hookTypes: { - name: string, - action: typeof Hook.prototype.action, - }[] = [ - { - name: "Command", action: { - case: "actionCommand", - value: new Hook_Command(), - } - }, - ]; - return {(fields, { add, remove }, { errors }) => ( <> @@ -106,28 +102,52 @@ export const HooksFormList = (props: { hooks: Hook[] }) => { } +const hookTypes: { + name: string, + action: typeof Hook.prototype.action, +}[] = [ + { + name: "Command", action: { + case: "actionCommand", + value: new Hook_Command({ + command: "echo {{ .ShellEscape .Summary }}", + }), + } + }, + { + name: "Discord", action: { + case: "actionDiscord", + value: new Hook_Discord({ + webhookUrl: "", + template: "{{ .Summary }}", + }), + } + }, + ]; + const HookBuilder = ({ field, hook }: { field: FormListFieldData, hook: Hook }) => { let component: React.ReactNode; switch (hook.action.case) { case "actionDiscord": - return - Discord Webhook} /> - - case "actionWebhook": - return - Webhook URL} /> - + return <> + + Discord Webhook} /> + + Text: + + + + case "actionCommand": return <> Script: - + default: return

    Unknown hook {hook.action.case}

    } - } \ No newline at end of file diff --git a/webui/src/components/OperationList.tsx b/webui/src/components/OperationList.tsx index e565c7a2..d8b91142 100644 --- a/webui/src/components/OperationList.tsx +++ b/webui/src/components/OperationList.tsx @@ -495,7 +495,7 @@ const RunHookOperationStatus = ({ op }: { op: Operation }) => { if (!hook.outputRef) { return; } - backrestService.getOperationData(new OperationDataRequest({ + backrestService.getBigOperationData(new OperationDataRequest({ id: op.id, key: hook.outputRef, })).then((resp) => { @@ -505,8 +505,11 @@ const RunHookOperationStatus = ({ op }: { op: Operation }) => { }); }, [hook.outputRef]); - return
    +  return <>
         Hook: {hook.name} 
    - {output} -
    + Output:
    +
    +      {output}
    +    
    + } \ No newline at end of file