164 lines
4.6 KiB
Python
164 lines
4.6 KiB
Python
import asyncio
|
|
import json
|
|
from datetime import datetime
|
|
from aiogram import F
|
|
from aiogram.types import Message, InlineKeyboardMarkup, InlineKeyboardButton
|
|
from app import dp
|
|
from auth import is_admin_msg
|
|
from keyboards import backup_kb
|
|
from lock_utils import acquire_lock, release_lock
|
|
from services.backup import backup_badge, last_backup, restore_help
|
|
from services.runner import run_cmd
|
|
|
|
|
|
async def cmd_repo_stats(msg: Message):
|
|
await msg.answer("⏳ Loading repo stats…", reply_markup=backup_kb)
|
|
|
|
# --- restore-size stats ---
|
|
rc1, raw1 = await run_cmd(
|
|
["restic", "stats", "--json"],
|
|
use_restic_env=True,
|
|
timeout=30
|
|
)
|
|
if rc1 != 0:
|
|
await msg.answer(raw1, reply_markup=backup_kb)
|
|
return
|
|
|
|
restore = json.loads(raw1)
|
|
|
|
# --- raw-data stats ---
|
|
rc2, raw2 = await run_cmd(
|
|
["restic", "stats", "--json", "--mode", "raw-data"],
|
|
use_restic_env=True,
|
|
timeout=30
|
|
)
|
|
if rc2 != 0:
|
|
await msg.answer(raw2, reply_markup=backup_kb)
|
|
return
|
|
|
|
raw = json.loads(raw2)
|
|
|
|
# --- snapshots count ---
|
|
rc3, raw_snaps = await run_cmd(
|
|
["restic", "snapshots", "--json"],
|
|
use_restic_env=True,
|
|
timeout=20
|
|
)
|
|
snaps = len(json.loads(raw_snaps)) if rc3 == 0 else "n/a"
|
|
|
|
msg_text = (
|
|
"📦 **Repository stats**\n\n"
|
|
f"🧉 Snapshots: {snaps}\n"
|
|
f"📁 Files: {restore.get('total_file_count', 'n/a')}\n"
|
|
f"💽 Logical size: {restore.get('total_size', 0) / (1024**3):.2f} GiB\n"
|
|
f"🧱 Stored data: {raw.get('total_pack_size', 0) / (1024**2):.2f} MiB\n"
|
|
)
|
|
|
|
await msg.answer(msg_text, reply_markup=backup_kb, parse_mode="Markdown")
|
|
|
|
|
|
async def cmd_backup_status(msg: Message):
|
|
await msg.answer("⏳ Loading snapshots…", reply_markup=backup_kb)
|
|
|
|
async def worker():
|
|
rc, raw = await run_cmd(
|
|
["restic", "snapshots", "--json"],
|
|
use_restic_env=True,
|
|
timeout=30
|
|
)
|
|
if rc != 0:
|
|
await msg.answer(raw, reply_markup=backup_kb)
|
|
return
|
|
|
|
snaps = json.loads(raw)
|
|
if not snaps:
|
|
await msg.answer("📦 Snapshots: none", reply_markup=backup_kb)
|
|
return
|
|
|
|
snaps.sort(key=lambda s: s["time"], reverse=True)
|
|
|
|
# --- badge ---
|
|
last = snaps[0]
|
|
last_time = datetime.fromisoformat(
|
|
last["time"].replace("Z", "+00:00")
|
|
)
|
|
badge = backup_badge(last_time)
|
|
|
|
# --- buttons ---
|
|
rows = []
|
|
for s in snaps[:5]:
|
|
t = datetime.fromisoformat(
|
|
s["time"].replace("Z", "+00:00")
|
|
)
|
|
rows.append([
|
|
InlineKeyboardButton(
|
|
text=f"🧉 {s['short_id']} | {t:%Y-%m-%d %H:%M}",
|
|
callback_data=f"snap:{s['short_id']}"
|
|
)
|
|
])
|
|
|
|
kb = InlineKeyboardMarkup(inline_keyboard=rows)
|
|
|
|
await msg.answer(
|
|
f"📦 Snapshots ({len(snaps)})\n{badge}",
|
|
reply_markup=kb
|
|
)
|
|
|
|
asyncio.create_task(worker())
|
|
|
|
|
|
async def cmd_backup_now(msg: Message):
|
|
if not acquire_lock("backup"):
|
|
await msg.answer("⚠️ Backup уже выполняется", reply_markup=backup_kb)
|
|
return
|
|
|
|
await msg.answer("▶️ Backup запущен", reply_markup=backup_kb)
|
|
|
|
async def worker():
|
|
try:
|
|
rc, out = await run_cmd(["sudo", "/usr/local/bin/backup.py", "restic-backup"], timeout=6 * 3600)
|
|
await msg.answer(("✅ OK\n" if rc == 0 else "❌ FAIL\n") + out, reply_markup=backup_kb)
|
|
finally:
|
|
release_lock("backup")
|
|
|
|
asyncio.create_task(worker())
|
|
|
|
|
|
async def cmd_last_backup(msg: Message):
|
|
try:
|
|
text = await asyncio.to_thread(last_backup)
|
|
except Exception as e:
|
|
await msg.answer(f"❌ Last backup failed: {type(e).__name__}: {e}", reply_markup=backup_kb)
|
|
return
|
|
await msg.answer(text, reply_markup=backup_kb)
|
|
|
|
|
|
@dp.message(F.text == "📦 Status")
|
|
async def bs(msg: Message):
|
|
if is_admin_msg(msg):
|
|
await cmd_backup_status(msg)
|
|
|
|
|
|
@dp.message(F.text == "📊 Repo stats")
|
|
async def rs(msg: Message):
|
|
if is_admin_msg(msg):
|
|
await cmd_repo_stats(msg)
|
|
|
|
|
|
@dp.message(F.text == "📦 Last backup")
|
|
async def lb(msg: Message):
|
|
if is_admin_msg(msg):
|
|
await cmd_last_backup(msg)
|
|
|
|
|
|
@dp.message(F.text == "▶️ Run backup")
|
|
async def br(msg: Message):
|
|
if is_admin_msg(msg):
|
|
await cmd_backup_now(msg)
|
|
|
|
|
|
@dp.message(F.text == "🧯 Restore help")
|
|
async def rh(msg: Message):
|
|
if is_admin_msg(msg):
|
|
await msg.answer(restore_help(), reply_markup=backup_kb)
|