wip
This commit is contained in:
253
pkg/data/data.go
Normal file
253
pkg/data/data.go
Normal file
@@ -0,0 +1,253 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"cloudsave/pkg/remote/client"
|
||||
"cloudsave/pkg/repository"
|
||||
"cloudsave/pkg/tools/archive"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type (
|
||||
Service struct {
|
||||
repo repository.Repository
|
||||
}
|
||||
)
|
||||
|
||||
func NewService(repo repository.Repository) *Service {
|
||||
return &Service{
|
||||
repo: repo,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) Add(name, path, remote string) (string, error) {
|
||||
gameID := repository.NewGameIdentifier(uuid.NewString())
|
||||
|
||||
if err := s.repo.Mkdir(gameID); err != nil {
|
||||
return "", fmt.Errorf("failed to add game reference: %w", err)
|
||||
}
|
||||
|
||||
m := repository.Metadata{
|
||||
ID: gameID.Key(),
|
||||
Name: name,
|
||||
Path: path,
|
||||
Version: 1,
|
||||
Date: time.Now(),
|
||||
}
|
||||
|
||||
if err := s.repo.WriteMetadata(gameID, m); err != nil {
|
||||
return "", fmt.Errorf("failed to add game reference: %w", err)
|
||||
}
|
||||
|
||||
return gameID.Key(), nil
|
||||
}
|
||||
|
||||
func (s *Service) One(gameID string) (repository.Metadata, error) {
|
||||
id := repository.NewGameIdentifier(gameID)
|
||||
|
||||
m, err := s.repo.Metadata(id)
|
||||
if err != nil {
|
||||
return repository.Metadata{}, fmt.Errorf("failed to get metadata: %w", err)
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (s *Service) Backup(gameID, backupID string) (repository.Backup, error) {
|
||||
id := repository.NewBackupIdentifier(gameID, backupID)
|
||||
|
||||
return s.repo.Backup(id)
|
||||
}
|
||||
|
||||
func (s *Service) UpdateMetadata(gameID string, m repository.Metadata) error {
|
||||
id := repository.NewGameIdentifier(gameID)
|
||||
|
||||
if err := s.repo.WriteMetadata(id, m); err != nil {
|
||||
return fmt.Errorf("failed to write metadate: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) Scan(gameID string) error {
|
||||
id := repository.NewGameIdentifier(gameID)
|
||||
|
||||
lastRun, err := s.repo.LastScan(id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get last scan time: %w", err)
|
||||
}
|
||||
|
||||
m, err := s.repo.Metadata(id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get game metadata: %w", err)
|
||||
}
|
||||
|
||||
if !IsDirectoryChanged(m.Path, lastRun) {
|
||||
return nil
|
||||
}
|
||||
|
||||
f, err := s.repo.WriteBlob(id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get datastore stream: %w", err)
|
||||
}
|
||||
if v, ok := f.(io.Closer); ok {
|
||||
defer v.Close()
|
||||
}
|
||||
|
||||
if err := archive.Tar(f, m.Path); err != nil {
|
||||
return fmt.Errorf("failed to make archive: %w", err)
|
||||
}
|
||||
|
||||
if err := s.repo.ResetLastScan(id); err != nil {
|
||||
return fmt.Errorf("failed to reset scan date: %w", err)
|
||||
}
|
||||
|
||||
m.Date = time.Now()
|
||||
m.Version += 1
|
||||
|
||||
if err := s.repo.WriteMetadata(id, m); err != nil {
|
||||
return fmt.Errorf("failed to update metadata: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) AllGames() ([]repository.Metadata, error) {
|
||||
ids, err := s.repo.All()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get the list of ids: %w", err)
|
||||
}
|
||||
|
||||
var ms []repository.Metadata
|
||||
for _, id := range ids {
|
||||
m, err := s.repo.Metadata(repository.NewGameIdentifier(id))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open metadata: %w", err)
|
||||
}
|
||||
ms = append(ms, m)
|
||||
}
|
||||
|
||||
return ms, nil
|
||||
}
|
||||
|
||||
func (s *Service) AllBackups(gameID string) ([]repository.Backup, error) {
|
||||
ids, err := s.repo.AllHist(repository.NewGameIdentifier(gameID))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get the list of ids: %w", err)
|
||||
}
|
||||
|
||||
var bs []repository.Backup
|
||||
for _, id := range ids {
|
||||
b, err := s.repo.Backup(repository.NewBackupIdentifier(gameID, id))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open metadata: %w", err)
|
||||
}
|
||||
bs = append(bs, b)
|
||||
}
|
||||
|
||||
return bs, nil
|
||||
}
|
||||
|
||||
func (l Service) PullArchive(gameID, backupID string, cli *client.Client) error {
|
||||
if len(backupID) > 0 {
|
||||
path := l.repo.DataPath(repository.NewBackupIdentifier(gameID, backupID))
|
||||
return cli.PullBackup(gameID, backupID, filepath.Join(path, "data.tar.gz"))
|
||||
}
|
||||
|
||||
path := l.repo.DataPath(repository.NewGameIdentifier(gameID))
|
||||
return cli.Pull(gameID, filepath.Join(path, "data.tar.gz"))
|
||||
}
|
||||
|
||||
func (l Service) PullCurrent(id, path string, cli *client.Client) error {
|
||||
gameID := repository.NewGameIdentifier(id)
|
||||
if err := l.repo.Mkdir(gameID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m, err := cli.Metadata(id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get metadata from the server: %w", err)
|
||||
}
|
||||
|
||||
if err := l.repo.WriteMetadata(gameID, m); err != nil {
|
||||
return fmt.Errorf("failed to write metadata: %w", err)
|
||||
}
|
||||
|
||||
archivePath := filepath.Join(l.repo.DataPath(gameID), "data.tar.gz")
|
||||
|
||||
if err := cli.Pull(id, archivePath); err != nil {
|
||||
return fmt.Errorf("failed to pull from the server: %w", err)
|
||||
}
|
||||
|
||||
f, err := l.repo.ReadBlob(gameID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open blob from local repository: %w", err)
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(path, 0740); err != nil {
|
||||
return fmt.Errorf("failed to create destination directory: %w", err)
|
||||
}
|
||||
|
||||
if err := archive.Untar(f, path); err != nil {
|
||||
return fmt.Errorf("failed to untar archive: %w", err)
|
||||
}
|
||||
|
||||
if err := l.repo.ResetLastScan(gameID); err != nil {
|
||||
return fmt.Errorf("failed to create .last_run file: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l Service) PullBackup(gameID, backupID string, cli *client.Client) error {
|
||||
id := repository.NewBackupIdentifier(gameID, backupID)
|
||||
|
||||
archivePath := filepath.Join(l.repo.DataPath(id), "data.tar.gz")
|
||||
|
||||
if err := cli.PullBackup(gameID, backupID, archivePath); err != nil {
|
||||
return fmt.Errorf("failed to pull backup: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l Service) RemoveGame(gameID string) error {
|
||||
return l.repo.Remove(repository.NewGameIdentifier(gameID))
|
||||
}
|
||||
|
||||
func (l Service) SetVersion(gameID string, value int) error {
|
||||
id := repository.NewGameIdentifier(gameID)
|
||||
|
||||
m, err := l.repo.Metadata(id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get metadata from the server: %w", err)
|
||||
}
|
||||
|
||||
m.Version = value
|
||||
|
||||
if err := l.repo.WriteMetadata(id, m); err != nil {
|
||||
return fmt.Errorf("failed to write metadata: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func IsDirectoryChanged(path string, lastRun time.Time) bool {
|
||||
changed := false
|
||||
_ = filepath.Walk(path, func(path string, info os.FileInfo, walkErr error) error {
|
||||
if walkErr != nil {
|
||||
return nil
|
||||
}
|
||||
if info.ModTime().After(lastRun) {
|
||||
changed = true
|
||||
return io.EOF // early exit
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return changed
|
||||
}
|
||||
Reference in New Issue
Block a user