"""Серверы: список, добавление, редактирование, удаление.""" import flet as ft from app.core import servers as servers_core from app.core.ssh import check_server from app.ui.components import confirm_dialog, snack class ServersPage(ft.Column): def __init__(self, page: ft.Page, go): super().__init__(expand=True, scroll=ft.ScrollMode.AUTO) self._page = page self.go = go 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=[], ) self.controls = [ ft.Row([ ft.Text("Серверы", size=28, weight=ft.FontWeight.BOLD), ft.FilledButton("Добавить сервер", icon=ft.Icons.ADD, on_click=self._add_dialog), ], alignment=ft.MainAxisAlignment.SPACE_BETWEEN), ft.Card(content=ft.Container(padding=10, content=self.table)), ] self.reload() def reload(self): self.table.rows = [] for s in servers_core.list_servers(): self.table.rows.append(ft.DataRow(cells=[ ft.DataCell(ft.Text(s.name, weight=ft.FontWeight.BOLD)), ft.DataCell(ft.Text(f"{s.host}:{s.port}")), ft.DataCell(ft.Text(s.username)), ft.DataCell(ft.Text(s.tags or "—")), ft.DataCell(ft.Text("…", key=f"status_{s.id}")), ft.DataCell(ft.Row([ ft.IconButton(ft.Icons.OPEN_IN_NEW, tooltip="Открыть", on_click=lambda e, sid=s.id: self.go("server_detail", sid)), ft.IconButton(ft.Icons.EDIT, tooltip="Изменить", on_click=lambda e, s=s: self._edit_dialog(s)), ft.IconButton(ft.Icons.DELETE, tooltip="Удалить", on_click=lambda e, s=s: self._delete(s)), ])), ])) self._page.update() for s in servers_core.list_servers(): self._page.run_task(self._check, s) async def _check(self, s): info = await check_server(s) txt = "🟢 онлайн" if info.get("online") else "🔴 оффлайн" self._statuses = getattr(self, "_statuses", {}) self._statuses[s.id] = txt for i, srv in enumerate(servers_core.list_servers()): if i < len(self.table.rows): self.table.rows[i].cells[4].content = ft.Text( self._statuses.get(srv.id, "…")) self._page.update() # ---------- диалоги ---------- def _form(self, s=None): return { "name": ft.TextField(label="Имя", value=s.name if s else "", width=300), "host": ft.TextField(label="Хост", value=s.host if s else "", width=300), "port": ft.TextField(label="Порт", value=str(s.port) if s else "22", width=100), "username": ft.TextField(label="Пользователь", value=s.username if s else "root", width=300), "auth_type": ft.Dropdown( label="Авторизация", width=300, value=s.auth_type if s else "password", options=[ ft.dropdown.Option("password", "Пароль"), ft.dropdown.Option("key", "SSH-ключ"), ], ), "password": ft.TextField(label="Пароль / passphrase", password=True, value=s.password if s else "", width=300), "key_path": ft.TextField(label="Путь к ключу", value=s.key_path or "" if s else "", width=300), "sudo_password": ft.TextField(label="Sudo-пароль (если отличается)", password=True, value=s.sudo_password or "" if s else "", width=300), "tags": ft.TextField(label="Теги (через запятую)", value=s.tags if s else "", width=300), } def _add_dialog(self, e): self._open_form("Новый сервер", self._form(), None) def _edit_dialog(self, s): self._open_form(f"Изменить: {s.name}", self._form(s), s.id) def _open_form(self, title, f, server_id): def save(e): fields = dict( name=f["name"].value, host=f["host"].value, port=int(f["port"].value or 22), username=f["username"].value, auth_type=f["auth_type"].value, password=f["password"].value or None, key_path=f["key_path"].value or None, sudo_password=f["sudo_password"].value or None, tags=f["tags"].value, ) if server_id: servers_core.update_server(server_id, **fields) else: servers_core.add_server(**fields) self._page.pop_dialog() self.reload() snack(self._page, "Сохранено") def test(e): async def _t(): fields = dict( id=0, name=f["name"].value or "test", host=f["host"].value, port=int(f["port"].value or 22), username=f["username"].value, auth_type=f["auth_type"].value, password=f["password"].value or None, key_path=f["key_path"].value or None, sudo_password=f["sudo_password"].value or None, tags="", color="blue", read_only=False, ) from app.db.models import Server info = await check_server(Server(**fields)) if info.get("online"): snack(self._page, f"✅ {info['os']} | {info['uptime']}") else: snack(self._page, f"❌ {info.get('error')}", error=True) self._page.run_task(_t) dlg = ft.AlertDialog( modal=True, title=ft.Text(title), content=ft.Column(list(f.values()), tight=True, scroll=ft.ScrollMode.AUTO, height=480), actions=[ ft.TextButton("Проверить подключение", on_click=test), ft.TextButton("Отмена", on_click=lambda e: self._close(dlg)), ft.FilledButton("Сохранить", on_click=save), ], ) self._page.show_dialog(dlg) def _close(self, dlg): self._page.pop_dialog() def _delete(self, s): confirm_dialog( self._page, "Удалить сервер?", f"Сервер «{s.name}» ({s.host}) будет удалён из приложения. На самом сервере ничего не изменится.", lambda: (servers_core.delete_server(s.id), self.reload()), )