(msg: Api.Message, backupName?: string)
| 374 | await msg.edit({ text: `未找到配置目录:${codeTag(`${installPath}/data`)}`, parseMode: "html" }); |
| 375 | return; |
| 376 | } |
| 377 | |
| 378 | const backupBaseDir = "/opt/openlist_backups"; |
| 379 | const { stdout: dateOut } = await execAsync( |
| 380 | `bash -lc 'date +%Y%m%d_%H%M%S'` |
| 381 | ); |
| 382 | const backupDir = `${backupBaseDir}/backup_${(dateOut || "").trim()}`; |
| 383 | await execAsync(`mkdir -p "${backupDir}"`); |
| 384 | await execAsync(`cp -r "${installPath}/data" "${backupDir}/"`); |
| 385 | |
| 386 | await msg.edit({ text: `备份成功\n目录: ${codeTag(backupDir)}`, parseMode: "html" }); |
| 387 | } catch (error: any) { |
| 388 | await msg.edit({ text: `备份失败: ${htmlEscape(error?.message || error)}`, parseMode: "html" }); |
| 389 | } |
| 390 | } |
| 391 | |
| 392 | private async handleRestore(msg: Api.Message, backupName?: string) { |
| 393 | try { |
| 394 | const installPath = await this.detectInstalledPath(); |
| 395 | const backupBaseDir = "/opt/openlist_backups"; |
| 396 | let targetBackupDir = ""; |
| 397 | |
| 398 | if (backupName) { |
| 399 | const safeBackupName = this.sanitizeBackupName(backupName); |
| 400 | if (!safeBackupName) { |
| 401 | await msg.edit({ text: "无效的备份名:仅允许备份目录名本身(字母、数字、点、下划线、短横线),不允许路径分隔符或 .." }); |
| 402 | return; |
| 403 | } |
| 404 | targetBackupDir = path.join(backupBaseDir, safeBackupName); |
| 405 | } else { |
| 406 | const { stdout: latestOut } = await execAsync( |
| 407 | `bash -lc 'ls -t "${backupBaseDir}" 2>/dev/null | head -n1'` |
| 408 | ); |
| 409 | const latest = (latestOut || "").trim(); |
| 410 | const safeLatest = this.sanitizeBackupName(latest); |
| 411 | if (!safeLatest) { |
| 412 | await msg.edit({ text: `未找到任何备份于:${codeTag(backupBaseDir)}`, parseMode: "html" }); |
| 413 | return; |
no test coverage detected