88 lines
4.0 KiB
Python
88 lines
4.0 KiB
Python
|
|
"""Резервное копирование: запуск, история, скачивание."""
|
||
|
|
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, "Бэкап запущен")
|