Один бинарник 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>
45 lines
1.1 KiB
Go
45 lines
1.1 KiB
Go
package restore
|
|
|
|
import "gitea.hsrv.site/mrleo1nid/backup-master/internal/manifest"
|
|
|
|
// order сортирует категории так, чтобы restore_after шли раньше зависящих от них.
|
|
// Порядок манифеста сохраняется, отсутствующие зависимости игнорируются, при цикле
|
|
// оставшиеся категории идут в исходном порядке.
|
|
func order(cats []manifest.Category) []manifest.Category {
|
|
idx := map[string]int{}
|
|
for i, c := range cats {
|
|
idx[c.Name] = i
|
|
}
|
|
done := make([]bool, len(cats))
|
|
var out []manifest.Category
|
|
for len(out) < len(cats) {
|
|
progressed := false
|
|
for i, c := range cats {
|
|
if done[i] {
|
|
continue
|
|
}
|
|
ready := true
|
|
for _, dep := range c.RestoreAfter {
|
|
if j, ok := idx[dep]; ok && !done[j] && j != i {
|
|
ready = false
|
|
break
|
|
}
|
|
}
|
|
if ready {
|
|
done[i] = true
|
|
out = append(out, c)
|
|
progressed = true
|
|
}
|
|
}
|
|
if !progressed {
|
|
for i, c := range cats {
|
|
if !done[i] {
|
|
done[i] = true
|
|
out = append(out, c)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return out
|
|
}
|