Initial commit: Debian Manager — управление Debian-серверами
- SSH/SFTP управление без агентов - Пакеты (apt), Docker, файлы, systemd, ufw, cron - Плейбуки, групповые операции, планировщик - Бэкапы, отчёты, Portainer, Proxmox VE, Gitea Deploy - Терминал, Telegram-уведомления, тёмная/светлая тема
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
"""Portainer: статус, открытие в браузере, установка/перезапуск."""
|
||||
import flet as ft
|
||||
|
||||
from app.core.ssh import SSHClient
|
||||
from app.core import docker_svc as d
|
||||
from app.db.models import Server
|
||||
from app.ui.components import snack
|
||||
|
||||
PORTAINER_PORT = 9000
|
||||
|
||||
|
||||
class PortainerPage(ft.Column):
|
||||
def __init__(self, page: ft.Page, go):
|
||||
super().__init__(expand=True, scroll=ft.ScrollMode.AUTO, spacing=12)
|
||||
self._page = page
|
||||
self.go = go
|
||||
self.cards_col = ft.Column(spacing=10)
|
||||
self.controls = [
|
||||
ft.Text("Portainer", size=28, weight=ft.FontWeight.BOLD),
|
||||
ft.Row([
|
||||
ft.OutlinedButton("Обновить", icon=ft.Icons.REFRESH,
|
||||
on_click=lambda e: self._reload()),
|
||||
]),
|
||||
self.cards_col,
|
||||
]
|
||||
self._reload()
|
||||
|
||||
def _reload(self):
|
||||
from app.core import servers as servers_core
|
||||
self.cards_col.controls = []
|
||||
for srv in servers_core.list_servers():
|
||||
self.cards_col.controls.append(self._card_placeholder(srv))
|
||||
self._page.update()
|
||||
from app.core import servers as servers_core
|
||||
for srv, card in zip(servers_core.list_servers(), self.cards_col.controls):
|
||||
self._page.run_task(self._fill, srv, card)
|
||||
|
||||
def _card_placeholder(self, srv):
|
||||
return ft.Card(content=ft.Container(
|
||||
padding=14, width=500,
|
||||
content=ft.Column([
|
||||
ft.Text(srv.name, weight=ft.FontWeight.BOLD, size=16),
|
||||
ft.ProgressBar(height=3),
|
||||
]),
|
||||
))
|
||||
|
||||
async def _fill(self, srv, card):
|
||||
url = f"http://{srv.host}:{PORTAINER_PORT}"
|
||||
try:
|
||||
async with SSHClient(srv) as c:
|
||||
containers = await d.list_containers(c)
|
||||
portainer = next((x for x in containers if "portainer" in x["Names"].lower()), None)
|
||||
if not portainer:
|
||||
card.content.content.controls[1] = ft.Column([
|
||||
ft.Text("Portainer не установлен", color=ft.Colors.GREY_500),
|
||||
ft.FilledButton("Установить", icon=ft.Icons.DOWNLOAD,
|
||||
on_click=lambda e, s=srv: self._install(s)),
|
||||
])
|
||||
else:
|
||||
running = "Up" in portainer["Status"]
|
||||
cid = portainer["ID"]
|
||||
card.content.content.controls[1] = ft.Column([
|
||||
ft.Row([
|
||||
ft.Icon(ft.Icons.CIRCLE, size=12,
|
||||
color=ft.Colors.GREEN_400 if running else ft.Colors.RED_400),
|
||||
ft.Text(portainer["Status"], size=12),
|
||||
]),
|
||||
ft.Row([
|
||||
ft.FilledButton("Открыть в браузере", icon=ft.Icons.OPEN_IN_BROWSER,
|
||||
on_click=lambda e, u=url: self._page.launch_url(u)),
|
||||
ft.OutlinedButton(
|
||||
"Перезапустить" if running else "Запустить",
|
||||
on_click=lambda e, s=srv, c=cid, a="restart" if running else "start":
|
||||
self._action(s, c, a)),
|
||||
]),
|
||||
ft.Text(url, size=11, color=ft.Colors.BLUE_300, selectable=True),
|
||||
])
|
||||
except Exception as e:
|
||||
card.content.content.controls[1] = ft.Text(f"ошибка: {e}", size=12,
|
||||
color=ft.Colors.RED_300)
|
||||
self._page.update()
|
||||
|
||||
def _install(self, srv):
|
||||
from app.core.tasks import tasks
|
||||
from app.ui.components import append_log, log_view
|
||||
async def job(log):
|
||||
async with SSHClient(srv) as c:
|
||||
log("$ docker volume create portainer_data")
|
||||
await c.sudo("docker volume create portainer_data 2>/dev/null || true")
|
||||
log("$ docker run portainer")
|
||||
r = await c.sudo(
|
||||
"docker rm -f portainer 2>/dev/null; "
|
||||
"docker run -d -p 9000:9000 --name portainer --restart=always "
|
||||
"-v /var/run/docker.sock:/var/run/docker.sock "
|
||||
"-v portainer_data:/data portainer/portainer-ce:lts", timeout=600)
|
||||
log(r.stdout + r.stderr)
|
||||
tasks.start("portainer", "установка Portainer", srv.id, job,
|
||||
on_done=lambda r: self._reload())
|
||||
snack(self._page, f"Установка Portainer на {srv.name} запущена")
|
||||
|
||||
def _action(self, srv, cid, action):
|
||||
async def _do():
|
||||
async with SSHClient(srv) as c:
|
||||
await d.container_action(c, cid, action)
|
||||
self._reload()
|
||||
self._page.run_task(_do)
|
||||
Reference in New Issue
Block a user