optimize guko fork installer and backup tools
This commit is contained in:
@@ -217,6 +217,58 @@ def next_manual_id(servers):
|
||||
return n
|
||||
|
||||
|
||||
|
||||
|
||||
def backup_bundle_path() -> Path:
|
||||
ts = datetime.now().strftime('%Y%m%d-%H%M%S')
|
||||
out = TMP_DIR / f'guko-backup-{ts}.tar.gz'
|
||||
out.parent.mkdir(parents=True, exist_ok=True)
|
||||
files = []
|
||||
for path in (SERVERS_JSON, HISTORY_JSON):
|
||||
if path.exists():
|
||||
files.append(path)
|
||||
for d in (KEYS_DIR, MEDIA_DIR, RESULTS_DIR):
|
||||
if d.exists():
|
||||
files.append(d)
|
||||
import tarfile
|
||||
with tarfile.open(out, 'w:gz') as tar:
|
||||
for item in files:
|
||||
arc = item.name if item.is_file() else item.name
|
||||
tar.add(item, arcname=arc)
|
||||
try:
|
||||
os.chmod(out, 0o600)
|
||||
except Exception:
|
||||
pass
|
||||
return out
|
||||
|
||||
|
||||
def inventory_summary_text() -> str:
|
||||
inv = load_inventory()
|
||||
servers = inv.get('servers') or []
|
||||
total = len(servers)
|
||||
password = key = skipped = 0
|
||||
for item in servers:
|
||||
try:
|
||||
cfg = resolve_ssh(item, inv)
|
||||
if cfg.get('auth') == 'password':
|
||||
password += 1
|
||||
elif cfg.get('key'):
|
||||
key += 1
|
||||
else:
|
||||
skipped += 1
|
||||
except Exception:
|
||||
skipped += 1
|
||||
return (
|
||||
'📦 <b>GUKO 配置概览</b>\n\n'
|
||||
f'服务器:<b>{total}</b> 台\n'
|
||||
f'密钥登录:{key} 台\n'
|
||||
f'密码登录:{password} 台\n'
|
||||
f'未完整配置:{skipped} 台\n'
|
||||
f'清单:<code>{safe(str(SERVERS_JSON))}</code>\n'
|
||||
f'密钥目录:<code>{safe(str(KEYS_DIR))}</code>\n'
|
||||
f'结果目录:<code>{safe(str(RESULTS_DIR))}</code>'
|
||||
)
|
||||
|
||||
def redact_inventory(inv: dict):
|
||||
def clean_server(s):
|
||||
out = json.loads(json.dumps(s, ensure_ascii=False))
|
||||
@@ -3253,6 +3305,18 @@ async def export_config_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
await update.message.reply_document(document=f, filename='guko-servers-redacted.json', caption='已导出脱敏配置(密码已隐藏)。')
|
||||
|
||||
|
||||
async def backup_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
if not await admin_guard(update): return
|
||||
path = backup_bundle_path()
|
||||
with path.open('rb') as f:
|
||||
await update.message.reply_document(document=f, filename=path.name, caption='GUKO 完整备份:servers/history/keys/media/results。请妥善保存。')
|
||||
|
||||
|
||||
async def summary_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
if not await guard(update): return
|
||||
await update.message.reply_text(inventory_summary_text(), parse_mode=ParseMode.HTML)
|
||||
|
||||
|
||||
async def testall_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
if not await admin_guard(update): return
|
||||
servers = [s for s in load_inventory().get('servers', []) if s.get('host')]
|
||||
@@ -3475,6 +3539,8 @@ async def post_init(app: Application):
|
||||
BotCommand('testssh', '测试服务器 SSH'),
|
||||
BotCommand('testall', '批量测试 SSH'),
|
||||
BotCommand('exportconfig', '导出脱敏配置'),
|
||||
BotCommand('backup', '导出完整备份'),
|
||||
BotCommand('summary', '配置概览'),
|
||||
BotCommand('info', '查看单台操作面板:/info 名字/IP/ID'),
|
||||
BotCommand('jobs', '查看后台任务'),
|
||||
BotCommand('history', '查看测试历史:/history 服务器'),
|
||||
@@ -3925,6 +3991,8 @@ def main():
|
||||
app.add_handler(CommandHandler('testssh', testssh_cmd))
|
||||
app.add_handler(CommandHandler('testall', testall_cmd))
|
||||
app.add_handler(CommandHandler('exportconfig', export_config_cmd))
|
||||
app.add_handler(CommandHandler('backup', backup_cmd))
|
||||
app.add_handler(CommandHandler('summary', summary_cmd))
|
||||
app.add_handler(CommandHandler('info', info_cmd))
|
||||
app.add_handler(CommandHandler('jobs', jobs_cmd))
|
||||
app.add_handler(CommandHandler('history', history_cmd))
|
||||
|
||||
Reference in New Issue
Block a user