Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions astrbot/core/backup/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,23 @@
from sqlmodel import SQLModel

from astrbot.core.db.po import (
ApiKey,
Attachment,
ChatUIProject,
CommandConfig,
CommandConflict,
ConversationV2,
CronJob,
DashboardTrustedDevice,
Persona,
PersonaFolder,
PlatformMessageHistory,
PlatformSession,
PlatformStat,
Preference,
ProviderStat,
SessionProjectRelation,
UmoAlias,
WebChatThread,
)
from astrbot.core.knowledge_base.models import (
Expand All @@ -43,6 +47,7 @@
# 主数据库模型类映射
MAIN_DB_MODELS: dict[str, type[SQLModel]] = {
"platform_stats": PlatformStat,
"provider_stats": ProviderStat,

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.

🟠 High · Database errors erase backed-up records

When a query for one of the newly registered tables fails, _export_main_database catches a failed table query and records an empty list; a replace restore then clears that target table and imports no rows, while the backup and restore still report success.

Propagate table export failures or mark the backup as incomplete so it cannot be mistaken for a complete backup.

Also at astrbot/core/backup/constants.py:65-66.

Prompt for AI agents
In `astrbot/core/backup/constants.py` at line 51:

**Database errors erase backed-up records**

When a query for one of the newly registered tables fails, `_export_main_database` catches a failed table query and records an empty list; a replace restore then clears that target table and imports no rows, while the backup and restore still report success.

Propagate table export failures or mark the backup as incomplete so it cannot be mistaken for a complete backup.

Also at `astrbot/core/backup/constants.py:65-66`.

"conversations": ConversationV2,
"cron_jobs": CronJob,
"personas": Persona,
Expand All @@ -56,6 +61,13 @@
"attachments": Attachment,
"command_configs": CommandConfig,
"command_conflicts": CommandConflict,
"umo_aliases": UmoAlias,
"api_keys": ApiKey,
}

# Device trust must be re-established after restore, never copied from a backup.
EXCLUDED_MAIN_DB_MODELS: dict[str, type[SQLModel]] = {
"dashboard_trusted_devices": DashboardTrustedDevice,
}

# 知识库元数据模型类映射
Expand Down
5 changes: 5 additions & 0 deletions astrbot/core/backup/exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,11 @@ async def export_all(
config_content = f.read()
zf.writestr("config/cmd_config.json", config_content)
self._add_checksum("config/cmd_config.json", config_content)
mcp_config_path = Path(get_astrbot_data_path()) / "mcp_server.json"
if mcp_config_path.is_file():
mcp_content = mcp_config_path.read_bytes()
zf.writestr("config/mcp_server.json", mcp_content)
self._add_checksum("config/mcp_server.json", mcp_content)
if progress_callback:
await progress_callback("config", 100, 100, "配置文件导出完成")

Expand Down
22 changes: 21 additions & 1 deletion astrbot/core/backup/importer.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

# 从共享常量模块导入
from .constants import (
EXCLUDED_MAIN_DB_MODELS,
KB_METADATA_MODELS,
MAIN_DB_MODELS,
get_backup_directories,
Expand Down Expand Up @@ -471,6 +472,22 @@ async def import_all(
except Exception as e:
result.add_warning(f"导入配置文件失败: {e}")

if "config/mcp_server.json" in zf.namelist():
try:
mcp_config_path = (
Path(get_astrbot_data_path()) / "mcp_server.json"
)
mcp_config_path.parent.mkdir(parents=True, exist_ok=True)
if mcp_config_path.exists():
shutil.copy2(
mcp_config_path,
mcp_config_path.with_suffix(".json.bak"),
)
mcp_config_path.write_bytes(zf.read("config/mcp_server.json"))
result.imported_files["mcp_config"] = 1
except Exception as e:
result.add_warning(f"Failed to restore MCP configuration: {e}")

if progress_callback:
await progress_callback("config", 100, 100, "配置文件导入完成")

Expand Down Expand Up @@ -532,7 +549,10 @@ async def _clear_main_db(self) -> None:
"""清空主数据库所有表"""
async with self.main_db.get_db() as session:
async with session.begin():
for table_name, model_class in MAIN_DB_MODELS.items():
# Replace restores also invalidate device trust on the target instance.
for table_name, model_class in (
MAIN_DB_MODELS | EXCLUDED_MAIN_DB_MODELS
).items():

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.

🟠 High · Failed restores erase existing records

When the process stops or database insertion fails after the clear commits and before the import transaction commits, import_all commits _clear_main_db() before _import_main_database() starts its separate transaction, so a crash or import failure between them leaves the newly registered tables empty along with the other cleared tables. The restore reports failure, but the target data is already lost.

Make clearing and importing the main database one transaction, or stage the restored data and swap it in only after the import succeeds.

Prompt for AI agents
In `astrbot/core/backup/importer.py` at line 555:

**Failed restores erase existing records**

When the process stops or database insertion fails after the clear commits and before the import transaction commits, `import_all` commits `_clear_main_db()` before `_import_main_database()` starts its separate transaction, so a crash or import failure between them leaves the newly registered tables empty along with the other cleared tables. The restore reports failure, but the target data is already lost.

Make clearing and importing the main database one transaction, or stage the restored data and swap it in only after the import succeeds.

try:
await session.execute(delete(model_class))
logger.debug(f"已清空表 {table_name}")
Expand Down
2 changes: 2 additions & 0 deletions docs/en/use/webui.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ The page title and section navigation stay fixed while only the settings pane on

System configuration changes save automatically. Check for a successful save message and restart AstrBot if the page indicates that a restart is required.

Backups under `Maintenance` include session aliases, model usage statistics, OpenAPI access keys, and MCP server configuration. Workspace files require a separate backup. TOTP trusted devices are excluded; replacing data from a backup clears existing device trust and requires device verification again. Older backups cannot recover data they did not include: export a new backup from the original instance before migrating. If a backup has no MCP configuration, the target instance's MCP configuration is kept.

## Plugins

Select `Extensions` in the sidebar. The top tabs are `Plugins`, `Skills`, `MCP Servers`, and `Handlers`. Within `Plugins`, switch between `Installed` and `AstrBot Plugin Market` to view local and market plugins.
Expand Down
2 changes: 2 additions & 0 deletions docs/zh/use/webui.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ ChatUI 支持以下常用能力:

系统配置修改后自动保存,请确认保存成功提示;如果页面提示需要重启,再按提示重启 AstrBot。

`维护` 中的备份包含会话别名、模型用量统计、OpenAPI 访问密钥和 MCP 服务配置。工作区文件需要单独备份。TOTP 受信任设备记录不纳入备份;替换恢复时会清除现有设备信任,需要重新验证设备。旧备份无法恢复未包含的数据,迁移前请从原实例重新导出备份。如果备份中没有 MCP 配置,则保留目标实例现有的 MCP 配置。

## 插件

点击左栏 `插件`,顶部可切换 `插件`、`技能`、`MCP` 和 `管理行为`。在 `插件` 标签内,通过 `已安装` 与 `插件市场` 切换查看本地插件和市场插件。
Expand Down
Loading
Loading