Files
backup-master/internal/archive/exclude.go
T
mrleo1nidandClaude Opus 5.5 1d44d067e9 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>
2026-10-06 21:52:40 +03:00

72 lines
1.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package archive
import (
"path"
"strings"
)
// Matcher проверяет абсолютные пути (со слешами) по списку исключений.
//
// - шаблон без glob-символов — префикс: "/opt/x/pgdata" исключает и сам каталог, и всё внутри;
// - шаблон с "/" и glob-символами — сопоставляется с полным путём ("/etc/x-ui/x-ui.db*");
// - шаблон без "/" — с именем файла ("*.swp").
type Matcher struct {
patterns []string
}
func NewMatcher(patterns ...[]string) *Matcher {
m := &Matcher{}
for _, ps := range patterns {
for _, p := range ps {
p = strings.TrimSpace(p)
if p == "" {
continue
}
if p != "/" {
p = strings.TrimSuffix(p, "/")
}
m.patterns = append(m.patterns, p)
}
}
return m
}
func (m *Matcher) Add(p ...string) {
m.patterns = append(m.patterns, NewMatcher(p).patterns...)
}
func (m *Matcher) Match(abs string) bool {
if m == nil {
return false
}
for _, p := range m.patterns {
if !strings.ContainsAny(p, "*?[") {
if abs == p || strings.HasPrefix(abs, p+"/") {
return true
}
continue
}
if strings.Contains(p, "/") {
if ok, _ := path.Match(p, abs); ok {
return true
}
continue
}
if ok, _ := path.Match(p, path.Base(abs)); ok {
return true
}
}
return false
}
// Covers сообщает, попадает ли abs под один из путей (точное совпадение или вложенность).
func Covers(paths []string, abs string) bool {
for _, p := range paths {
p = strings.TrimSuffix(p, "/")
if abs == p || strings.HasPrefix(abs, p+"/") {
return true
}
}
return false
}