Агенты подхватывают изменения с сервера: каталог и самообновление
CI / test (push) Successful in 1m29s
CI / test (push) Successful in 1m29s
- сервер отдаёт агенту итоговый каталог (встроенный каталог сервера + переопределения клиента); если каталог не подходит версии агента — встроенный каталог агента и предупреждение - самообновление перед бэкапом: версия сервера новее → скачивание с того же сервера (только /dl/...), проверка sha256 и запуска новой версии, подмена с откатом, перезапуск через exec; при ошибке бэкап идёт текущей версией - галочка «Автообновление» у клиента (по умолчанию включена), версия агента в списке и карточке, отставшие подсвечены; версия сервера в шапке - миграция существующей базы: колонки auto_update и agent_version - backupctl backup --no-update Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1947b26913
commit
488abc2d34
21 files changed
+674
-51
No files matched your search
@@ -94,7 +94,38 @@ func OpenStore(path string) (*Store, error) {
|
||||
db.Close()
|
||||
return nil, fmt.Errorf("миграция: %w", err)
|
||||
}
|
||||
return &Store{db: db}, nil
|
||||
st := &Store{db: db}
|
||||
// колонки, добавленные после первых релизов: у существующих баз их нет
|
||||
for _, col := range []struct{ table, name, def string }{
|
||||
{"clients", "auto_update", "INTEGER NOT NULL DEFAULT 1"},
|
||||
{"clients", "agent_version", "TEXT NOT NULL DEFAULT ''"},
|
||||
} {
|
||||
if err := st.ensureColumn(col.table, col.name, col.def); err != nil {
|
||||
db.Close()
|
||||
return nil, fmt.Errorf("миграция %s.%s: %w", col.table, col.name, err)
|
||||
}
|
||||
}
|
||||
return st, nil
|
||||
}
|
||||
|
||||
func (s *Store) ensureColumn(table, name, def string) error {
|
||||
rows, err := s.db.Query(`SELECT name FROM pragma_table_info(?)`, table)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
found := false
|
||||
for rows.Next() {
|
||||
var n string
|
||||
if rows.Scan(&n) == nil && n == name {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
rows.Close()
|
||||
if found {
|
||||
return nil
|
||||
}
|
||||
_, err = s.db.Exec(fmt.Sprintf(`ALTER TABLE %s ADD COLUMN %s %s`, table, name, def))
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Store) Close() error { return s.db.Close() }
|
||||
@@ -119,17 +150,20 @@ type Client struct {
|
||||
LastErrorAt int64
|
||||
StaleNotified bool
|
||||
Disabled bool
|
||||
AutoUpdate bool // агент сам обновляется до версии сервера
|
||||
AgentVersion string // версия агента при последнем обращении
|
||||
}
|
||||
|
||||
const clientCols = `id, name, domain, token_hash, config_yaml, keep_daily, keep_weekly, keep_monthly,
|
||||
heavy_keep, heavy_every_days, stale_hours, created_at, last_backup_at, last_heavy_at, last_seen_at,
|
||||
last_error, last_error_at, stale_notified, disabled`
|
||||
last_error, last_error_at, stale_notified, disabled, auto_update, agent_version`
|
||||
|
||||
func scanClient(row interface{ Scan(...any) error }) (*Client, error) {
|
||||
var c Client
|
||||
err := row.Scan(&c.ID, &c.Name, &c.Domain, &c.TokenHash, &c.ConfigYAML, &c.KeepDaily, &c.KeepWeekly,
|
||||
&c.KeepMonthly, &c.HeavyKeep, &c.HeavyEveryDays, &c.StaleHours, &c.CreatedAt, &c.LastBackupAt,
|
||||
&c.LastHeavyAt, &c.LastSeenAt, &c.LastError, &c.LastErrorAt, &c.StaleNotified, &c.Disabled)
|
||||
&c.LastHeavyAt, &c.LastSeenAt, &c.LastError, &c.LastErrorAt, &c.StaleNotified, &c.Disabled,
|
||||
&c.AutoUpdate, &c.AgentVersion)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
@@ -172,9 +206,10 @@ func (s *Store) ListClients() ([]*Client, error) {
|
||||
|
||||
func (s *Store) UpdateClientSettings(c *Client) error {
|
||||
_, err := s.db.Exec(`UPDATE clients SET name = ?, config_yaml = ?, keep_daily = ?, keep_weekly = ?,
|
||||
keep_monthly = ?, heavy_keep = ?, heavy_every_days = ?, stale_hours = ?, disabled = ? WHERE id = ?`,
|
||||
keep_monthly = ?, heavy_keep = ?, heavy_every_days = ?, stale_hours = ?, disabled = ?, auto_update = ?
|
||||
WHERE id = ?`,
|
||||
c.Name, c.ConfigYAML, c.KeepDaily, c.KeepWeekly, c.KeepMonthly, c.HeavyKeep, c.HeavyEveryDays,
|
||||
c.StaleHours, c.Disabled, c.ID)
|
||||
c.StaleHours, c.Disabled, c.AutoUpdate, c.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -193,6 +228,11 @@ func (s *Store) TouchClient(id int64) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Store) SetAgentVersion(id int64, version string) error {
|
||||
_, err := s.db.Exec(`UPDATE clients SET agent_version = ? WHERE id = ?`, version, id)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Store) MarkClientSuccess(id int64, at int64, heavy bool) error {
|
||||
q := `UPDATE clients SET last_backup_at = ?1, last_error = '', last_error_at = 0, stale_notified = 0`
|
||||
if heavy {
|
||||
|
||||
Reference in new issue
Block a user