Files
debian-manager/app/main.py
T

178 lines
6.5 KiB
Python
Raw Normal View History

"""Debian Manager — управление Debian-серверами."""
import asyncio
import flet as ft
from app.db.database import init_db
from app.core import servers as servers_core
from app.core import scheduler
from app.db.models import Setting
from app.db.database import get_session
from app.ui.pages.dashboard import DashboardPage
from app.ui.pages.servers import ServersPage
from app.ui.pages.server_detail import ServerDetailPage
from app.ui.pages.group_ops import GroupOpsPage
from app.ui.pages.playbooks import PlaybooksPage
from app.ui.pages.diff import DiffPage
from app.ui.pages.gitea import GiteaPage
from app.ui.pages.scheduler import SchedulerPage
from app.ui.pages.backups import BackupsPage
from app.ui.pages.reports import ReportsPage
from app.ui.pages.portainer import PortainerPage
from app.ui.pages.agent import AgentPage
from app.ui.pages.proxmox_page import ProxmoxPage
from app.ui.pages.wizard import WizardPage
from app.ui.pages.settings import SettingsPage
from app.ui.pages.tasks import TasksPage
def _get_setting(key: str) -> str:
with get_session() as s:
row = s.get(Setting, key)
return row.value if row else ""
def _set_setting(key: str, value: str) -> None:
with get_session() as s:
row = s.get(Setting, key) or Setting(key=key)
row.value = value
s.add(row)
s.commit()
def main(page: ft.Page):
page.title = "Debian Manager"
page.window.width = 1400
page.window.height = 900
page.padding = 0
init_db()
# тема из настроек
theme = _get_setting("theme")
page.theme_mode = (ft.ThemeMode.DARK if theme != "light" else ft.ThemeMode.LIGHT)
# seed тестового сервера при первом запуске
if not servers_core.list_servers():
servers_core.add_server(
name="Test VPS", host="132.243.17.164", port=2292,
username="dimon", password="123456789987654321", tags="удалённые",
)
# запуск планировщика и фоновых сервисов
async def _start_bg():
scheduler.start()
try:
from app.core import webhook, agent
await webhook.start()
await agent.start()
except Exception:
pass
page.run_task(_start_bg)
content = ft.Container(expand=True, padding=20)
PAGES = {
"dashboard": ("Дашборд", ft.Icons.DASHBOARD, DashboardPage),
"servers": ("Серверы", ft.Icons.DNS, ServersPage),
"wizard": ("Мастер настройки", ft.Icons.AUTO_FIX_HIGH, WizardPage),
"group": ("Групповые операции", ft.Icons.GROUPS, GroupOpsPage),
"scheduler": ("Планировщик", ft.Icons.SCHEDULE, SchedulerPage),
"playbooks": ("Плейбуки", ft.Icons.PLAYLIST_PLAY, PlaybooksPage),
"backups": ("Бэкапы", ft.Icons.BACKUP, BackupsPage),
"reports": ("Отчёты", ft.Icons.SUMMARIZE, ReportsPage),
"portainer": ("Portainer", ft.Icons.WIDGETS, PortainerPage),
"agent": ("Агент", ft.Icons.SENSORS, AgentPage),
"proxmox": ("Proxmox", ft.Icons.CLOUD_QUEUE, ProxmoxPage),
"diff": ("Сравнение", ft.Icons.COMPARE_ARROWS, DiffPage),
"gitea": ("Gitea Deploy", ft.Icons.ROCKET_LAUNCH, GiteaPage),
"tasks": ("Журнал задач", ft.Icons.TASK_ALT, TasksPage),
"settings": ("Настройки", ft.Icons.SETTINGS, SettingsPage),
}
state = {"route": "dashboard", "detail_server_id": None,
"group_filter": "Все"}
# top_bar объявлен до render, чтобы render мог его обновлять
top_bar = ft.Container(padding=ft.Padding.only(left=20, top=10))
def render():
top_bar.content = group_chips()
route = state["route"]
if route == "server_detail":
content.content = ServerDetailPage(page, state["detail_server_id"], go)
elif route == "dashboard":
content.content = DashboardPage(page, go, group_filter=state["group_filter"])
else:
cls = PAGES[route][2]
content.content = cls(page, go)
nav.selected_index = list(PAGES.keys()).index(route) if route in PAGES else 0
page.update()
def go(route: str, server_id=None, group_filter=None):
state["route"] = route
state["detail_server_id"] = server_id
if group_filter is not None:
state["group_filter"] = group_filter
render()
def toggle_theme(e):
new = "light" if page.theme_mode == ft.ThemeMode.DARK else "dark"
page.theme_mode = ft.ThemeMode.LIGHT if new == "light" else ft.ThemeMode.DARK
_set_setting("theme", new)
theme_btn.icon = ft.Icons.LIGHT_MODE if new == "dark" else ft.Icons.DARK_MODE
page.update()
# группы серверов для дашборда
def group_chips():
servers = servers_core.list_servers()
tags = sorted({t.strip() for s in servers for t in (s.tags or "").split(",") if t.strip()})
groups = ["Все"] + tags
return ft.Row([
ft.Chip(
label=ft.Text(g),
selected=state["group_filter"] == g,
on_select=lambda e, g=g: go("dashboard", group_filter=g),
) for g in groups
], spacing=6)
theme_btn = ft.IconButton(
ft.Icons.LIGHT_MODE if page.theme_mode == ft.ThemeMode.DARK else ft.Icons.DARK_MODE,
tooltip="Сменить тему", on_click=toggle_theme,
)
# NavigationRail — компактный режим (только иконки), tooltip через label
nav = ft.NavigationRail(
selected_index=0,
label_type=ft.NavigationRailLabelType.NONE,
min_width=60,
destinations=[
ft.NavigationRailDestination(
icon=ft.Icon(v[1], tooltip=v[0]), # tooltip на самой иконке
label=v[0],
)
for v in PAGES.values()
],
on_change=lambda e: go(list(PAGES.keys())[e.control.selected_index]),
trailing=ft.Column([theme_btn], alignment=ft.MainAxisAlignment.END),
)
# верхняя панель с группами (только на дашборде)
top_bar.content = group_chips()
page.add(
ft.Column([
top_bar,
ft.Row(
[nav, ft.VerticalDivider(width=1), content],
expand=True,
spacing=0,
),
], expand=True, spacing=0),
)
render()
if __name__ == "__main__":
ft.app(target=main)