backup-master: сервер бэкапов, агент и восстановление VPS

Один бинарник backupctl:
- server: API для агентов и restore, веб-морда (один админ), SQLite,
  ретеншн GFS, heavy-части, уведомления ntfy, раздача agent.sh/restore.sh;
- backup/plan: сбор по каталогу категорий (files/systemd/xui/compose),
  горячие дампы Postgres/Mongo/MySQL, снимок x-ui.db, docker-тома,
  digest образов, проверка crontab, потоковая отправка tar.zst;
- restore: провижининг на основе vps_installer с теми же версиями пакетов,
  recipes 3x-ui/nebula/sing-box нужных версий, восстановление compose
  с заливкой дампов и закреплением образов по digest.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
mrleo1nidandClaude Opus 5.5 committed 2026-10-06 21:52:40 +03:00
commit 1d44d067e9
53 files changed
+7278

No files matched your search

+110
View File
@@ -0,0 +1,110 @@
// Package manifest описывает метаданные бэкапа: что и откуда собрано, какие версии
// стояли на сервере. Манифест лежит первым файлом в main-архиве и отдельно хранится
// на сервере, чтобы restore мог спланировать восстановление до скачивания архива.
package manifest
import "time"
const FormatVersion = 1
// MainPart — имя основной части бэкапа. Остальные части — heavy-категории.
const MainPart = "main"
type Manifest struct {
FormatVersion int `json:"format_version"`
AgentVersion string `json:"agent_version"`
Domain string `json:"domain"`
Hostname string `json:"hostname"`
CreatedAt time.Time `json:"created_at"`
OS OSInfo `json:"os"`
Packages map[string]string `json:"packages,omitempty"` // apt-пакет → точная версия dpkg
Features Features `json:"features"`
SudoUsers []string `json:"sudo_users,omitempty"`
Categories []Category `json:"categories"`
Parts []string `json:"parts"` // main + heavy-части, вошедшие в этот бэкап
Warnings []string `json:"warnings,omitempty"`
}
type OSInfo struct {
ID string `json:"id"`
VersionID string `json:"version_id"`
PrettyName string `json:"pretty_name"`
Arch string `json:"arch"` // как dpkg --print-architecture: amd64, arm64
}
// Features — признаки базовой настройки vps_installer, которые нужно повторить.
type Features struct {
Blocklist bool `json:"blocklist"`
SSHHardened bool `json:"ssh_hardened"`
}
type Category struct {
Name string `json:"name"`
Type string `json:"type"`
Auto bool `json:"auto,omitempty"` // найдена автоматически, в каталоге не описана
Version string `json:"version,omitempty"`
Install string `json:"install,omitempty"` // recipe установки бинарника
Service *ServiceState `json:"service,omitempty"`
Paths []string `json:"paths,omitempty"` // абсолютные пути в <cat>/files/
Binaries []string `json:"binaries,omitempty"` // абсолютные пути в <cat>/bin/ (fallback)
Dumps []Dump `json:"dumps,omitempty"`
Volumes []Volume `json:"volumes,omitempty"`
Heavy []string `json:"heavy,omitempty"` // пути, ушедшие в отдельную часть <cat>
HeavySkipped bool `json:"heavy_skipped,omitempty"`
Compose *Compose `json:"compose,omitempty"`
EnsureDirs []string `json:"ensure_dirs,omitempty"`
PostRestore []string `json:"post_restore,omitempty"`
RestoreAfter []string `json:"restore_after,omitempty"`
}
type ServiceState struct {
Unit string `json:"unit"`
Enabled bool `json:"enabled"`
Active bool `json:"active"`
}
// Dump — файл в <cat>/dumps/. Kind: postgres, mongo, mysql, mariadb, sqlite, cmd.
type Dump struct {
Name string `json:"name"`
Kind string `json:"kind"`
Service string `json:"service,omitempty"` // compose-сервис
Target string `json:"target,omitempty"` // sqlite: куда положить файл при восстановлении
RestoreCmd string `json:"restore_cmd,omitempty"` // cmd: команда, получает DUMP_FILE
}
// Volume — именованный docker-том, содержимое в <cat>/volumes/<name>/.
type Volume struct {
Name string `json:"name"` // реальное имя тома в docker
Key string `json:"key"` // ключ в compose-файле
}
type Compose struct {
Project string `json:"project"`
Dir string `json:"dir"`
ConfigFiles []string `json:"config_files"` // абсолютные пути
Running bool `json:"running"`
Images map[string]Image `json:"images,omitempty"` // по имени сервиса
ExternalNetworks []string `json:"external_networks,omitempty"`
Git *GitInfo `json:"git,omitempty"`
}
type Image struct {
Ref string `json:"ref"`
Digest string `json:"digest,omitempty"` // repo@sha256:...
Built bool `json:"built,omitempty"`
}
type GitInfo struct {
Remote string `json:"remote,omitempty"`
Commit string `json:"commit"`
Dirty bool `json:"dirty,omitempty"`
}
// Archive-пути внутри tar.
func FilesPrefix(cat string) string { return cat + "/files/" }
func BinPrefix(cat string) string { return cat + "/bin/" }
func DumpsPrefix(cat string) string { return cat + "/dumps/" }
func VolumesPrefix(cat string) string { return cat + "/volumes/" }
const ManifestFile = "manifest.json"