Skip to content

feat(backup): 实现安全的进程内备份恢复 - #1795

Draft
MX-fox wants to merge 16 commits into
sealdice:masterfrom
MX-fox:feat/backup-restore-v2
Draft

MX-fox wants to merge 16 commits into
sealdice:masterfrom
MX-fox:feat/backup-restore-v2

Conversation

@MX-fox

@MX-fox MX-fox commented Aug 9, 2026 •

Copy link
Copy Markdown
Member

Summary by Sourcery

引入一个对进程安全的、具备事务性的运行时备份与恢复生命周期机制,包含运行时监管、v2 备份清单,以及用于进程内恢复的健壮安全检查。

New Features:

  • 添加运行时监管器和生命周期 API,用于在不重启主进程的情况下重建并热切换 Dice 运行时。
  • 引入进程内备份上传、恢复以及状态查询的 HTTP API,支持幂等的请求 ID 和安全的状态令牌。
  • 将备份归档升级为 v2 清单格式,包含按文件的 SHA-256、大小元数据,以及严格的 ZIP 路径校验。
  • 实现按运行时的任务管理及适配器关闭语义,以安全地使运行时静默、完成并停止。

Bug Fixes:

  • 防止删除当前被恢复事务使用的备份,并确保在崩溃后恢复状态能够被安全地重建。
  • 收紧 SQLite WAL 检查点处理逻辑,对不完整的检查点直接失败,而不是静默继续。

Enhancements:

  • 重构备份创建流程,使其在磁盘上严格串行、内容可验证且具备原子性,并采用安全文件模式和目录同步。
  • 让适配器进程和 WebSocket 客户端具备运行时感知能力,以便在关闭时干净退出,不在不同代之间泄露资源。
  • 确保托盘提示和账号菜单从当前已发布的运行时读取数据,而不是使用过期的进程内状态。
  • 改进版本检查与重启逻辑,复用现有标志位,同时在各平台之间统一延迟处理方式。

Tests:

  • 增加针对备份归档检查、v2 清单验证、恢复日志记录、回滚语义以及运行时监管器行为的大量测试。
  • 增加平台特定测试,用于原子文件替换、重启参数重建以及数据库操作器初始化错误处理。
Original summary in English

Summary by Sourcery

Introduce a process-safe, transactional runtime backup and restore lifecycle with runtime supervision, v2 backup manifests, and robust safety checks for in-process recovery.

New Features:

  • Add a runtime supervisor and lifecycle APIs to rebuild and hot-swap Dice runtimes without restarting the main process.
  • Introduce in-process backup upload, restore, and status query HTTP APIs with idempotent request IDs and secure status tokens.
  • Upgrade backup archives to a v2 manifest format with per-file SHA-256, size metadata, and strict ZIP path validation.
  • Implement per-runtime task management and adapter shutdown semantics to quiesce, finalize, and stop runtimes safely.

Bug Fixes:

  • Prevent deletion of backups currently used by restore transactions and ensure restore state is recovered safely after crashes.
  • Tighten SQLite WAL checkpoint handling to fail on incomplete checkpoints instead of silently continuing.

Enhancements:

  • Refactor backup creation to be strictly serialized, content-verified, and atomic on disk with secure file modes and directory syncs.
  • Make adapter processes and WebSocket clients runtime-aware so they stop cleanly on shutdown and do not leak resources across generations.
  • Ensure tray tooltips and account menus read from the currently published runtime instead of stale in-process state.
  • Improve version check and reboot logic to reuse existing flags while normalizing delay handling across platforms.

Tests:

  • Add extensive tests for backup archive inspection, v2 manifest validation, restore journaling, rollback semantics, and runtime supervisor behavior.
  • Add platform-specific tests for atomic file replacement, restart argument reconstruction, and database operator initialization error handling.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @MX-fox, your pull request is larger than the review limit of 150000 diff characters

@sourcery-ai

sourcery-ai Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

实现一个对进程安全的、就地(in-place)备份/恢复生命周期,引入运行时监督器(runtime supervisor)、事务性的文件恢复日志(journaling)、更严格的备份校验以及具备运行时感知的适配器/进程停机机制,并新增用于备份上传/恢复和状态查询的 API 端点。

进程内运行时备份恢复生命周期的时序图

sequenceDiagram
    actor User
    participant API as sd_api_backup_restore
    participant DiceManager
    participant RuntimeSupervisor
    participant RestoreWorker as restoreWorker

    User->>API: POST /sd-api/backup/restore {name,requestId}
    API->>DiceManager: ScheduleRestore(name, requestId)
    DiceManager-->>API: RestoreOperation{operationId,statusToken}
    API->>RuntimeSupervisor: enqueueRestore(operationId)
    RuntimeSupervisor-->>API: accepted (true/false)
    API-->>User: Response{operationId,statusToken,reloading}

    loop restore queue worker
        RuntimeSupervisor->>RestoreWorker: restore(ctx, operationId)
        RestoreWorker->>DiceManager: Quiesce(ctx)
        RestoreWorker->>DiceManager: Finalize(ctx)
        RestoreWorker->>DiceManager: PrepareScheduledRestore(DiceManager)
        RestoreWorker->>DiceManager: ApplyScheduledRestore()
        RestoreWorker->>RuntimeSupervisor: buildValidatedRuntime()
        RuntimeSupervisor->>DiceManager: CommitScheduledRestore()
        RuntimeSupervisor->>DiceManager: MarkScheduledRestoreSucceeded()
    end
Loading

File-Level Changes

Change Details Files
通过使用 manifest v2、SHA-256 校验、SQLite 专用刷写(flushing)、目录同步以及对备份操作的互斥控制来强化备份创建过程。
  • 为备份操作添加全局互斥锁和目录校验,防止并发修改。
  • 生成 v2 版本的备份清单(manifest),包含按文件记录的大小和 SHA-256、排序后的文件列表以及数据库类型元数据。
  • 规范化并约束归档路径仅指向受管数据目录,跳过由进程拥有的文件和 SQLite 边车文件(sidecars)。
  • 在将 SQLite 数据库包含进备份前,通过更严格的 WAL 检查点实现对其进行刷写。
  • 将备份文件及其父目录同步到磁盘,并在完成时使用原子重命名语义。
  • 在 BackupClean 和 Delete API 中阻止删除当前被恢复事务引用的备份。
dice/dice_backup.go
dice/service/backup.go
引入运行时监督器和生命周期(Quiesce/Finalize),支持在不退出主进程的情况下安全地进行进程内备份恢复以及 Runtime 重建。
  • 为 DiceManager 添加运行时上下文、取消机制和任务追踪,引入 GoRuntime 辅助工具以及按代(generation)划分的等待组(wait group)。
  • 实现 Quiesce,用于停止定时任务(cron)、适配器、后台任务,并在保持数据库连接打开的情况下刷新可变状态。
  • 实现 Finalize,用于拆除 JS 运行时、扩展存储、帮助文档,并以共享阶段语义关闭数据库。
  • 添加 runtimeSupervisor,其负责当前 applicationRuntime,协调 API 维护、构建/校验新的 Runtime 实例并发布它们。
  • 调整 main.go 来构造 supervisor,停止引导阶段的 DiceManager,启动受管 Runtime,并暴露由进程清理使用的 stop provider。
  • 修改托盘(tray)/httpServe 逻辑,使其基于清理回调而非 DiceManager 工作,并定期通过 API 获取 Runtime 托盘提示信息(tooltip)。
runtime_supervisor.go
runtime_supervisor_test.go
dice/runtime_lifecycle.go
dice/runtime_lifecycle_test.go
dice/dice_manager.go
dice/dice.go
tray_common.go
tray_darwin.go
tray_windows.go
tray_others.go
main.go
实现事务性、仅追加(append-only)的恢复日志机制,包含安全的 ZIP 校验、路径规范化、磁盘空间检查,以及用于在线 SQLite 恢复的回滚逻辑。
  • 定义 BackupArchiveInfo、RestoreStatus、restorePending、restoreJournal、restoreJournalRecord 和 restoreStatusAuth 类型,用于表示恢复状态和日志条目。
  • 执行严格的 ZIP 预检查(中央目录边界、条目数量、路径长度、压缩比),并强制使用 data/ 路径,拒绝不安全的 Windows 文件名和符号链接。
  • 基于 ZIP 内容实现 v2 manifest 校验,包括 SHA-256 哈希以及对安全备份的 SQLite 专用要求。
  • 按文件跟踪恢复意图/完成步骤(backup-original、install-staged、remove-installed、restore-original),并将日志记录以 journal.jsonl 的形式追加写入。
  • 实现 ApplyScheduledRestore,将备份解压到预备(staging)区域,构建按文件记录的恢复日志,并通过原子重命名和目录同步应用变更。
  • 实现回滚逻辑,能够根据日志状态安全地恢复原始文件或移除预备文件,包括 SQLite 边车文件的合成/清理。
  • 添加 RecoverInterruptedRestore,用于在启动时检测并回滚未完成的事务,处理空/破损日志,当状态不确定时将系统置于降级模式。
  • 提供辅助工具,以使用 requestId 幂等地调度恢复操作,生成并刷新状态令牌,并仅在 Runtime 发布后标记恢复成功。
  • 添加操作系统特定的原子替换(atomic replace)辅助方法及跨卷/磁盘空间检查,以确保恢复在同一卷上进行且有足够可用空间。
dice/dice_backup_restore.go
dice/dice_backup_restore_test.go
dice/atomic_replace_windows.go
dice/atomic_replace_unix.go
新增备份/恢复 API,用于上传、恢复调度和状态查询,并支持认证、幂等性以及 TOCTOU 安全的下载。
  • 用 InspectBackupArchive 替换旧的备份列表解析逻辑,返回经过校验的元数据以及基于数据库类型和内容的 Restorable 标记。
  • 实现 backupDownload,使用 OpenBackupArchive 通过已验证的文件描述符提供内容,并设置合适的 Content-Disposition 头。
  • 通过 Dice.DeleteBackup 路由备份删除操作,在移除文件前检查恢复使用情况,并支持批量删除。
  • 添加 backupUpload,用于流式处理 multipart 上传,将其存储在 backups/ 中,验证 ZIP+manifest,并通过 SHA-256 摘要复用已有的相同内容。
  • 添加 backupRestore,接收备份名称/requestId,通过 DiceManager.ScheduleRestore 调度恢复,并将其加入运行时恢复队列。
  • 添加 backupRestoreStatus,返回与操作绑定的状态(通过 bearer 头)或全局恢复状态,并通过 Cache-Control: no-store 禁止缓存。
  • 在 uiServe 的 CORS 配置中暴露新的恢复状态相关头。
  • 添加 API 运行时门控中间件,在维护/不可用期间返回 503,同时保持登录盐值(signin salt)和恢复状态可访问。
  • 使登录盐值端点从 runtimeAuthSnapshot 而非原始 DiceManager 读取,并包含 passwordRequired 标志。
api/backup.go
api/api_bind.go
api/runtime_gate_test.go
使适配器和 IM 会话执行具备运行时感知能力,在恢复期间允许有序地关闭受管进程和后台任务。
  • 引入 DiceManager.GoRuntime/beginRuntimeTask 辅助函数,并将运行时上下文传播到适配器循环、重连延迟和长时任务中。
  • 重构 diceServe 为 diceServePrepare/diceServeEndpoint,允许 supervisor 按端点控制启动,并使用具备上下文感知的 ServeQQ。
  • 为 PlatformAdapterGocq/WalleQ/Milky 添加受管进程辅助工具,以在 Start/Wait/Kill 过程中协调 runtimeStopping 标志和互斥锁。
  • 修改内置的 QQ/Lagrange/Milky/WalleQ 辅助工具,使用受管进程辅助工具、非阻塞的通道发送,以及具备上下文感知的延迟来处理登录流程和二维码(QR)。
  • 更新 IMSession.Execute/ExecuteNew、通知、群组加入/退出流程,以及 Kook/Satori/Slack/TG 处理器,使其通过 runDiceRuntimeTask 运行任务并遵守 Runtime 取消。
  • 为各适配器(Onebot、官方 QQ、Satori、Red、Minecraft、SealChat、Dodo、Kook、Milky、Gocq、WalleQ、Telegram、Slack、钉钉)添加 RuntimeShutdown 实现,以干净地停止套接字、进程和内部服务。
  • 确保内部 gocq/walle/walleq/Milky 进程实例在 Runtime 关闭时停止,并使重连循环观察 runtimeStopping 标志。
dice/platform_adapter_gocq_helper.go
dice/platform_adapter_gocq.go
dice/platform_adapter_walleq_helper.go
dice/platform_adapter_walleq.go
dice/platform_adapter_milky_helper.go
dice/platform_adapter_milky.go
dice/platform_adapter_red.go
dice/platform_adapter_dodo.go
dice/platform_adapter_sealchat.go
dice/platform_adapter_minecraft.go
dice/platform_adapter_onebot.go
dice/platform_adapter_onebot_util.go
dice/platform_adapter_kook.go
dice/platform_adapter_satori.go
dice/platform_adapter_telegram.go
dice/platform_adapter_slack.go
dice/platform_adapter_official_qq.go
dice/platform_adapter_qq_helper.go
dice/platform_adapter_walleq_helper.go
dice/im_session.go
dice/builtin_commands.go
强化针对多代(multi-generation)Runtime 生命周期的重启/更新路径以及数据库操作器构造逻辑。
  • 将 main.go 切换为使用 dboperator.NewDatabaseOperator,而非全局单例,使每一代 Runtime 拥有独立的操作器实例。
  • 确保数据库引擎的 Close() 进行空指针检查并重置内部指针,防止重复使用已关闭的操作器。
  • 添加 buildRestartArgs,以在保留部署标志的同时去除已有的 --delay 参数,并根据平台注入新的延迟。
  • 更新 doReboot,使用 os.Executable 探测可执行文件路径并校验其存在性,构建重启参数,并在 Windows 和 Unix 上使用正确的 Syscall/exec 语义。
  • 使 CheckVersion/Update 逻辑具备上下文感知能力,使用 CheckVersionContext 并在后端版本查询中传入 DiceManager。
main.go
utils/dboperator/db.go
utils/dboperator/engine/sqlite/engine_sqlite.go
utils/dboperator/engine/mysql/engine_mysql.go
utils/dboperator/engine/pgsql/engine_pgsql.go
update.go
update_test.go
utils/dboperator/db_test.go
根据新的 Runtime 行为和 nocgo SQLite 配置调整测试和迁移。
  • 在每次运行时重新初始化迁移 v150 的 attrs user sheetIdBindByGroupUserId 映射,避免跨 Runtime 污染。
  • 强制 nocgo 测试构建在需要的地方嵌入 SQLite 引擎。
  • 添加针对恢复日志记录、ZIP 安全性、运行时生命周期协调、运行时门控行为以及在缺失 DSN 情况下构建 DB 操作器的集中测试。
  • 使用 jsLoopDone 通道保护 dice JS 循环的拆除,以允许 Finalize 等待 JS 核心执行完全关闭。
migrate/v2/v150/v150_attrs.go
dice/execute_new_db_nocgo_test.go
dice/service/log_db_nocgo_test.go
dice/dice_jsvm.go
dice/dice_backup_restore_test.go
runtime_supervisor_test.go
dice/runtime_lifecycle_test.go
api/runtime_gate_test.go
utils/dboperator/db_test.go

Tips and commands

Interacting with Sourcery

  • 触发新的审查: 在拉取请求(pull request)中评论 @sourcery-ai review。
  • 继续讨论: 直接回复 Sourcery 的审查评论。
  • 从审查评论生成 GitHub issue: 通过回复审查评论,请求 Sourcery 从该评论创建一个 issue。你也可以在审查评论中回复 @sourcery-ai issue 来从评论创建 issue。
  • 生成拉取请求标题: 在拉取请求标题中的任意位置写上 @sourcery-ai,即可随时生成标题。你也可以在拉取请求中评论 @sourcery-ai title 来(重新)生成标题。
  • 生成拉取请求摘要: 在拉取请求正文中的任意位置写上 @sourcery-ai summary,即可在你想要的位置随时生成 PR 摘要。你也可以在拉取请求中评论 @sourcery-ai summary 来(重新)生成摘要。
  • 生成审阅者指南: 在拉取请求中评论 @sourcery-ai guide,即可随时(重新)生成审阅者指南。
  • 解决所有 Sourcery 评论: 在拉取请求中评论 @sourcery-ai resolve,即可标记所有 Sourcery 评论为已解决。如果你已经处理完所有评论并不再想看到它们,这非常有用。
  • 关闭所有 Sourcery 审查: 在拉取请求中评论 @sourcery-ai dismiss,即可关闭所有现有的 Sourcery 审查。尤其适用于你希望从一次新的审查重新开始的情况——别忘了再评论 @sourcery-ai review 来触发新的审查!

Customizing Your Experience

访问你的 控制面板 以:

  • 启用或禁用审查功能,例如 Sourcery 生成的拉取请求摘要、审阅者指南等。
  • 更改审查语言。
  • 添加、移除或编辑自定义审查说明。
  • 调整其他审查设置。

Getting Help

Original review guide in English

Reviewer's Guide

Implement a process-safe, in-place backup/restore lifecycle with a runtime supervisor, transactional file restore journaling, stricter backup validation, and runtime-aware adapter/process shutdown, plus new API endpoints for backup upload/restore and status querying.

Sequence diagram for in-process runtime backup restore lifecycle

sequenceDiagram
    actor User
    participant API as sd_api_backup_restore
    participant DiceManager
    participant RuntimeSupervisor
    participant RestoreWorker as restoreWorker

    User->>API: POST /sd-api/backup/restore {name,requestId}
    API->>DiceManager: ScheduleRestore(name, requestId)
    DiceManager-->>API: RestoreOperation{operationId,statusToken}
    API->>RuntimeSupervisor: enqueueRestore(operationId)
    RuntimeSupervisor-->>API: accepted (true/false)
    API-->>User: Response{operationId,statusToken,reloading}

    loop restore queue worker
        RuntimeSupervisor->>RestoreWorker: restore(ctx, operationId)
        RestoreWorker->>DiceManager: Quiesce(ctx)
        RestoreWorker->>DiceManager: Finalize(ctx)
        RestoreWorker->>DiceManager: PrepareScheduledRestore(DiceManager)
        RestoreWorker->>DiceManager: ApplyScheduledRestore()
        RestoreWorker->>RuntimeSupervisor: buildValidatedRuntime()
        RuntimeSupervisor->>DiceManager: CommitScheduledRestore()
        RuntimeSupervisor->>DiceManager: MarkScheduledRestoreSucceeded()
    end
Loading

File-Level Changes

Change Details Files
Harden backup creation with manifest v2, SHA-256 verification, SQLite-specific flushing, directory sync, and mutual exclusion over backup operations.
  • Wrap backup operations with a global mutex and directory validation to prevent concurrent modifications.
  • Generate a v2 backup manifest with per-file size and SHA-256, sorted file list, and database type metadata.
  • Normalize and constrain archive paths to managed data directories, skipping process-owned files and SQLite sidecars.
  • Flush SQLite databases via a stricter WAL checkpoint implementation before including them in backups.
  • Sync backup files and parent directory to disk and use atomic rename semantics on completion.
  • Prevent deletion of backups currently referenced by a restore transaction in BackupClean and Delete APIs.
dice/dice_backup.go
dice/service/backup.go
Introduce a runtime supervisor and lifecycle (Quiesce/Finalize) to allow safe in-process backup restore and Runtime rebuild without exiting the main process.
  • Add runtime context, cancellation, and task tracking to DiceManager, with GoRuntime helpers and generation-level wait groups.
  • Implement Quiesce to stop cron jobs, adapters, background tasks, and flush mutable state while keeping the DB open.
  • Implement Finalize to tear down JS runtimes, extension storage, help docs, and close the DB with shared phase semantics.
  • Add a runtimeSupervisor that owns the current applicationRuntime, coordinates API maintenance, builds/validates new Runtime instances, and publishes them.
  • Wire main.go to construct the supervisor, stop the bootstrap DiceManager, start managed Runtime, and expose a stop provider used by process cleanup.
  • Add tray/httpServe changes to operate on a cleanup callback instead of DiceManager and to periodically fetch Runtime tray tooltip via API.
runtime_supervisor.go
runtime_supervisor_test.go
dice/runtime_lifecycle.go
dice/runtime_lifecycle_test.go
dice/dice_manager.go
dice/dice.go
tray_common.go
tray_darwin.go
tray_windows.go
tray_others.go
main.go
Implement transactional, append-only restore journaling with safe ZIP validation, path normalization, disk-space checks, and rollback logic for online SQLite restore.
  • Define BackupArchiveInfo, RestoreStatus, restorePending, restoreJournal, restoreJournalRecord, and restoreStatusAuth types to represent restore state and journal entries.
  • Perform strict ZIP preflight (central directory bounds, entry count, path length, compression ratio) and enforce data/ path, rejecting unsafe Windows names and symlinks.
  • Implement v2 manifest verification over ZIP contents, including SHA-256 hashes and SQLite-specific requirements for safety backups.
  • Track restore intent/done steps per file (backup-original, install-staged, remove-installed, restore-original) and write journal.jsonl append-only records.
  • Implement ApplyScheduledRestore to extract backups into a staging area, build a per-file restore journal, and apply changes with atomic renames and directory syncs.
  • Implement rollback logic that can safely restore originals or remove staged files based on journal state, including SQLite sidecar synthesis/cleanup.
  • Add RecoverInterruptedRestore to detect and roll back incomplete transactions at startup, handle empty/torn logs, and move the system into degraded state when uncertain.
  • Provide helpers to schedule restore operations idempotently with requestId, generate and refresh status tokens, and mark restore success only after Runtime publish.
  • Add OS-specific atomic replace helpers and cross-volume/disk-space checks to ensure restore uses same volume and has enough free space.
dice/dice_backup_restore.go
dice/dice_backup_restore_test.go
dice/atomic_replace_windows.go
dice/atomic_replace_unix.go
Add new backup/restore APIs for upload, restore scheduling, and status querying with authentication, idempotency, and TOCTOU-safe download.
  • Replace legacy backup list parsing with InspectBackupArchive, returning validated metadata and a Restorable flag based on DB type and contents.
  • Implement backupDownload using OpenBackupArchive to serve content via an already-validated file descriptor and proper Content-Disposition headers.
  • Route backup deletion through Dice.DeleteBackup, which checks restore usage before removing files and supports batch deletes.
  • Add backupUpload to stream multipart uploads, store them in backups/, validate ZIP+manifest, and reuse existing identical content by SHA-256 digest.
  • Add backupRestore to accept a backup name/requestId, schedule a restore via DiceManager.ScheduleRestore, and enqueue it into the runtime restore queue.
  • Add backupRestoreStatus which returns either operation-bound status (via bearer headers) or global restore status, and ensures no caching via Cache-Control: no-store.
  • Expose new headers for restore status in uiServe CORS config.
  • Add API runtime gate middleware that returns 503 during maintenance/unavailability while keeping signin salt and restore status accessible.
  • Make signin salt endpoint read from a runtimeAuthSnapshot rather than the raw DiceManager and include passwordRequired flag.
api/backup.go
api/api_bind.go
api/runtime_gate_test.go
Make adapter and IM session execution runtime-aware, allowing graceful shutdown of managed processes and background tasks during restore.
  • Introduce DiceManager.GoRuntime/beginRuntimeTask helpers and propagate runtime context into adapter loops, reconnect delays, and long-running tasks.
  • Refactor diceServe into diceServePrepare/diceServeEndpoint, allowing supervisor-controlled startup per endpoint with context-aware ServeQQ.
  • Add managed process helpers for PlatformAdapterGocq/WalleQ/Milky to coordinate Start/Wait/Kill with runtimeStopping flags and mutexes.
  • Modify built-in QQ/Lagrange/Milky/WalleQ helpers to use the managed process helpers, non-blocking channel sends, and context-aware delays for login flows and QR handling.
  • Update IMSession.Execute/ExecuteNew, notices, group join/quit flows, and Kook/Satori/Slack/TG handlers to run tasks via runDiceRuntimeTask and respect Runtime cancellation.
  • Add RuntimeShutdown implementations to adapters (Onebot, Official QQ, Satori, Red, Minecraft, SealChat, Dodo, Kook, Milky, Gocq, WalleQ, Telegram, Slack, Dingtalk) to stop sockets, processes, and internal servers cleanly.
  • Ensure internal gocq/walle/walleq/Milky process instances stop on Runtime shutdown and that reconnect loops observe runtimeStopping flags.
dice/platform_adapter_gocq_helper.go
dice/platform_adapter_gocq.go
dice/platform_adapter_walleq_helper.go
dice/platform_adapter_walleq.go
dice/platform_adapter_milky_helper.go
dice/platform_adapter_milky.go
dice/platform_adapter_red.go
dice/platform_adapter_dodo.go
dice/platform_adapter_sealchat.go
dice/platform_adapter_minecraft.go
dice/platform_adapter_onebot.go
dice/platform_adapter_onebot_util.go
dice/platform_adapter_kook.go
dice/platform_adapter_satori.go
dice/platform_adapter_telegram.go
dice/platform_adapter_slack.go
dice/platform_adapter_official_qq.go
dice/platform_adapter_qq_helper.go
dice/platform_adapter_walleq_helper.go
dice/im_session.go
dice/builtin_commands.go
Harden restart/update paths and database operator construction for multi-generation Runtime lifecycle.
  • Switch main.go to use dboperator.NewDatabaseOperator instead of a global singleton, so each Runtime generation gets its own operator instance.
  • Ensure DB engines Close() nil-check and reset internal pointers, preventing reuse of closed operators.
  • Add buildRestartArgs to strip existing --delay arguments while preserving deployment flags and to inject a new delay depending on platform.
  • Update doReboot to probe executable path using os.Executable, verify it exists, build restart arguments, and use proper Syscall/exec semantics on Windows and Unix.
  • Make CheckVersion/Update logic context-aware, using CheckVersionContext and passing DiceManager into backend version queries.
main.go
utils/dboperator/db.go
utils/dboperator/engine/sqlite/engine_sqlite.go
utils/dboperator/engine/mysql/engine_mysql.go
utils/dboperator/engine/pgsql/engine_pgsql.go
update.go
update_test.go
utils/dboperator/db_test.go
Adjust tests and migrations for new Runtime behaviour and nocgo SQLite configuration.
  • Reinitialize migration v150 attrs user sheetIdBindByGroupUserId map at each run to avoid cross-Runtime contamination.
  • Force nocgo test builds to embed the SQLite engine where needed.
  • Add focused tests for restore journaling, ZIP safety, runtime lifecycle coordination, runtime gate behaviour, and DB operator construction under missing DSN.
  • Guard dice JS loop teardown with a jsLoopDone channel to allow Finalize to wait for JS core execution shutdown.
migrate/v2/v150/v150_attrs.go
dice/execute_new_db_nocgo_test.go
dice/service/log_db_nocgo_test.go
dice/dice_jsvm.go
dice/dice_backup_restore_test.go
runtime_supervisor_test.go
dice/runtime_lifecycle_test.go
api/runtime_gate_test.go
utils/dboperator/db_test.go

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@MX-fox
MX-fox marked this pull request as draft August 11, 2026 13:33
MX-fox added 14 commits August 15, 2026 22:19
A timed-out Quiesce/Finalize no longer permanently poisons the shared phase
result. Context-only failures can be retried by a later caller with a fresh
context, so a slow adapter or JS loop cannot leave the Runtime uncloseable.
- Onebot: protect antPool with a mutex and make isShuttingDown atomic.
- Gocq/WalleQ: serialize login index, diceServing, socket and reverse app access.
- Minecraft/SealChat: make Reconnecting atomic and guard Socket swaps/snapshots.
- Red/Dodo: guard conn/WebSocket/Client access with runtimeMu and snapshots.
- Milky/Discord/Kook/Telegram/Slack/Satori shutdown stops closing sessions in place,
  avoiding writes that race with in-flight handlers.
releaseRetryablePendingRestore now checks journal.json and old-data before
ScheduleRestore removes the .restore directory, preserving possible original
data copies from interrupted legacy restores.
…aths

- Publish backups via temp file + rename so list/clean never observe a
  half-written archive.
- Enforce the same ZIP/path/size limits during creation and keep
  non-strict backups best-effort for optional files and symlinks.
- ScheduleRestore now validates, hashes and copies from one verified fd.
- Journal entries re-validate target/rollback/staged paths on read and apply.
- Linux uses RENAME_NOREPLACE; import filenames use the full SHA-256.
- Rollback ambiguity and pending-missing/rolled_back recovery are handled
  without invalid state transitions, and rolled-back restores are no longer
  auto-retried at startup.
- Multiple queued operation IDs are preserved instead of overwriting each
  other, and the worker observes cancellation between items.
- API maintenance drain and adapter RuntimeShutdown calls are bounded by
  timeouts instead of hanging forever.
- ImportBackup no longer holds the global backup mutex while streaming the
  network request body.
- Close plugin WebSockets once in Finalize instead of per-Dice jsClear.
- Dispose the process exit job object so KILL_ON_JOB_CLOSE can reap child
  processes when a Runtime is torn down.
- Clear each Dice's DBOperator reference after closing the manager operator.
- runtimeAuth now follows uiPassword changes and keeps the login salt
  endpoint correct after password edits.
- Restore status tokens are no longer persisted in plaintext; tokens are
  cached in memory and reissued after restart or expiry.
- Replaying an already-succeeded restore returns reloading:false instead of
  enqueueing an empty run.
- Restore legacy delete/download responses keep their old success/error
  shape, and multipart uploads are bounded.
- cleanupCreate now returns an error, keeps the process file lock when the
  Runtime fails to stop, and doReboot refuses to start a second process.
- Windows fallback ports are propagated to the supervisor/runtime and
  --address keeps its historical persistence behavior.
- force_stop on Android delegates to the injected supervisor stop path.
- Dice name validation is platform-aware at startup while restore validation
  stays strictly portable across operating systems.
- Correct restart-arg test to use the real --hide-ui flag.
- Remove unused update listener functions.
- Keep the legacy --delay startup log and detect non-directory frontend
  overwrite in the degraded control plane.
- Make backup filenames same-second collision resistant and stop BackupClean
  from counting temporary/foreign files; log actual delete/skip counts.
- v1 backups without versionCode are accepted again.
- Dice.Init tolerates a missing Parent for standalone/test construction.
- dboperator drops its package-level singleton path; DBCheck uses a fresh
  engine instance.
…nits

- OfficialQQ RuntimeShutdown snapshots and closes servers/channels once.
- BuiltinQQServeProcessKillBase drops the ignored isSync parameter.
- directorySize detects Windows reparse points.
- BackupInUse takes the backup mutex itself; OpenBackupArchive also compares
  size and modtime against the validated file.
Use GetVolumeInformation to identify the volume instead of the lexical drive
letter, so junction/subst mounts are not mistaken for the same filesystem.
Legacy v1 backups with non-UTF8 entry names are decoded as CP437 before path
normalization, preserving compatibility without weakening traversal checks.
@MX-fox
MX-fox force-pushed the feat/backup-restore-v2 branch from 536c7d7 to 64c9b7d Compare August 15, 2026 14:23
MX-fox added 2 commits August 15, 2026 22:33
The Linux-specific rename file was missing syncRestoreDirectoryPath and
isLinkedRestorePath after the rebase, breaking dice compilation on Linux CI.
- Remove unused preflight/copy path wrappers after fd-based restore checks.
- Rename shadowed journal validation error and avoid variadic errors.Join
  with a guaranteed nil receiver.
- Explicitly discard cleanup errors at process-exit call sites.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant