cloudbackup

package
v1.2.9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 13, 2026 License: AGPL-3.0 Imports: 19 Imported by: 0

Documentation

Overview

Package cloudbackup is the user-facing service for cloud backup destinations. It owns:

  • building the JSON backup payload (a stable, ordered representation of the pilot's data);
  • CRUD over backup destinations (with AES-256-GCM encryption of credentials at rest);
  • executing one-off backup runs (BuildJSON → upload → retention prune → audit log);
  • a process-local scheduler that ticks every minute and queues due destinations.

The package depends only on the repository layer and the provider plugin registry. HTTP handlers, JWT, and Gin live elsewhere.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrDestinationNotFound = errors.New("backup destination not found")
	ErrProviderUnknown     = errors.New("unknown backup provider")
	ErrInvalidSchedule     = errors.New("invalid schedule")
	ErrUnauthorized        = errors.New("not authorized for this destination")
	ErrConcurrentRun       = errors.New("a backup is already running for this destination")
)

Errors returned by the service layer. These map cleanly onto HTTP status codes in the handler layer.

Functions

This section is empty.

Types

type BuildMetadata

type BuildMetadata struct {
	SHA256          string
	SizeBytes       int64
	FlightCount     int
	AircraftCount   int
	LicenseCount    int
	CredentialCount int
	// ContentType describes the bytes returned by BuildJSON. Currently always
	// "application/gzip" — JSON wrapped in gzip — but we expose it explicitly
	// so the provider can set the correct Content-Type header.
	ContentType string
	// Filename is a recommended object name, e.g. "ninerlog-backup-2025-...".
	Filename string
}

BuildMetadata accompanies a generated backup payload. The counts feed the BackupRun audit record and the SHA-256 powers the "skip if unchanged" optimization.

type CreateDestinationInput

type CreateDestinationInput struct {
	UserID             uuid.UUID
	Provider           string
	DisplayName        string
	Config             provider.Config
	Credentials        provider.Credentials
	Schedule           models.BackupSchedule
	ScheduleHourUTC    int
	ScheduleDayOfWeek  *int
	ScheduleDayOfMonth *int
	RetentionCount     int
	Enabled            bool
}

CreateDestinationInput is the service-level input for creating a backup destination. Credentials are accepted as a map of strings keyed by the provider's CredentialSchema.

type DefaultJSONBuilder

type DefaultJSONBuilder struct {
	Flights     *service.FlightService
	Aircraft    *service.AircraftService
	Licenses    *service.LicenseService
	Credentials *service.CredentialService
	ClassRating *service.ClassRatingService
	// AttachCrew is called with the flight slice before serialisation so the
	// crew-table fallback that the export pathway relies on still fires.
	// Optional.
	AttachCrew func(ctx context.Context, flights []*models.Flight)
	// SortFlights is called with the flight slice before serialisation.
	// Optional; defaults to chronological order (date, off-block, id).
	SortFlights func(flights []*models.Flight)
	// Version is embedded in the payload. Defaults to "1.0".
	Version string
	// Format is embedded in the payload. Defaults to "NinerLog JSON Backup".
	Format string
	// Now returns the timestamp used for exportedAt and the filename.
	// Defaults to time.Now().UTC.
	Now func() time.Time
}

DefaultJSONBuilder is the production implementation of JSONBuilder. It composes the existing per-resource services to produce a stable backup payload that matches the on-the-wire JSON export ExportDataJSON has emitted since v1.0.

Stability guarantees:

  • Top-level keys: exportedAt, version, format, flights, aircraft, licenses, credentials.
  • Flights are sorted chronologically (date, then off-block/departure).
  • Aircraft are sorted by registration.
  • Licenses are sorted by id; class ratings are sorted by id.
  • Credentials are sorted by id.
  • The exportedAt field is excluded from the SHA-256 fingerprint used for "skip if unchanged" so a re-run on an otherwise-identical dataset is correctly identified as a no-op.

func (*DefaultJSONBuilder) BuildJSON

func (b *DefaultJSONBuilder) BuildJSON(ctx context.Context, userID uuid.UUID) (io.ReadCloser, BuildMetadata, error)

BuildJSON gathers the user's data, serialises it to gzipped JSON, and returns a reader along with metadata for the BackupRun audit log.

type JSONBuilder

type JSONBuilder interface {
	BuildJSON(ctx context.Context, userID uuid.UUID) (io.ReadCloser, BuildMetadata, error)
}

JSONBuilder is implemented by anything that can produce the data half of a backup. The default implementation queries the existing flight / aircraft / license / credential services.

type Options

type Options struct {
	DestinationRepo repository.BackupDestinationRepository
	RunRepo         repository.BackupRunRepository
	Registry        *provider.Registry
	Crypto          *cryptoutil.AEAD
	Builder         JSONBuilder
	// Clock is the function used everywhere "now" is needed. Defaults to
	// time.Now.UTC.
	Clock func() time.Time
}

Options bundles the dependencies for New.

type RunRequest

type RunRequest struct {
	DestinationID uuid.UUID
	// UserID must match the destination's owner; the runner re-checks this
	// to guarantee scheduler bugs cannot leak data across tenants.
	UserID  uuid.UUID
	Trigger models.BackupRunTrigger
}

RunRequest describes a single backup execution.

type Scheduler

type Scheduler struct {
	// contains filtered or unexported fields
}

Scheduler is a process-local goroutine that periodically asks the repository for due destinations and dispatches them via Service.RunOnce.

Design notes:

  • One scheduler per API process. In a horizontally-scaled deployment (which we are not yet doing) you would want a leadership election or row-level lock; the current design only guarantees at-most-once per destination per process due to runLocks.
  • Ticks every minute, with a small jitter on the first tick to spread out load when multiple instances of the API restart simultaneously.
  • Per-user concurrency cap: at most one run per user can be in flight across all of their destinations.

func NewScheduler

func NewScheduler(svc *Service, tick time.Duration, logger *log.Logger) *Scheduler

NewScheduler constructs a scheduler attached to svc. tick is the period of the loop; pass 0 for the default 60s.

func (*Scheduler) Start

func (s *Scheduler) Start(ctx context.Context)

Start launches the scheduler loop in a goroutine. Cancel the supplied context (or call Stop) to terminate.

func (*Scheduler) Stop

func (s *Scheduler) Stop()

Stop signals the loop to exit and waits for it to drain in-flight ticks.

type Service

type Service struct {
	// contains filtered or unexported fields
}

Service is the cloud backup application service. Construct via New.

func New

func New(opts Options) (*Service, error)

New constructs a Service. Returns an error if any required dependency is missing.

func (*Service) CreateDestination

func (s *Service) CreateDestination(ctx context.Context, in CreateDestinationInput) (*models.BackupDestination, error)

CreateDestination validates the provider/config/credentials combination, authenticates against the provider, encrypts the credential blob, and persists the destination. The supplied credentials are never written to disk in plaintext nor returned in the response.

func (*Service) DeleteDestination

func (s *Service) DeleteDestination(ctx context.Context, destinationID, userID uuid.UUID) error

DeleteDestination removes a destination (and cascades to its runs).

func (*Service) GetDestination

func (s *Service) GetDestination(ctx context.Context, destinationID, userID uuid.UUID) (*models.BackupDestination, error)

GetDestination returns one destination by ID, scoped to the user.

func (*Service) GetRun

func (s *Service) GetRun(ctx context.Context, runID, userID uuid.UUID) (*models.BackupRun, error)

GetRun returns a single run record, scoped to the user.

func (*Service) ListDestinations

func (s *Service) ListDestinations(ctx context.Context, userID uuid.UUID) ([]*models.BackupDestination, error)

ListDestinations returns all destinations owned by the user.

func (*Service) ListProviders

func (s *Service) ListProviders() []provider.Provider

ListProviders returns all registered providers, sorted by name.

func (*Service) ListRuns

func (s *Service) ListRuns(ctx context.Context, destinationID, userID uuid.UUID, page, pageSize int) ([]*models.BackupRun, int, error)

ListRuns returns audit-log rows for one destination scoped to the user.

func (*Service) Provider

func (s *Service) Provider(name string) (provider.Provider, error)

Provider returns the registered provider by name, or ErrProviderUnknown.

func (*Service) RunOnce

func (s *Service) RunOnce(ctx context.Context, req RunRequest) (*models.BackupRun, error)

RunOnce executes a single backup and records the result. It is safe to call concurrently; the second concurrent call against the same destination returns ErrConcurrentRun.

The high-level flow is:

  1. Look up & authorise the destination.
  2. Build the JSON payload.
  3. Optionally short-circuit ("skipped") if the payload hash matches the last successful run.
  4. Upload via the provider.
  5. Prune old objects to honour RetentionCount.
  6. Persist a BackupRun row and update the destination's status.

Errors at any step are recorded as a "failed" BackupRun with a sanitised message; the function returns the original error so callers can surface it.

func (*Service) SetScheduler

func (s *Service) SetScheduler(sc *Scheduler)

SetScheduler attaches a scheduler to this service so that handler-side "destination updated" hints can propagate. Returning the scheduler also lets callers Start/Stop it during process lifecycle.

func (*Service) TestDestination

func (s *Service) TestDestination(ctx context.Context, destinationID, userID uuid.UUID) (bool, string, error)

TestDestination decrypts the stored credentials and re-runs the provider's Validate routine. It also updates last_error / status to reflect the result so the UI's freshly-displayed status matches reality.

func (*Service) UpdateDestination

func (s *Service) UpdateDestination(ctx context.Context, destinationID, userID uuid.UUID, in UpdateDestinationInput) (*models.BackupDestination, error)

UpdateDestination applies a partial update. Credentials are intentionally not modifiable here — to rotate, delete and recreate the destination.

type UpdateDestinationInput

type UpdateDestinationInput struct {
	DisplayName        *string
	Schedule           *models.BackupSchedule
	ScheduleHourUTC    *int
	ScheduleDayOfWeek  *int
	ScheduleDayOfMonth *int
	RetentionCount     *int
	Enabled            *bool
}

UpdateDestinationInput is the service-level input for partial updates. All pointer fields are optional.

Directories

Path Synopsis
Package netguard restricts the network destinations the cloud-backup subsystem is allowed to connect to, mitigating server-side request forgery (SSRF).
Package netguard restricts the network destinations the cloud-backup subsystem is allowed to connect to, mitigating server-side request forgery (SSRF).
Package provider defines the contract every cloud backup provider plugin must satisfy.
Package provider defines the contract every cloud backup provider plugin must satisfy.
s3
Package s3 implements the cloud backup provider contract against Amazon S3 and S3-compatible object stores (MinIO, Backblaze B2, Cloudflare R2, Wasabi, etc.) using the minio-go client.
Package s3 implements the cloud backup provider contract against Amazon S3 and S3-compatible object stores (MinIO, Backblaze B2, Cloudflare R2, Wasabi, etc.) using the minio-go client.
sftp
Package sftp implements the cloud backup provider contract against any SSH server with SFTP enabled (OpenSSH, Dropbear, atmoz/sftp, hosting providers offering SFTP-only accounts, etc.) using golang.org/x/crypto/ssh and github.com/pkg/sftp.
Package sftp implements the cloud backup provider contract against any SSH server with SFTP enabled (OpenSSH, Dropbear, atmoz/sftp, hosting providers offering SFTP-only accounts, etc.) using golang.org/x/crypto/ssh and github.com/pkg/sftp.
webdav
Package webdav implements the cloud backup provider contract against any RFC 4918 compliant WebDAV server (Nextcloud, ownCloud, mailbox.org, Box, Apache mod_dav, …) using the studio-b12/gowebdav client.
Package webdav implements the cloud backup provider contract against any RFC 4918 compliant WebDAV server (Nextcloud, ownCloud, mailbox.org, Box, Apache mod_dav, …) using the studio-b12/gowebdav client.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL