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 ¶
- Variables
- type BuildMetadata
- type CreateDestinationInput
- type CustomCurrencyRule
- type DefaultJSONBuilder
- type FlightBaseline
- type JSONBuilder
- type LicenseWithRatings
- type NotificationPreferences
- type Options
- type Payload
- type RunRequest
- type Scheduler
- type Service
- func (s *Service) CreateDestination(ctx context.Context, in CreateDestinationInput) (*models.BackupDestination, error)
- func (s *Service) DeleteDestination(ctx context.Context, destinationID, userID uuid.UUID) error
- func (s *Service) GetDestination(ctx context.Context, destinationID, userID uuid.UUID) (*models.BackupDestination, error)
- func (s *Service) GetRun(ctx context.Context, runID, userID uuid.UUID) (*models.BackupRun, error)
- func (s *Service) ListDestinations(ctx context.Context, userID uuid.UUID) ([]*models.BackupDestination, error)
- func (s *Service) ListProviders() []provider.Provider
- func (s *Service) ListRuns(ctx context.Context, destinationID, userID uuid.UUID, page, pageSize int) ([]*models.BackupRun, int, error)
- func (s *Service) Provider(name string) (provider.Provider, error)
- func (s *Service) RunOnce(ctx context.Context, req RunRequest) (*models.BackupRun, error)
- func (s *Service) SetScheduler(sc *Scheduler)
- func (s *Service) TestDestination(ctx context.Context, destinationID, userID uuid.UUID) (bool, string, error)
- func (s *Service) UpdateDestination(ctx context.Context, destinationID, userID uuid.UUID, ...) (*models.BackupDestination, error)
- type UpdateDestinationInput
Constants ¶
This section is empty.
Variables ¶
var ( ErrDestinationNotFound = errors.New("backup destination not found") ErrProviderUnknown = errors.New("unknown backup provider") ErrInvalidSchedule = errors.New("invalid schedule") 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 ("application/gzip").
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 CustomCurrencyRule ¶ added in v1.3.7
type CustomCurrencyRule struct {
Name string `json:"name"`
Description *string `json:"description,omitempty"`
Emoji *string `json:"emoji,omitempty"`
Definition models.CustomCurrencyRuleBody `json:"definition"`
Enabled bool `json:"enabled"`
Notify bool `json:"notify"`
}
CustomCurrencyRule is the portable half of a user-authored currency rule. Sharing state (isShared, shareToken, importedFrom) is deliberately excluded: a share token is unique across the installation and belongs to the rule it was minted for, not to a copy restored elsewhere.
func NewCustomCurrencyRule ¶ added in v1.3.7
func NewCustomCurrencyRule(r *models.CustomCurrencyRule) CustomCurrencyRule
NewCustomCurrencyRule projects a stored rule onto its portable half.
type DefaultJSONBuilder ¶
type DefaultJSONBuilder struct {
Flights *service.FlightService
Aircraft *service.AircraftService
Licenses *service.LicenseService
Credentials *service.CredentialService
ClassRating *service.ClassRatingService
// Contacts, CustomCurrency and Notifications back the payload sections of
// the same name. A nil service omits its section rather than failing the
// backup.
Contacts *service.ContactService
CustomCurrency *currency.CustomService
Notifications *service.NotificationService
// AttachCrew is called with the flight slice before serialisation.
// 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 per-resource services to produce a stable backup payload matching the ExportDataJSON wire format.
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".
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 FlightBaseline ¶ added in v1.3.7
type FlightBaseline struct {
BaselineDate time.Time `json:"baselineDate"`
TotalFlights int `json:"totalFlights"`
TotalMinutes int `json:"totalMinutes"`
PICMinutes int `json:"picMinutes"`
SICMinutes int `json:"sicMinutes"`
DualMinutes int `json:"dualMinutes"`
DualGivenMinutes int `json:"dualGivenMinutes"`
MultiPilotMinutes int `json:"multiPilotMinutes"`
NightMinutes int `json:"nightMinutes"`
IFRMinutes int `json:"ifrMinutes"`
SoloMinutes int `json:"soloMinutes"`
CrossCountryMinutes int `json:"crossCountryMinutes"`
PICUSMinutes int `json:"picusMinutes"`
SPICMinutes int `json:"spicMinutes"`
ExaminerMinutes int `json:"examinerMinutes"`
ReliefMinutes int `json:"reliefMinutes"`
LandingsDay int `json:"landingsDay"`
LandingsNight int `json:"landingsNight"`
Notes *string `json:"notes,omitempty"`
}
FlightBaseline is the portable half of a user's carried-forward hours snapshot. models.FlightBaseline carries no JSON tags, so the wire shape is declared here.
func NewFlightBaseline ¶ added in v1.3.7
func NewFlightBaseline(b *models.FlightBaseline) *FlightBaseline
NewFlightBaseline projects a stored baseline onto its portable half.
func (FlightBaseline) ToModel ¶ added in v1.3.7
func (b FlightBaseline) ToModel(userID uuid.UUID) *models.FlightBaseline
ToModel rebuilds a storable baseline owned by the given user.
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 LicenseWithRatings ¶ added in v1.3.7
type LicenseWithRatings struct {
License *models.License `json:"license"`
ClassRatings []*models.ClassRating `json:"classRatings"`
}
LicenseWithRatings pairs a licence with its class ratings so a restore can wire ratings to freshly minted licence IDs.
type NotificationPreferences ¶ added in v1.3.7
type NotificationPreferences struct {
EmailEnabled bool `json:"emailEnabled"`
EnabledCategories []string `json:"enabledCategories"`
WarningDays []int64 `json:"warningDays"`
CheckHour int `json:"checkHour"`
}
NotificationPreferences is the portable half of a user's notification settings; identifiers and timestamps are reassigned on restore.
func NewNotificationPreferences ¶ added in v1.3.7
func NewNotificationPreferences(p *models.NotificationPreferences) *NotificationPreferences
NewNotificationPreferences projects stored preferences onto their portable half.
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 Payload ¶ added in v1.3.7
type Payload struct {
ExportedAt string `json:"exportedAt"`
Version string `json:"version"`
Format string `json:"format"`
Flights []*models.Flight `json:"flights"`
Aircraft []*models.Aircraft `json:"aircraft"`
Licenses []LicenseWithRatings `json:"licenses"`
Credentials []*models.Credential `json:"credentials"`
Contacts []*models.Contact `json:"contacts"`
CustomCurrencyRules []CustomCurrencyRule `json:"customCurrencyRules"`
// NotificationPreferences and FlightBaseline are single-row settings and
// are omitted when the user has none.
NotificationPreferences *NotificationPreferences `json:"notificationPreferences,omitempty"`
FlightBaseline *FlightBaseline `json:"flightBaseline,omitempty"`
}
Payload is the wire layout of one backup, shared by GET /exports/json, POST /imports/json and every cloud backup run. Field order matches the original ExportDataJSON output; new sections are appended.
Every section holds data a user owns and would expect to survive moving to another server. Sections added here must be restored by ImportDataJSON in the same change.
type RunRequest ¶
type RunRequest struct {
DestinationID uuid.UUID
// UserID must match the destination's owner; the runner re-checks it.
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.
- One scheduler per API process; runLocks guarantees at-most-once per destination per process.
- Ticks every minute, with a small delay on the first tick.
- Per-user concurrency cap: at most one run per user can be in flight across all of their destinations.
func NewScheduler ¶
NewScheduler constructs a scheduler attached to svc. tick is the period of the loop; pass 0 for the default 60s.
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service is the cloud backup application service. Construct via New.
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 ¶
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) 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 ¶
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) RunOnce ¶
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:
- Look up & authorise the destination.
- Build the JSON payload.
- Optionally short-circuit ("skipped") if the payload hash matches the last successful run.
- Upload via the provider.
- Prune old objects to honour RetentionCount.
- 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.
func (*Service) SetScheduler ¶
SetScheduler attaches a scheduler to this service.
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, updating last_error / status to reflect the result.
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 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.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package netguard restricts the network destinations the cloud-backup subsystem is allowed to connect to (SSRF mitigation).
|
Package netguard restricts the network destinations the cloud-backup subsystem is allowed to connect to (SSRF mitigation). |
|
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. |