"""Резервное копирование: запуск, история, скачивание.""" import flet as ft from app.core import backup as bk from app.core import servers as servers_core from app.core.tasks import tasks from app.ui.components import append_log, log_view, snack class BackupsPage(ft.Column): def __init__(self, page: ft.Page, go): super().__init__(expand=True, scroll=ft.ScrollMode.AUTO, spacing=12) self._page = page self.srv_dd = ft.Dropdown(label="Сервер", width=250, options=[ ft.dropdown.Option(str(s.id), s.name) for s in servers_core.list_servers() ]) self.paths_tf = ft.TextField( label="Пути (через пробел)", value="/etc", width=400, hint_text="/etc /home/user /var/www", ) self.download_cb = ft.Checkbox(label="Скачать на этот ПК", value=True) self.log = log_view() self.log_container = ft.Container( height=180, border=ft.Border.all(1, ft.Colors.GREY_800), border_radius=8, padding=8, content=self.log, visible=False) self.table = ft.DataTable(columns=[ ft.DataColumn(ft.Text("Дата")), ft.DataColumn(ft.Text("Сервер")), ft.DataColumn(ft.Text("Пути")), ft.DataColumn(ft.Text("Размер")), ft.DataColumn(ft.Text("Статус")), ft.DataColumn(ft.Text("Файл")), ], rows=[]) count, size = bk.stats() self.stats_text = ft.Text(f"Всего бэкапов: {count}, суммарно {size/1048576:.1f} МБ", size=13, color=ft.Colors.GREY_500) self.controls = [ ft.Text("Резервное копирование", size=28, weight=ft.FontWeight.BOLD), ft.Row([self.srv_dd, self.paths_tf, self.download_cb, ft.FilledButton("Создать бэкап", icon=ft.Icons.BACKUP, on_click=self._run)]), self.stats_text, self.log_container, ft.Text("История", weight=ft.FontWeight.BOLD, size=16), ft.Card(content=ft.Container(padding=10, content=self.table)), ] self._reload() def _reload(self): servers = {s.id: s.name for s in servers_core.list_servers()} self.table.rows = [] for r in bk.list_backups(): color = ft.Colors.GREEN_300 if r.status == "success" else \ ft.Colors.RED_300 if r.status == "error" else ft.Colors.GREY_500 self.table.rows.append(ft.DataRow(cells=[ ft.DataCell(ft.Text(f"{r.created_at:%d.%m %H:%M}", size=11)), ft.DataCell(ft.Text(servers.get(r.server_id, "?"), size=12)), ft.DataCell(ft.Text(r.paths, size=11)), ft.DataCell(ft.Text(f"{r.size/1048576:.1f} МБ" if r.size else "—", size=11)), ft.DataCell(ft.Text(r.status, size=11, color=color)), ft.DataCell(ft.Text(r.file or "—", size=10, color=ft.Colors.GREY_500)), ])) self._page.update() def _run(self, e): if not self.srv_dd.value: snack(self._page, "Выберите сервер", error=True) return srv = servers_core.get_server(int(self.srv_dd.value)) paths = self.paths_tf.value.strip() if not paths: snack(self._page, "Укажите пути", error=True) return self.log_container.visible = True self._page.update() async def job(log): rec = await bk.run_backup(srv, paths, self.download_cb.value, log) log(f"=== {rec.status} ===" if rec else "=== error ===") tasks.start("backup", f"бэкап {paths}", srv.id, job, on_log=lambda l: (append_log(self.log, l), self._page.update()), on_done=lambda r: self._reload()) snack(self._page, "Бэкап запущен")