Add webui

This commit is contained in:
garethgeorge
2023-11-11 00:39:24 -08:00
parent f365913430
commit b57ff969dd
26 changed files with 4553 additions and 75 deletions

View File

@@ -23,7 +23,7 @@ jobs:
go-version: '1.20'
- name: Restic
run: apt install restic
run: sudo apt install -y restic
- name: Build
run: go build -v ./...

View File

@@ -91,6 +91,61 @@ func (x *Config) GetPlans() []*Plan {
return nil
}
type User struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"` // plaintext password
}
func (x *User) Reset() {
*x = User{}
if protoimpl.UnsafeEnabled {
mi := &file_v1_config_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *User) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*User) ProtoMessage() {}
func (x *User) ProtoReflect() protoreflect.Message {
mi := &file_v1_config_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use User.ProtoReflect.Descriptor instead.
func (*User) Descriptor() ([]byte, []int) {
return file_v1_config_proto_rawDescGZIP(), []int{1}
}
func (x *User) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *User) GetPassword() string {
if x != nil {
return x.Password
}
return ""
}
type Repo struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -105,7 +160,7 @@ type Repo struct {
func (x *Repo) Reset() {
*x = Repo{}
if protoimpl.UnsafeEnabled {
mi := &file_v1_config_proto_msgTypes[1]
mi := &file_v1_config_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -118,7 +173,7 @@ func (x *Repo) String() string {
func (*Repo) ProtoMessage() {}
func (x *Repo) ProtoReflect() protoreflect.Message {
mi := &file_v1_config_proto_msgTypes[1]
mi := &file_v1_config_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -131,7 +186,7 @@ func (x *Repo) ProtoReflect() protoreflect.Message {
// Deprecated: Use Repo.ProtoReflect.Descriptor instead.
func (*Repo) Descriptor() ([]byte, []int) {
return file_v1_config_proto_rawDescGZIP(), []int{1}
return file_v1_config_proto_rawDescGZIP(), []int{2}
}
func (x *Repo) GetId() string {
@@ -169,14 +224,13 @@ type Plan struct {
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
Repo string `protobuf:"bytes,2,opt,name=repo,proto3" json:"repo,omitempty"`
RepoPath string `protobuf:"bytes,3,opt,name=repo_path,proto3" json:"repo_path,omitempty"` // subpath of the repo to backup to
Paths []string `protobuf:"bytes,4,rep,name=paths,proto3" json:"paths,omitempty"`
}
func (x *Plan) Reset() {
*x = Plan{}
if protoimpl.UnsafeEnabled {
mi := &file_v1_config_proto_msgTypes[2]
mi := &file_v1_config_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -189,7 +243,7 @@ func (x *Plan) String() string {
func (*Plan) ProtoMessage() {}
func (x *Plan) ProtoReflect() protoreflect.Message {
mi := &file_v1_config_proto_msgTypes[2]
mi := &file_v1_config_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -202,7 +256,7 @@ func (x *Plan) ProtoReflect() protoreflect.Message {
// Deprecated: Use Plan.ProtoReflect.Descriptor instead.
func (*Plan) Descriptor() ([]byte, []int) {
return file_v1_config_proto_rawDescGZIP(), []int{2}
return file_v1_config_proto_rawDescGZIP(), []int{3}
}
func (x *Plan) GetId() string {
@@ -219,13 +273,6 @@ func (x *Plan) GetRepo() string {
return ""
}
func (x *Plan) GetRepoPath() string {
if x != nil {
return x.RepoPath
}
return ""
}
func (x *Plan) GetPaths() []string {
if x != nil {
return x.Paths
@@ -245,22 +292,23 @@ var file_v1_config_proto_rawDesc = []byte{
0x28, 0x0b, 0x32, 0x08, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x52, 0x05, 0x72, 0x65,
0x70, 0x6f, 0x73, 0x12, 0x1e, 0x0a, 0x05, 0x70, 0x6c, 0x61, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03,
0x28, 0x0b, 0x32, 0x08, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x05, 0x70, 0x6c,
0x61, 0x6e, 0x73, 0x22, 0x56, 0x0a, 0x04, 0x52, 0x65, 0x70, 0x6f, 0x12, 0x0e, 0x0a, 0x02, 0x69,
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75,
0x72, 0x69, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x69, 0x12, 0x1a, 0x0a,
0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x76,
0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x65, 0x6e, 0x76, 0x22, 0x5e, 0x0a, 0x04, 0x50,
0x6c, 0x61, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x65, 0x70, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28,
0x09, 0x52, 0x04, 0x72, 0x65, 0x70, 0x6f, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x70, 0x6f, 0x5f,
0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x70, 0x6f,
0x5f, 0x70, 0x61, 0x74, 0x68, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, 0x04,
0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x70, 0x61, 0x74, 0x68, 0x73, 0x42, 0x2e, 0x5a, 0x2c, 0x67,
0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x61, 0x72, 0x65, 0x74, 0x68,
0x67, 0x65, 0x6f, 0x72, 0x67, 0x65, 0x2f, 0x72, 0x65, 0x73, 0x74, 0x69, 0x63, 0x75, 0x69, 0x2f,
0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x33,
0x61, 0x6e, 0x73, 0x22, 0x36, 0x0a, 0x04, 0x55, 0x73, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e,
0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12,
0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28,
0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x56, 0x0a, 0x04, 0x52,
0x65, 0x70, 0x6f, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x02, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x69, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
0x52, 0x03, 0x75, 0x72, 0x69, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72,
0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72,
0x64, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x76, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03,
0x65, 0x6e, 0x76, 0x22, 0x40, 0x0a, 0x04, 0x50, 0x6c, 0x61, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69,
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x72,
0x65, 0x70, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x65, 0x70, 0x6f, 0x12,
0x14, 0x0a, 0x05, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05,
0x70, 0x61, 0x74, 0x68, 0x73, 0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x61, 0x72, 0x65, 0x74, 0x68, 0x67, 0x65, 0x6f, 0x72, 0x67, 0x65,
0x2f, 0x72, 0x65, 0x73, 0x74, 0x69, 0x63, 0x75, 0x69, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@@ -275,15 +323,16 @@ func file_v1_config_proto_rawDescGZIP() []byte {
return file_v1_config_proto_rawDescData
}
var file_v1_config_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
var file_v1_config_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
var file_v1_config_proto_goTypes = []interface{}{
(*Config)(nil), // 0: v1.Config
(*Repo)(nil), // 1: v1.Repo
(*Plan)(nil), // 2: v1.Plan
(*User)(nil), // 1: v1.User
(*Repo)(nil), // 2: v1.Repo
(*Plan)(nil), // 3: v1.Plan
}
var file_v1_config_proto_depIdxs = []int32{
1, // 0: v1.Config.repos:type_name -> v1.Repo
2, // 1: v1.Config.plans:type_name -> v1.Plan
2, // 0: v1.Config.repos:type_name -> v1.Repo
3, // 1: v1.Config.plans:type_name -> v1.Plan
2, // [2:2] is the sub-list for method output_type
2, // [2:2] is the sub-list for method input_type
2, // [2:2] is the sub-list for extension type_name
@@ -310,7 +359,7 @@ func file_v1_config_proto_init() {
}
}
file_v1_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Repo); i {
switch v := v.(*User); i {
case 0:
return &v.state
case 1:
@@ -322,6 +371,18 @@ func file_v1_config_proto_init() {
}
}
file_v1_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Repo); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_v1_config_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Plan); i {
case 0:
return &v.state
@@ -340,7 +401,7 @@ func file_v1_config_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_v1_config_proto_rawDesc,
NumEnums: 0,
NumMessages: 3,
NumMessages: 4,
NumExtensions: 0,
NumServices: 0,
},

612
gen/go/v1/restic.pb.go Normal file
View File

@@ -0,0 +1,612 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.31.0
// protoc (unknown)
// source: v1/restic.proto
package v1
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type ResticSnapshot struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
UnixTimeMs int64 `protobuf:"varint,2,opt,name=unix_time_ms,json=time,proto3" json:"unix_time_ms,omitempty"`
Hostname string `protobuf:"bytes,3,opt,name=hostname,proto3" json:"hostname,omitempty"`
Username string `protobuf:"bytes,4,opt,name=username,proto3" json:"username,omitempty"`
Tree string `protobuf:"bytes,5,opt,name=tree,proto3" json:"tree,omitempty"` // tree hash
Parent string `protobuf:"bytes,6,opt,name=parent,proto3" json:"parent,omitempty"` // parent snapshot's id
Paths []string `protobuf:"bytes,7,rep,name=paths,proto3" json:"paths,omitempty"`
Tags []string `protobuf:"bytes,8,rep,name=tags,proto3" json:"tags,omitempty"`
}
func (x *ResticSnapshot) Reset() {
*x = ResticSnapshot{}
if protoimpl.UnsafeEnabled {
mi := &file_v1_restic_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ResticSnapshot) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ResticSnapshot) ProtoMessage() {}
func (x *ResticSnapshot) ProtoReflect() protoreflect.Message {
mi := &file_v1_restic_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ResticSnapshot.ProtoReflect.Descriptor instead.
func (*ResticSnapshot) Descriptor() ([]byte, []int) {
return file_v1_restic_proto_rawDescGZIP(), []int{0}
}
func (x *ResticSnapshot) GetId() string {
if x != nil {
return x.Id
}
return ""
}
func (x *ResticSnapshot) GetUnixTimeMs() int64 {
if x != nil {
return x.UnixTimeMs
}
return 0
}
func (x *ResticSnapshot) GetHostname() string {
if x != nil {
return x.Hostname
}
return ""
}
func (x *ResticSnapshot) GetUsername() string {
if x != nil {
return x.Username
}
return ""
}
func (x *ResticSnapshot) GetTree() string {
if x != nil {
return x.Tree
}
return ""
}
func (x *ResticSnapshot) GetParent() string {
if x != nil {
return x.Parent
}
return ""
}
func (x *ResticSnapshot) GetPaths() []string {
if x != nil {
return x.Paths
}
return nil
}
func (x *ResticSnapshot) GetTags() []string {
if x != nil {
return x.Tags
}
return nil
}
type BackupProgressEntry struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// Types that are assignable to Entry:
//
// *BackupProgressEntry_Status
// *BackupProgressEntry_Summary
Entry isBackupProgressEntry_Entry `protobuf_oneof:"entry"`
}
func (x *BackupProgressEntry) Reset() {
*x = BackupProgressEntry{}
if protoimpl.UnsafeEnabled {
mi := &file_v1_restic_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *BackupProgressEntry) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*BackupProgressEntry) ProtoMessage() {}
func (x *BackupProgressEntry) ProtoReflect() protoreflect.Message {
mi := &file_v1_restic_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use BackupProgressEntry.ProtoReflect.Descriptor instead.
func (*BackupProgressEntry) Descriptor() ([]byte, []int) {
return file_v1_restic_proto_rawDescGZIP(), []int{1}
}
func (m *BackupProgressEntry) GetEntry() isBackupProgressEntry_Entry {
if m != nil {
return m.Entry
}
return nil
}
func (x *BackupProgressEntry) GetStatus() *BackupProgressStatusEntry {
if x, ok := x.GetEntry().(*BackupProgressEntry_Status); ok {
return x.Status
}
return nil
}
func (x *BackupProgressEntry) GetSummary() *BackupProgressSummary {
if x, ok := x.GetEntry().(*BackupProgressEntry_Summary); ok {
return x.Summary
}
return nil
}
type isBackupProgressEntry_Entry interface {
isBackupProgressEntry_Entry()
}
type BackupProgressEntry_Status struct {
Status *BackupProgressStatusEntry `protobuf:"bytes,1,opt,name=status,proto3,oneof"`
}
type BackupProgressEntry_Summary struct {
Summary *BackupProgressSummary `protobuf:"bytes,2,opt,name=summary,proto3,oneof"`
}
func (*BackupProgressEntry_Status) isBackupProgressEntry_Entry() {}
func (*BackupProgressEntry_Summary) isBackupProgressEntry_Entry() {}
type BackupProgressStatusEntry struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
PercentDone float64 `protobuf:"fixed64,1,opt,name=percent_done,json=percent,proto3" json:"percent_done,omitempty"` // 0.0 - 1.0
TotalFiles int64 `protobuf:"varint,2,opt,name=total_files,proto3" json:"total_files,omitempty"`
TotalBytes int64 `protobuf:"varint,3,opt,name=total_bytes,proto3" json:"total_bytes,omitempty"`
FilesDone int64 `protobuf:"varint,4,opt,name=files_done,proto3" json:"files_done,omitempty"`
BytesDone int64 `protobuf:"varint,5,opt,name=bytes_done,proto3" json:"bytes_done,omitempty"`
}
func (x *BackupProgressStatusEntry) Reset() {
*x = BackupProgressStatusEntry{}
if protoimpl.UnsafeEnabled {
mi := &file_v1_restic_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *BackupProgressStatusEntry) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*BackupProgressStatusEntry) ProtoMessage() {}
func (x *BackupProgressStatusEntry) ProtoReflect() protoreflect.Message {
mi := &file_v1_restic_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use BackupProgressStatusEntry.ProtoReflect.Descriptor instead.
func (*BackupProgressStatusEntry) Descriptor() ([]byte, []int) {
return file_v1_restic_proto_rawDescGZIP(), []int{2}
}
func (x *BackupProgressStatusEntry) GetPercentDone() float64 {
if x != nil {
return x.PercentDone
}
return 0
}
func (x *BackupProgressStatusEntry) GetTotalFiles() int64 {
if x != nil {
return x.TotalFiles
}
return 0
}
func (x *BackupProgressStatusEntry) GetTotalBytes() int64 {
if x != nil {
return x.TotalBytes
}
return 0
}
func (x *BackupProgressStatusEntry) GetFilesDone() int64 {
if x != nil {
return x.FilesDone
}
return 0
}
func (x *BackupProgressStatusEntry) GetBytesDone() int64 {
if x != nil {
return x.BytesDone
}
return 0
}
type BackupProgressSummary struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
FilesNew int64 `protobuf:"varint,1,opt,name=files_new,proto3" json:"files_new,omitempty"`
FilesChanged int64 `protobuf:"varint,2,opt,name=files_changed,proto3" json:"files_changed,omitempty"`
FilesUnmodified int64 `protobuf:"varint,3,opt,name=files_unmodified,proto3" json:"files_unmodified,omitempty"`
DirsNew int64 `protobuf:"varint,4,opt,name=dirs_new,proto3" json:"dirs_new,omitempty"`
DirsChanged int64 `protobuf:"varint,5,opt,name=dirs_changed,proto3" json:"dirs_changed,omitempty"`
DirsUnmodified int64 `protobuf:"varint,6,opt,name=dirs_unmodified,proto3" json:"dirs_unmodified,omitempty"`
DataBlobs int64 `protobuf:"varint,7,opt,name=data_blobs,proto3" json:"data_blobs,omitempty"`
TreeBlobs int64 `protobuf:"varint,8,opt,name=tree_blobs,proto3" json:"tree_blobs,omitempty"`
DataAdded int64 `protobuf:"varint,9,opt,name=data_added,proto3" json:"data_added,omitempty"`
TotalFilesProcessed int64 `protobuf:"varint,10,opt,name=total_files_processed,proto3" json:"total_files_processed,omitempty"`
TotalBytesProcessed int64 `protobuf:"varint,11,opt,name=total_bytes_processed,proto3" json:"total_bytes_processed,omitempty"`
TotalDuration int64 `protobuf:"varint,12,opt,name=total_duration,proto3" json:"total_duration,omitempty"`
SnapshotId string `protobuf:"bytes,13,opt,name=snapshot_id,proto3" json:"snapshot_id,omitempty"`
}
func (x *BackupProgressSummary) Reset() {
*x = BackupProgressSummary{}
if protoimpl.UnsafeEnabled {
mi := &file_v1_restic_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *BackupProgressSummary) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*BackupProgressSummary) ProtoMessage() {}
func (x *BackupProgressSummary) ProtoReflect() protoreflect.Message {
mi := &file_v1_restic_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use BackupProgressSummary.ProtoReflect.Descriptor instead.
func (*BackupProgressSummary) Descriptor() ([]byte, []int) {
return file_v1_restic_proto_rawDescGZIP(), []int{3}
}
func (x *BackupProgressSummary) GetFilesNew() int64 {
if x != nil {
return x.FilesNew
}
return 0
}
func (x *BackupProgressSummary) GetFilesChanged() int64 {
if x != nil {
return x.FilesChanged
}
return 0
}
func (x *BackupProgressSummary) GetFilesUnmodified() int64 {
if x != nil {
return x.FilesUnmodified
}
return 0
}
func (x *BackupProgressSummary) GetDirsNew() int64 {
if x != nil {
return x.DirsNew
}
return 0
}
func (x *BackupProgressSummary) GetDirsChanged() int64 {
if x != nil {
return x.DirsChanged
}
return 0
}
func (x *BackupProgressSummary) GetDirsUnmodified() int64 {
if x != nil {
return x.DirsUnmodified
}
return 0
}
func (x *BackupProgressSummary) GetDataBlobs() int64 {
if x != nil {
return x.DataBlobs
}
return 0
}
func (x *BackupProgressSummary) GetTreeBlobs() int64 {
if x != nil {
return x.TreeBlobs
}
return 0
}
func (x *BackupProgressSummary) GetDataAdded() int64 {
if x != nil {
return x.DataAdded
}
return 0
}
func (x *BackupProgressSummary) GetTotalFilesProcessed() int64 {
if x != nil {
return x.TotalFilesProcessed
}
return 0
}
func (x *BackupProgressSummary) GetTotalBytesProcessed() int64 {
if x != nil {
return x.TotalBytesProcessed
}
return 0
}
func (x *BackupProgressSummary) GetTotalDuration() int64 {
if x != nil {
return x.TotalDuration
}
return 0
}
func (x *BackupProgressSummary) GetSnapshotId() string {
if x != nil {
return x.SnapshotId
}
return ""
}
var File_v1_restic_proto protoreflect.FileDescriptor
var file_v1_restic_proto_rawDesc = []byte{
0x0a, 0x0f, 0x76, 0x31, 0x2f, 0x72, 0x65, 0x73, 0x74, 0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x12, 0x02, 0x76, 0x31, 0x22, 0xca, 0x01, 0x0a, 0x0e, 0x52, 0x65, 0x73, 0x74, 0x69, 0x63,
0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x0c, 0x75, 0x6e, 0x69, 0x78,
0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04,
0x74, 0x69, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65,
0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65,
0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01,
0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04,
0x74, 0x72, 0x65, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x72, 0x65, 0x65,
0x12, 0x16, 0x0a, 0x06, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09,
0x52, 0x06, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x61, 0x74, 0x68,
0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x70, 0x61, 0x74, 0x68, 0x73, 0x12, 0x12,
0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x74, 0x61,
0x67, 0x73, 0x22, 0x8e, 0x01, 0x0a, 0x13, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x50, 0x72, 0x6f,
0x67, 0x72, 0x65, 0x73, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x37, 0x0a, 0x06, 0x73, 0x74,
0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x76, 0x31, 0x2e,
0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x53, 0x74,
0x61, 0x74, 0x75, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x48, 0x00, 0x52, 0x06, 0x73, 0x74, 0x61,
0x74, 0x75, 0x73, 0x12, 0x35, 0x0a, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x02,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70,
0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x48,
0x00, 0x52, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x6e,
0x74, 0x72, 0x79, 0x22, 0xbe, 0x01, 0x0a, 0x19, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x50, 0x72,
0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x45, 0x6e, 0x74, 0x72,
0x79, 0x12, 0x1d, 0x0a, 0x0c, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x5f, 0x64, 0x6f, 0x6e,
0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74,
0x12, 0x20, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18,
0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x66, 0x69, 0x6c,
0x65, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x62, 0x79, 0x74, 0x65,
0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x62,
0x79, 0x74, 0x65, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x5f, 0x64, 0x6f,
0x6e, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x5f,
0x64, 0x6f, 0x6e, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x64, 0x6f,
0x6e, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f,
0x64, 0x6f, 0x6e, 0x65, 0x22, 0x87, 0x04, 0x0a, 0x15, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x50,
0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x1c,
0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x5f, 0x6e, 0x65, 0x77, 0x18, 0x01, 0x20, 0x01, 0x28,
0x03, 0x52, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x5f, 0x6e, 0x65, 0x77, 0x12, 0x24, 0x0a, 0x0d,
0x66, 0x69, 0x6c, 0x65, 0x73, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x18, 0x02, 0x20,
0x01, 0x28, 0x03, 0x52, 0x0d, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67,
0x65, 0x64, 0x12, 0x2a, 0x0a, 0x10, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x5f, 0x75, 0x6e, 0x6d, 0x6f,
0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x66, 0x69,
0x6c, 0x65, 0x73, 0x5f, 0x75, 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x12, 0x1a,
0x0a, 0x08, 0x64, 0x69, 0x72, 0x73, 0x5f, 0x6e, 0x65, 0x77, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03,
0x52, 0x08, 0x64, 0x69, 0x72, 0x73, 0x5f, 0x6e, 0x65, 0x77, 0x12, 0x22, 0x0a, 0x0c, 0x64, 0x69,
0x72, 0x73, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03,
0x52, 0x0c, 0x64, 0x69, 0x72, 0x73, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x12, 0x28,
0x0a, 0x0f, 0x64, 0x69, 0x72, 0x73, 0x5f, 0x75, 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65,
0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x64, 0x69, 0x72, 0x73, 0x5f, 0x75, 0x6e,
0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x61, 0x74, 0x61,
0x5f, 0x62, 0x6c, 0x6f, 0x62, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x64, 0x61,
0x74, 0x61, 0x5f, 0x62, 0x6c, 0x6f, 0x62, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x72, 0x65, 0x65,
0x5f, 0x62, 0x6c, 0x6f, 0x62, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x74, 0x72,
0x65, 0x65, 0x5f, 0x62, 0x6c, 0x6f, 0x62, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x61, 0x74, 0x61,
0x5f, 0x61, 0x64, 0x64, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x64, 0x61,
0x74, 0x61, 0x5f, 0x61, 0x64, 0x64, 0x65, 0x64, 0x12, 0x34, 0x0a, 0x15, 0x74, 0x6f, 0x74, 0x61,
0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65,
0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x15, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x66,
0x69, 0x6c, 0x65, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x12, 0x34,
0x0a, 0x15, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x70, 0x72,
0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x15, 0x74,
0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x63, 0x65,
0x73, 0x73, 0x65, 0x64, 0x12, 0x26, 0x0a, 0x0e, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x64, 0x75,
0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x74, 0x6f,
0x74, 0x61, 0x6c, 0x5f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x20, 0x0a, 0x0b,
0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28,
0x09, 0x52, 0x0b, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x69, 0x64, 0x42, 0x2e,
0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x61, 0x72,
0x65, 0x74, 0x68, 0x67, 0x65, 0x6f, 0x72, 0x67, 0x65, 0x2f, 0x72, 0x65, 0x73, 0x74, 0x69, 0x63,
0x75, 0x69, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_v1_restic_proto_rawDescOnce sync.Once
file_v1_restic_proto_rawDescData = file_v1_restic_proto_rawDesc
)
func file_v1_restic_proto_rawDescGZIP() []byte {
file_v1_restic_proto_rawDescOnce.Do(func() {
file_v1_restic_proto_rawDescData = protoimpl.X.CompressGZIP(file_v1_restic_proto_rawDescData)
})
return file_v1_restic_proto_rawDescData
}
var file_v1_restic_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
var file_v1_restic_proto_goTypes = []interface{}{
(*ResticSnapshot)(nil), // 0: v1.ResticSnapshot
(*BackupProgressEntry)(nil), // 1: v1.BackupProgressEntry
(*BackupProgressStatusEntry)(nil), // 2: v1.BackupProgressStatusEntry
(*BackupProgressSummary)(nil), // 3: v1.BackupProgressSummary
}
var file_v1_restic_proto_depIdxs = []int32{
2, // 0: v1.BackupProgressEntry.status:type_name -> v1.BackupProgressStatusEntry
3, // 1: v1.BackupProgressEntry.summary:type_name -> v1.BackupProgressSummary
2, // [2:2] is the sub-list for method output_type
2, // [2:2] is the sub-list for method input_type
2, // [2:2] is the sub-list for extension type_name
2, // [2:2] is the sub-list for extension extendee
0, // [0:2] is the sub-list for field type_name
}
func init() { file_v1_restic_proto_init() }
func file_v1_restic_proto_init() {
if File_v1_restic_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_v1_restic_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ResticSnapshot); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_v1_restic_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*BackupProgressEntry); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_v1_restic_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*BackupProgressStatusEntry); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_v1_restic_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*BackupProgressSummary); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
file_v1_restic_proto_msgTypes[1].OneofWrappers = []interface{}{
(*BackupProgressEntry_Status)(nil),
(*BackupProgressEntry_Summary)(nil),
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_v1_restic_proto_rawDesc,
NumEnums: 0,
NumMessages: 4,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_v1_restic_proto_goTypes,
DependencyIndexes: file_v1_restic_proto_depIdxs,
MessageInfos: file_v1_restic_proto_msgTypes,
}.Build()
File_v1_restic_proto = out.File
file_v1_restic_proto_rawDesc = nil
file_v1_restic_proto_goTypes = nil
file_v1_restic_proto_depIdxs = nil
}

View File

@@ -20,7 +20,7 @@ func serveGRPC(ctx context.Context, socket string) error {
return fmt.Errorf("failed to listen: %w", err)
}
grpcServer := grpc.NewServer()
v1.RegisterResticUIServer(grpcServer, &server{})
v1.RegisterResticUIServer(grpcServer, NewServer())
go func() {
<-ctx.Done()
grpcServer.GracefulStop()

View File

@@ -3,6 +3,8 @@ package api
import (
"context"
"fmt"
"sync"
"sync/atomic"
"time"
v1 "github.com/garethgeorge/resticui/gen/go/v1"
@@ -11,17 +13,44 @@ import (
"google.golang.org/protobuf/types/known/emptypb"
)
type server struct {
type Server struct {
*v1.UnimplementedResticUIServer
reqId atomic.Uint64
eventChannelsMu sync.Mutex
eventChannels map[uint64]chan *v1.Event
}
var _ v1.ResticUIServer = &server{}
var _ v1.ResticUIServer = &Server{}
func (s *server) GetConfig(ctx context.Context, empty *emptypb.Empty) (*v1.Config, error) {
func NewServer() *Server {
s := &Server{
eventChannels: make(map[uint64]chan *v1.Event),
}
go func() {
for {
time.Sleep(3 * time.Second)
s.PublishEvent(&v1.Event{
Event: &v1.Event_Log{
Log: &v1.LogEvent{
Message: fmt.Sprintf("event push test, it is %v", time.Now().Format(time.RFC3339)),
},
},
})
}
}()
return s
}
// GetConfig implements GET /v1/config
func (s *Server) GetConfig(ctx context.Context, empty *emptypb.Empty) (*v1.Config, error) {
return config.Default.Get()
}
func (s *server) SetConfig(ctx context.Context, c *v1.Config) (*v1.Config, error) {
// SetConfig implements PUT /v1/config
func (s *Server) SetConfig(ctx context.Context, c *v1.Config) (*v1.Config, error) {
err := config.Default.Update(c)
if err != nil {
return nil, fmt.Errorf("failed to update config: %w", err)
@@ -29,27 +58,33 @@ func (s *server) SetConfig(ctx context.Context, c *v1.Config) (*v1.Config, error
return config.Default.Get()
}
func (s *server) GetEvents(_ *emptypb.Empty, stream v1.ResticUI_GetEventsServer) error {
// GetEvents implements GET /v1/events
func (s *Server) GetEvents(_ *emptypb.Empty, stream v1.ResticUI_GetEventsServer) error {
reqId := s.reqId.Add(1)
// Register a channel to receive events for this invocation
s.eventChannelsMu.Lock()
eventChan := make(chan *v1.Event, 3)
s.eventChannels[reqId] = eventChan
s.eventChannelsMu.Unlock()
defer delete(s.eventChannels, reqId)
for {
zap.S().Info("Sending event")
stream.Send(&v1.Event{
Timestamp: 0,
Event: &v1.Event_BackupStatusChange{
BackupStatusChange: &v1.BackupStatusEvent{
Status: v1.Status_IN_PROGRESS,
Percent: 0,
Plan: "myplan",
},
},
})
timer := time.NewTimer(time.Second * 1)
select {
case <-stream.Context().Done():
zap.S().Info("Get events hangup")
return nil
case <-timer.C:
case event := <-eventChan:
stream.Send(event)
}
}
}
// PublishEvent publishes an event to all GetEvents streams. It is effectively a multicast.
func (s *Server) PublishEvent(event *v1.Event) {
zap.S().Debug("Publishing event", zap.Any("event", event))
s.eventChannelsMu.Lock()
defer s.eventChannelsMu.Unlock()
for _, ch := range s.eventChannels {
ch <- event
}
}

View File

@@ -23,14 +23,14 @@ type LsEntry struct {
}
type Snapshot struct {
Id string `json:"id"`
Time string `json:"time"`
Tree string `json:"tree"`
Paths []string `json:"paths"`
Hostname string `json:"hostname"`
Username string `json:"username"`
Id string `json:"id"`
ShortId string `json:"short_id"`
Tags []string `json:"tags"`
Parent string `json:"parent"`
}
type BackupProgressEntry struct {
@@ -58,9 +58,6 @@ type BackupProgressEntry struct {
FilesDone int `json:"files_done"`
TotalBytes int `json:"total_bytes"`
BytesDone int `json:"bytes_done"`
// Error fields
Error string `json:"error"`
}
// readBackupProgressEntrys returns the summary event or an error if the command failed.

View File

@@ -14,6 +14,6 @@ plugins:
- paths=source_relative
- generate_unbound_methods=true
- plugin: grpc-gateway-ts
out: ../gen/ts
out: ../webui/gen/ts
opt:
- paths=source_relative

View File

@@ -12,7 +12,6 @@ message Event {
LogEvent log = 3 [json_name="log"];
BackupStatusEvent backup_status_change = 4 [json_name="backup_status"];
}
}
message LogEvent {

47
proto/v1/restic.proto Normal file
View File

@@ -0,0 +1,47 @@
syntax = "proto3";
package v1;
option go_package = "github.com/garethgeorge/resticui/go/proto/v1";
message ResticSnapshot {
string id = 1 [json_name = "id"];
int64 unix_time_ms = 2 [json_name = "time"];
string hostname = 3 [json_name = "hostname"];
string username = 4 [json_name = "username"];
string tree = 5 [json_name = "tree"]; // tree hash
string parent = 6 [json_name = "parent"]; // parent snapshot's id
repeated string paths = 7 [json_name = "paths"];
repeated string tags = 8 [json_name = "tags"];
}
message BackupProgressEntry {
oneof entry {
BackupProgressStatusEntry status = 1 [json_name = "status"];
BackupProgressSummary summary = 2 [json_name = "summary"];
}
}
message BackupProgressStatusEntry {
double percent_done = 1 [json_name = "percent"]; // 0.0 - 1.0
int64 total_files = 2 [json_name = "total_files"];
int64 total_bytes = 3 [json_name = "total_bytes"];
int64 files_done = 4 [json_name = "files_done"];
int64 bytes_done = 5 [json_name = "bytes_done"];
}
message BackupProgressSummary {
int64 files_new = 1 [json_name = "files_new"];
int64 files_changed = 2 [json_name = "files_changed"];
int64 files_unmodified = 3 [json_name = "files_unmodified"];
int64 dirs_new = 4 [json_name = "dirs_new"];
int64 dirs_changed = 5 [json_name = "dirs_changed"];
int64 dirs_unmodified = 6 [json_name = "dirs_unmodified"];
int64 data_blobs = 7 [json_name = "data_blobs"];
int64 tree_blobs = 8 [json_name = "tree_blobs"];
int64 data_added = 9 [json_name = "data_added"];
int64 total_files_processed = 10 [json_name = "total_files_processed"];
int64 total_bytes_processed = 11 [json_name = "total_bytes_processed"];
int64 total_duration = 12 [json_name = "total_duration"];
string snapshot_id = 13 [json_name = "snapshot_id"];
}

View File

@@ -15,6 +15,7 @@ service ResticUI {
get: "/v1/config"
};
}
rpc SetConfig (Config) returns (Config) {
option (google.api.http) = {
post: "/v1/config"

3
webui/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
.parcel-cache
node_modules
dist

View File

@@ -10,6 +10,11 @@ export type Config = {
plans?: Plan[]
}
export type User = {
name?: string
password?: string
}
export type Repo = {
id?: string
uri?: string
@@ -20,6 +25,5 @@ export type Repo = {
export type Plan = {
id?: string
repo?: string
repoPath?: string
paths?: string[]
}

View File

@@ -0,0 +1,55 @@
/* eslint-disable */
// @ts-nocheck
/*
* This file is a generated Typescript file for GRPC Gateway, DO NOT MODIFY
*/
type Absent<T, K extends keyof T> = { [k in Exclude<keyof T, K>]?: undefined };
type OneOf<T> =
| { [k in keyof T]?: undefined }
| (
keyof T extends infer K ?
(K extends string & keyof T ? { [k in K]: T[K] } & Absent<T, K>
: never)
: never);
export type ResticSnapshot = {
id?: string
unixTimeMs?: string
hostname?: string
username?: string
tree?: string
parent?: string
paths?: string[]
tags?: string[]
}
type BaseBackupProgressEntry = {
}
export type BackupProgressEntry = BaseBackupProgressEntry
& OneOf<{ status: BackupProgressStatusEntry; summary: BackupProgressSummary }>
export type BackupProgressStatusEntry = {
percentDone?: number
totalFiles?: string
totalBytes?: string
filesDone?: string
bytesDone?: string
}
export type BackupProgressSummary = {
filesNew?: string
filesChanged?: string
filesUnmodified?: string
dirsNew?: string
dirsChanged?: string
dirsUnmodified?: string
dataBlobs?: string
treeBlobs?: string
dataAdded?: string
totalFilesProcessed?: string
totalBytesProcessed?: string
totalDuration?: string
snapshotId?: string
}

3589
webui/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,28 @@
{
"name": "webui",
"name": "resticui",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "parcel serve src/index.html",
"build": "parcel build src/index.html",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
"license": "ISC",
"dependencies": {
"@types/lodash": "^4.14.191",
"@types/node": "^18.11.18",
"@types/react": "^18.0.26",
"@types/react-dom": "^18.0.10",
"axios": "^1.2.2",
"lodash": "^4.17.21",
"parcel": "^2.8.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"typescript": "^4.9.4"
},
"devDependencies": {
"buffer": "^5.7.1",
"process": "^0.11.10"
}
}

5
webui/src/index.css Normal file
View File

@@ -0,0 +1,5 @@
body {
font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue",
Helvetica, Arial, "Lucida Grande", sans-serif;
font-weight: 300;
}

12
webui/src/index.html Normal file
View File

@@ -0,0 +1,12 @@
<html>
<head>
<title>Guidebook FS Search</title>
<link rel="stylesheet" href="./index.css" />
</head>
<body>
<div id="app"></div>
<!-- The script tag below is just a place holder.
Parcel will perform magic to insert TSC output here: -->
<script src="index.tsx" type="module"></script>
</body>
</html>

6
webui/src/index.tsx Normal file
View File

@@ -0,0 +1,6 @@
import * as React from "react";
import { createRoot } from "react-dom/client";
import { App } from "./views/App";
const el = document.querySelector("#app");
el && createRoot(el).render(<App />);

24
webui/src/views/App.tsx Normal file
View File

@@ -0,0 +1,24 @@
import test from "node:test";
import * as React from "react";
import _ from "lodash";
export const App = () => {
const [searchTerm, setSearchTerm] = React.useState("");
const [results, setResults] = React.useState<any>();
return (
<div>
<h1>Guidebook FS Search</h1>
<div>
Search:
<input
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
></input>
</div>
<pre>
<code>{"RESULTS: " + JSON.stringify(results, null, 2)}</code>
</pre>
</div>
);
};

11
webui/tsconfig.json Normal file
View File

@@ -0,0 +1,11 @@
{
"compilerOptions": {
"jsx": "react",
"target": "es2016",
"module": "commonjs",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
}
}