package model import ( "strings" "time" ) const SchemaVersion = 1 type JobType string const ( JobShare JobType = "share" JobAppdata JobType = "appdata" JobDocker JobType = "docker" JobVM JobType = "vm" JobFlash JobType = "flash" JobRsync JobType = "rsync" ) type RepositoryType string const ( RepositoryLocal RepositoryType = "local" RepositorySMB RepositoryType = "smb" RepositoryNFS RepositoryType = "nfs" RepositorySFTP RepositoryType = "sftp" ) type Config struct { SchemaVersion int `json:"schemaVersion"` Jobs []Job `json:"jobs"` Repositories []Repository `json:"repositories"` Notifications []NotificationTarget `json:"notifications"` Settings Settings `json:"settings"` } type Settings struct { ResticPath string `json:"resticPath"` RsyncPath string `json:"rsyncPath"` RestoreRoot string `json:"restoreRoot"` PersistentLogDir string `json:"persistentLogDir,omitempty"` ShowLiveLog bool `json:"showLiveLog"` CatchUpWindowHrs int `json:"catchUpWindowHours"` CheckCron string `json:"checkCron"` PruneCron string `json:"pruneCron"` } type Job struct { SchemaVersion int `json:"schemaVersion"` ID string `json:"id"` Name string `json:"name"` Type JobType `json:"type"` Enabled bool `json:"enabled"` RepositoryID string `json:"repositoryId"` Sources []Source `json:"sources"` Schedule Schedule `json:"schedule"` Consistency Consistency `json:"consistency"` Compression string `json:"compression"` CPUCores int `json:"cpuCores,omitempty"` ReadConcurrency int `json:"readConcurrency,omitempty"` Excludes []string `json:"excludes,omitempty"` Tags []string `json:"tags,omitempty"` FlashImage bool `json:"flashImage,omitempty"` Retention Retention `json:"retention"` NotifyOn []string `json:"notifyOn,omitempty"` ShutdownSecs int `json:"shutdownTimeoutSeconds"` Rsync RsyncOptions `json:"rsync,omitempty"` } type RsyncOptions struct { Target string `json:"target"` Overwrite bool `json:"overwrite"` Delete bool `json:"delete"` PreservePermissions bool `json:"preservePermissions"` PreserveOwner bool `json:"preserveOwner"` PreserveGroup bool `json:"preserveGroup"` PreserveTimes bool `json:"preserveTimes"` PreserveLinks bool `json:"preserveLinks"` PreserveACLs bool `json:"preserveAcls"` PreserveXattrs bool `json:"preserveXattrs"` Checksum bool `json:"checksum"` DryRun bool `json:"dryRun"` SkipSpaceCheck bool `json:"skipSpaceCheck"` } type Source struct { Path string `json:"path,omitempty"` WorkloadID string `json:"workloadId,omitempty"` Metadata map[string]string `json:"metadata,omitempty"` } type Schedule struct { Cron string `json:"cron"` Timezone string `json:"timezone,omitempty"` } type Consistency struct { Mode string `json:"mode"` } type Retention struct { KeepWithinDays int `json:"keepWithinDays,omitempty"` Daily int `json:"daily"` Weekly int `json:"weekly"` Monthly int `json:"monthly"` } type Repository struct { SchemaVersion int `json:"schemaVersion"` ID string `json:"id"` Name string `json:"name"` Type RepositoryType `json:"type"` Location string `json:"location"` PasswordRef string `json:"passwordRef"` CredentialRef string `json:"credentialRef,omitempty"` Mount *MountConfig `json:"mount,omitempty"` Options map[string]string `json:"options,omitempty"` } type MountConfig struct { Managed bool `json:"managed"` Remote string `json:"remote,omitempty"` MountPoint string `json:"mountPoint,omitempty"` ReadOnly bool `json:"readOnly,omitempty"` } type NotificationTarget struct { SchemaVersion int `json:"schemaVersion"` ID string `json:"id"` Name string `json:"name"` Type string `json:"type"` Enabled bool `json:"enabled"` URL string `json:"url,omitempty"` Topic string `json:"topic,omitempty"` TokenRef string `json:"tokenRef,omitempty"` Events []string `json:"events,omitempty"` } type Run struct { SchemaVersion int `json:"schemaVersion"` ID string `json:"id"` JobID string `json:"jobId,omitempty"` TaskType string `json:"taskType"` Status string `json:"status"` Priority int `json:"priority"` CreatedAt time.Time `json:"createdAt"` StartedAt *time.Time `json:"startedAt,omitempty"` FinishedAt *time.Time `json:"finishedAt,omitempty"` SnapshotID string `json:"snapshotId,omitempty"` Message string `json:"message,omitempty"` ErrorCode string `json:"errorCode,omitempty"` BytesAdded int64 `json:"bytesAdded,omitempty"` FilesNew int64 `json:"filesNew,omitempty"` FilesChanged int64 `json:"filesChanged,omitempty"` BytesProcessed int64 `json:"bytesProcessed,omitempty"` FilesProcessed int64 `json:"filesProcessed,omitempty"` RetentionBefore int `json:"retentionBefore,omitempty"` RetentionAfter int `json:"retentionAfter,omitempty"` RetentionRemoved int `json:"retentionRemoved,omitempty"` RepositoryBefore int64 `json:"repositoryBefore,omitempty"` RepositoryAfter int64 `json:"repositoryAfter,omitempty"` RepositoryFreed int64 `json:"repositoryFreed,omitempty"` ProgressPercent float64 `json:"progressPercent,omitempty"` ProgressBytes int64 `json:"progressBytes,omitempty"` ProgressTotal int64 `json:"progressTotal,omitempty"` ProgressFiles int64 `json:"progressFiles,omitempty"` ProgressFileTotal int64 `json:"progressFileTotal,omitempty"` BytesPerSecond float64 `json:"bytesPerSecond,omitempty"` SecondsRemaining int64 `json:"secondsRemaining,omitempty"` CurrentFile string `json:"currentFile,omitempty"` LiveLog []string `json:"liveLog,omitempty"` } type RestoreTask struct { SchemaVersion int `json:"schemaVersion"` ID string `json:"id"` RepositoryID string `json:"repositoryId"` SnapshotID string `json:"snapshotId"` Includes []string `json:"includes"` Target string `json:"target"` InPlace bool `json:"inPlace"` Confirmed bool `json:"confirmed"` } type Snapshot struct { ID string `json:"id"` ShortID string `json:"short_id"` Time time.Time `json:"time"` Hostname string `json:"hostname"` Paths []string `json:"paths"` Tags []string `json:"tags"` } type RepositoryStats struct { RepositoryID string `json:"repositoryId"` RepositoryName string `json:"repositoryName"` StoredBytes int64 `json:"storedBytes"` FileCount int64 `json:"fileCount"` SnapshotCount int `json:"snapshotCount"` Error string `json:"error,omitempty"` } func DefaultConfig() Config { return Config{ SchemaVersion: SchemaVersion, Jobs: []Job{}, Repositories: []Repository{}, Notifications: []NotificationTarget{{SchemaVersion: SchemaVersion, ID: "unraid", Name: "Unraid", Type: "unraid", Enabled: true, Events: []string{"success", "warning", "failed"}}}, Settings: Settings{ ResticPath: "/usr/local/libexec/urbm/restic", RsyncPath: "/usr/bin/rsync", RestoreRoot: "/mnt/user/urbm-restores", CatchUpWindowHrs: 24, CheckCron: "0 3 1 * *", PruneCron: "0 4 * * 0", }, } } func NormalizeConfig(c Config) Config { if strings.TrimSpace(c.Settings.RsyncPath) == "" { c.Settings.RsyncPath = "/usr/bin/rsync" } notifications := make([]NotificationTarget, 0, len(c.Notifications)+1) hasUnraid := false for _, target := range c.Notifications { if target.Type == "ntfy" { continue } if target.Type == "unraid" { hasUnraid = true } notifications = append(notifications, target) } if !hasUnraid { notifications = append(notifications, NotificationTarget{SchemaVersion: SchemaVersion, ID: "unraid", Name: "Unraid", Type: "unraid", Enabled: true, Events: []string{"success", "warning", "failed"}}) } c.Notifications = notifications return c }