mirror of
https://github.com/status-im/status-go.git
synced 2026-08-31 09:01:16 +00:00
Part of the Go project layout migration, item 31. Pure move plus import-path rewrite across 687 files. No API or behaviour change. The services keep their grouping under pkg/services/<name> rather than being promoted to pkg/<name>: 27 top-level directories in pkg/ would read worse than what we have, and the grouping is what makes "an RPC service" identifiable at a glance. Paths that follow the move: the logosstorage test target and generate step, the two wallet token-list tools, the migration-order check (and the pre-rebase hook symlinked to it), and the storage env helper. refs #7067
187 lines
3.5 KiB
Go
187 lines
3.5 KiB
Go
package backup
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"sync"
|
|
"time"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
"github.com/status-im/status-go/internal/panics"
|
|
"github.com/status-im/status-go/internal/pausable"
|
|
"github.com/status-im/status-go/internal/signal"
|
|
)
|
|
|
|
//go:generate go tool mockgen -package=mock_backup_controller -source controller.go -destination=mock/mock_backup_controller.go
|
|
|
|
type Config struct {
|
|
PrivateKey []byte
|
|
FileNameProvider FilenameProvider
|
|
BackupEnabled bool
|
|
Interval time.Duration
|
|
}
|
|
|
|
type FilenameProvider interface {
|
|
GetBackupFilename() (string, error)
|
|
}
|
|
|
|
type Provider interface {
|
|
ExportBackup() ([]byte, error)
|
|
ImportBackup(data []byte) error
|
|
}
|
|
|
|
type Controller struct {
|
|
pausable.PauseBroadcaster
|
|
|
|
config Config
|
|
core *core
|
|
logger *zap.Logger
|
|
quit chan struct{}
|
|
mutex sync.Mutex
|
|
wg *sync.WaitGroup
|
|
}
|
|
|
|
type CompletedEvent struct {
|
|
FileName string
|
|
}
|
|
|
|
func (b CompletedEvent) MarshalJSON() ([]byte, error) {
|
|
responseItem := struct {
|
|
FileName string `json:"fileName,omitempty"`
|
|
}{
|
|
FileName: b.FileName,
|
|
}
|
|
return json.Marshal(responseItem)
|
|
}
|
|
|
|
func NewController(config Config, logger *zap.Logger) (*Controller, error) {
|
|
if len(config.PrivateKey) == 0 {
|
|
return nil, errors.New("private key must be provided")
|
|
}
|
|
if isNil(config.FileNameProvider) {
|
|
return nil, errors.New("filename provider must be provided")
|
|
}
|
|
|
|
return &Controller{
|
|
config: config,
|
|
core: newCore(),
|
|
logger: logger,
|
|
wg: &sync.WaitGroup{},
|
|
quit: make(chan struct{}),
|
|
}, nil
|
|
}
|
|
|
|
func (c *Controller) Register(componentName string, provider Provider) {
|
|
c.mutex.Lock()
|
|
defer c.mutex.Unlock()
|
|
|
|
c.core.Register(componentName, provider)
|
|
}
|
|
|
|
func (c *Controller) Start() {
|
|
if !c.config.BackupEnabled {
|
|
return
|
|
}
|
|
c.MarkStarted()
|
|
c.wg.Add(1)
|
|
|
|
go func() {
|
|
defer panics.LogOnPanic()
|
|
defer c.wg.Done()
|
|
sub := c.Subscribe()
|
|
defer sub.Unsubscribe()
|
|
pt := pausable.NewPausableTicker(pausable.PausableTickerConfig{
|
|
Interval: c.config.Interval,
|
|
OnTick: func() {
|
|
_, err := c.PerformBackup()
|
|
if err != nil {
|
|
c.logger.Error("Error performing backup", zap.Error(err))
|
|
}
|
|
},
|
|
}, sub.C())
|
|
pt.Run(c.quit)
|
|
}()
|
|
}
|
|
|
|
func (c *Controller) Stop() {
|
|
close(c.quit)
|
|
c.wg.Wait()
|
|
c.MarkStopped()
|
|
}
|
|
|
|
func (c *Controller) PerformBackup() (string, error) {
|
|
c.mutex.Lock()
|
|
defer c.mutex.Unlock()
|
|
|
|
backupData, err := c.core.Create(c.config.PrivateKey)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
fileName, err := c.config.FileNameProvider.GetBackupFilename()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if err := os.MkdirAll(filepath.Dir(fileName), 0700); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
file, err := os.Create(fileName)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer file.Close()
|
|
|
|
_, err = file.Write(backupData)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
signal.SendLocalBackUpCompleted(CompletedEvent{
|
|
FileName: fileName,
|
|
})
|
|
|
|
return fileName, nil
|
|
}
|
|
|
|
func (c *Controller) LoadBackup(filePath string) error {
|
|
c.mutex.Lock()
|
|
defer c.mutex.Unlock()
|
|
|
|
file, err := os.Open(filePath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer file.Close()
|
|
|
|
fileInfo, err := file.Stat()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
backupData := make([]byte, fileInfo.Size())
|
|
_, err = file.Read(backupData)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return c.core.Restore(c.config.PrivateKey, backupData)
|
|
}
|
|
|
|
// isNil reports whether i is a nil pointer or a nil value held in an interface.
|
|
func isNil(i interface{}) bool {
|
|
if i == nil {
|
|
return true
|
|
}
|
|
switch reflect.TypeOf(i).Kind() {
|
|
case reflect.Ptr, reflect.Interface:
|
|
return reflect.ValueOf(i).IsNil()
|
|
}
|
|
return false
|
|
}
|