跳转到主要内容

数据库模式

本文档描述了 duplistatus 用于存储备份操作数据的 SQLite 数据库模式。

数据库位置​

数据库存储在应用程序数据目录中:

  • 默认位置:/app/data/backups.db
  • Docker 卷:duplistatus_data:/app/data
  • 文件名:backups.db

数据库迁移系统​

duplistatus 使用自动迁移系统来处理版本之间的数据库模式更改。

迁移版本历史​

以下是将数据库带到当前状态的历史迁移版本:

  • 模式 v1.0(应用程序 v0.6.x 及更早版本):初始数据库模式,包含 machines 和 backups 表
  • 模式 v2.0(应用程序 v0.7.x):添加缺失列和配置表
  • 模式 v3.0(应用程序 v0.7.x):将 machines 表重命名为 servers,添加 server_url 列
  • 模式 v3.1(应用程序 v0.8.x):增强备份数据字段,添加 server_password 列
  • 模式 v4.0(应用程序 v0.9.x / v1.0.x):添加用户访问控制(users、sessions、audit_log 表)
  • 模式 v4.1(应用程序 v1.5.x):添加 api_keys 和可选 API 密钥身份验证、IP 白名单和上传限制的默认配置键
  • 模式 v4.2(应用程序 v1.5.x):添加 daily_summary_deliveries 分类账和可选每日摘要通知的默认 daily_summary 配置

当前应用程序版本(v1.5.x)使用 模式 v4.2 作为最新数据库模式版本。

迁移过程​

  1. 自动备份:迁移前创建备份
  2. 模式更新:更新数据库结构
  3. 数据迁移:保留现有数据
  4. 验证:确认迁移成功

表​

服务器表​

存储有关正在监控的 Duplicati 服务器的信息。

字段​

字段类型描述
idTEXT PRIMARY KEY唯一服务器标识符
nameTEXT NOT NULL来自 Duplicati 的服务器名称
server_urlTEXTDuplicati 服务器 URL
aliasTEXT用户定义的友好名称
noteTEXT用户定义的备注/描述
server_passwordTEXT服务器身份验证密码
created_atDATETIME服务器创建时间戳

备份表​

存储从 duplicati 服务器接收的备份操作数据。

关键字段​

字段类型描述
idTEXT PRIMARY KEY唯一备份标识符
server_idTEXT NOT NULL引用服务器表
backup_nameTEXT NOT NULL备份作业名称
backup_idTEXT NOT NULL来自 duplicati 的备份 ID
dateDATETIME NOT NULL备份执行时间
statusTEXT NOT NULL备份状态(成功、警告、错误、致命)
duration_secondsINTEGER NOT NULL持续时间(秒)
sizeINTEGER源文件大小
uploaded_sizeINTEGER上传数据大小
examined_filesINTEGER检查的文件数量
warningsINTEGER警告数量
errorsINTEGER错误数量
created_atDATETIME记录创建时间戳

消息数组(JSON 存储)​

字段类型描述
messages_arrayTEXT日志消息的 JSON 数组
warnings_arrayTEXT警告消息的 JSON 数组
errors_arrayTEXT错误消息的 JSON 数组
available_backupsTEXT可用备份版本的 JSON 数组

文件操作字段​

字段类型描述
examined_filesINTEGER备份期间检查的文件数量
opened_filesINTEGER为备份打开的文件数量
added_filesINTEGER添加到备份的新文件数量
modified_filesINTEGER备份中修改的文件数量
deleted_filesINTEGER从备份中删除的文件数量
deleted_foldersINTEGER从备份中删除的文件夹数量
added_foldersINTEGER添加到备份的文件夹数量
modified_foldersINTEGER备份中修改的文件夹数量
not_processed_filesINTEGER未处理的文件数量
too_large_filesINTEGER过大而无法处理的文件数量
files_with_errorINTEGER出错的文件数量
added_symlinksINTEGER添加的符号链接数量
modified_symlinksINTEGER修改的符号链接数量
deleted_symlinksINTEGER删除的符号链接数量

文件大小字段​

字段类型描述
size_of_examined_files整数备份期间检查的文件大小
size_of_opened_files整数为备份打开的文件大小
size_of_added_files整数添加到备份的新文件大小
size_of_modified_files整数备份中修改的文件大小

操作状态字段​

字段类型描述
parsed_result文本(非空)解析的操作结果
main_operation文本(非空)主要操作类型
interrupted布尔值备份是否被中断
partial_backup布尔值备份是否为部分备份
dryrun布尔值备份是否为试运行
version文本使用的 duplicati 版本
begin_time日期时间(非空)备份开始时间
end_time日期时间(非空)备份结束时间
warnings_actual_length整数实际警告数量
errors_actual_length整数实际错误数量
messages_actual_length整数实际消息数量

后端统计字段​

字段类型描述
bytes_downloaded整数从目标位置下载的字节数
known_file_size整数目标位置上的已知文件大小
last_backup_dateDATETIME目标位置的最后备份日期
backup_list_countINTEGER备份版本数量
reported_quota_errorBOOLEAN已报告配额错误
reported_quota_warningBOOLEAN已报告配额警告
backend_main_operationTEXT后端主要操作
backend_parsed_resultTEXT后端解析结果
backend_interruptedBOOLEAN后端操作已中断
backend_versionTEXT后端版本
backend_begin_timeDATETIME后端操作开始时间
backend_durationTEXT后端操作持续时间
backend_warnings_actual_lengthINTEGER后端警告计数
backend_errors_actual_lengthINTEGER后端错误计数

配置表​

存储应用程序配置设置。

字段​

字段类型描述
keyTEXT PRIMARY KEY NOT NULL配置键
valueTEXT配置值 (JSON)

常见配置键​

  • email_config: 电子邮件通知设置
  • ntfy_config: NTFY 通知设置
  • overdue_tolerance: 过期备份容忍度设置
  • notification_templates: 通知消息模板
  • daily_summary: 每日摘要模式、计划、时区、可选的公共仪表板 URL,以及可选的 SMTP 收件人覆盖(smtpRecipient;空值使用电子邮件设置)
  • cron_service: Cron 任务计划,包括 daily-summary-dispatch(minute hour * * * 来自 daily_summary.utcTime)
  • audit_retention_days: 审计日志保留期限(默认:90 天)

数据库版本表​

跟踪数据库模式版本以用于迁移目的。

字段​

字段类型描述
versionTEXT PRIMARY KEY数据库版本
applied_atDATETIME迁移应用的时间

用户表​

存储用户账户信息以用于身份验证和访问控制。

字段​

字段类型描述
idTEXT PRIMARY KEY唯一用户标识符
usernameTEXT UNIQUE NOT NULL登录用户名
password_hashTEXT NOT NULLBcrypt 哈希密码
is_adminBOOLEAN NOT NULL用户是否具有管理员权限
must_change_passwordBOOLEAN是否需要更改密码
created_atDATETIME账户创建时间戳
updated_atDATETIME最后更新时间戳
last_login_atDATETIME最后成功登录时间戳
last_login_ipTEXT最后登录的 IP 地址
failed_login_attemptsINTEGER登录失败尝试次数
locked_untilDATETIME账户锁定过期时间(如果被锁定)

会话表​

存储用户会话数据以进行身份验证和安全控制。

字段​

字段类型描述
idTEXT PRIMARY KEY会话标识符
user_idTEXT引用用户表(未认证会话可为空)
created_atDATETIME会话创建时间戳
last_accessedDATETIME最后访问时间戳
expires_atDATETIME NOT NULL会话过期时间戳
ip_addressTEXT会话来源的 IP 地址
user_agentTEXT用户代理字符串
csrf_tokenTEXT会话的 CSRF 令牌
csrf_expires_atDATETIMECSRF 令牌过期时间

审计日志表​

存储用户操作和系统事件的审计轨迹。

字段​

字段类型描述
idINTEGER PRIMARY KEY AUTOINCREMENT唯一审计日志条目标识符
timestampDATETIME事件时间戳
user_idTEXT引用用户表(可为空)
usernameTEXT操作时的用户名
actionTEXT NOT NULL执行的操作
categoryTEXT NOT NULL操作类别(例如 'authentication'、'settings'、'backup')
target_typeTEXT目标类型(例如 'server'、'backup'、'user')
target_idTEXT目标标识符
detailsTEXT额外详情(JSON)
ip_addressTEXT请求者的 IP 地址
user_agentTEXT用户代理字符串
statusTEXT NOT NULL操作状态('success'、'failure'、'error')
error_messageTEXT操作失败时的错误消息

API 密钥表​

存储外部 HTTP API 的哈希 API 密钥。明文密钥仅在创建时显示一次,不会被存储。

字段​

字段类型描述
idTEXT PRIMARY KEY唯一密钥标识符
nameTEXT NOT NULL显示名称
key_hashTEXT UNIQUE密钥的 SHA-256 哈希
key_prefixTEXT密钥的前四个字符(用于指纹识别)
key_suffixTEXT密钥的后四个字符(用于指纹识别)
scopeTEXT NOT NULLupload 或 read
descriptionTEXT可选描述
enabledINTEGER密钥激活时为 1
created_atDATETIME创建时间戳
created_byTEXT创建密钥的管理员的用户 ID
expires_atDATETIME可选过期时间
last_used_atDATETIME上次成功使用时间
usage_countINTEGER成功使用次数

相关配置键在 configurations 表中:external_api_require_api_key, ip_trusted_proxies, admin_ip_allowlist, external_api_ip_allowlist, upload_limits。

每日摘要发送表​

用于每日摘要邮件发送的每通道分类账。旧版本行可能包含早期版本中的 ntfy 通道。每个计划执行(或唯一的手动发送)每个通道最多有一行。发送前存储渲染后的有效载荷,以便重试时保持相同的快照。超过30天的行将被清理。

如果进程在提供商接受消息后但在记录成功之前终止,则该通道可能会重试(至少一次)。

字段​

字段类型描述
idTEXT PRIMARY KEY唯一发送标识符
occurrence_keyTEXT NOT NULL计划密钥 scheduled:UTC:{date}:{HH:mm} 或 manual:{uuid}
channelTEXT NOT NULLemail 或 ntfy
triggerTEXT NOT NULLscheduled, manual, 或 retry
summary_dateTEXT NOT NULL快照的本地日历日期
time_zoneTEXT NOT NULL已保存的IANA时区
payload_jsonTEXT渲染后的主题、HTML、文本和NTFY字段
stateTEXT NOT NULLpending, sending, sent, 或 failed
attempt_countINTEGER发送尝试次数
next_retry_atDATETIME失败通道可再次被认领的时间
lease_expires_atDATETIME认领租约;可以回收陈旧的租约
errorTEXT最后错误(如果有)
created_atDATETIME行创建时间戳
updated_atDATETIME最后更新时间戳
sent_atDATETIME成功时间戳

(occurrence_key, channel) 上的唯一索引可防止在同一通道上重复发送相同的事件。

会话管理​

数据库支持的会话存储​

会话存储在数据库中,具有内存回退:

  • 主存储:数据库支持的会话表
  • 回退:内存存储(旧版支持或错误情况)
  • 会话 ID:加密安全的随机字符串
  • 过期:可配置的会话超时
  • CSRF 保护:跨站请求伪造保护
  • 自动清理:过期会话会自动删除

会话 API 端点​

  • POST /api/session:创建新会话
  • GET /api/session:验证现有会话
  • DELETE /api/session:销毁会话
  • GET /api/csrf:获取 CSRF 令牌

索引​

数据库包含多个索引以实现最佳查询性能:

  • 主键:所有表都有主键索引
  • 外键:备份表中的服务器引用、会话和审计日志中的用户引用
  • 查询优化:经常查询字段上的索引
  • 日期索引:日期字段上的索引用于基于时间的查询
  • 用户索引:用户名索引用于快速用户查找
  • 会话索引:过期和 user_id 索引用于会话管理
  • 审计索引:时间戳、user_id、操作、类别和状态索引用于审计查询
  • API 密钥索引:唯一哈希,以及用于身份验证的启用/范围查找

关系​

  • 服务器 → 备份:一对多关系
  • 用户 → 会话:一对多关系(会话可以在没有用户的情况下存在)
  • 用户 → 审计日志:一对多关系(审计条目可以在没有用户的情况下存在)
  • 用户 → API 密钥:通过 created_by 的一对多关系(用户删除后密钥仍然保留)
  • 备份 → 消息:嵌入式 JSON 数组
  • 配置:键值存储

数据类型​

  • TEXT:字符串数据、JSON 数组
  • INTEGER:数值数据、文件计数、大小
  • REAL:浮点数、持续时间
  • DATETIME:时间戳数据
  • BOOLEAN:真/假值

备份状态值​

  • Success:备份成功完成
  • Warning:备份完成但有警告
  • Error:备份完成但有错误
  • Fatal:备份致命失败

常见查询​

获取服务器的最新备份​

SELECT * FROM backups
WHERE server_id = ?
ORDER BY date DESC
LIMIT 1;

获取服务器的所有备份​

SELECT * FROM backups
WHERE server_id = ?
ORDER BY date DESC;

获取服务器摘要​

SELECT
s.name,
s.alias,
COUNT(b.id) as backup_count,
MAX(b.date) as last_backup,
b.status as last_status
FROM servers s
LEFT JOIN backups b ON s.id = b.server_id
GROUP BY s.id;

获取总体摘要​

SELECT
COUNT(DISTINCT s.id) as total_servers,
COUNT(b.id) as total_backups_runs,
COUNT(DISTINCT s.id || ':' || b.backup_name) as total_backups,
COALESCE(SUM(b.uploaded_size), 0) as total_uploaded_size,
(
SELECT COALESCE(SUM(b2.known_file_size), 0)
FROM backups b2
INNER JOIN (
SELECT server_id, MAX(date) as max_date
FROM backups
GROUP BY server_id
) latest ON b2.server_id = latest.server_id AND b2.date = latest.max_date
) as total_storage_used,
(
SELECT COALESCE(SUM(b2.size_of_examined_files), 0)
FROM backups b2
INNER JOIN (
SELECT server_id, MAX(date) as max_date
FROM backups
GROUP BY server_id
) latest ON b2.server_id = latest.server_id AND b2.date = latest.max_date
) as total_backuped_size
FROM servers s
LEFT JOIN backups b ON b.server_id = s.id;

数据库清理​

-- Delete old backups (older than 30 days)
DELETE FROM backups
WHERE date < datetime('now', '-30 days');

-- Delete servers with no backups
DELETE FROM servers
WHERE id NOT IN (SELECT DISTINCT server_id FROM backups);

JSON 到数据库映射​

API 请求体到数据库列映射​

当 duplicati 通过 HTTP POST 发送备份数据时,JSON 结构被映射到数据库列:

{
"Data": {
"ExaminedFiles": 15399, // → examined_files
"OpenedFiles": 1861, // → opened_files
"AddedFiles": 1861, // → added_files
"SizeOfExaminedFiles": 11086692615, // → size_of_examined_files
"SizeOfOpenedFiles": 13450481, // → size_of_opened_files
"SizeOfAddedFiles": 13450481, // → size_of_added_files
"SizeOfModifiedFiles": 0, // → size_of_modified_files
"ParsedResult": "Success", // → status
"BeginTime": "2025-04-21T23:45:46.9712217Z", // → begin_time and date
"Duration": "00:00:51.3856057", // → duration_seconds (calculated)
"WarningsActualLength": 0, // → warnings_actual_length
"ErrorsActualLength": 0 // → errors_actual_length
},
"Extra": {
"machine-id": "66f5ffc7ff474a73a3c9cba4ac7bfb65", // → server_id
"machine-name": "WSJ-SER5", // → server name
"backup-name": "WSJ-SER5 Local files", // → backup_name
"backup-id": "DB-2" // → backup_id
}
}

注意:备份表中的 size 字段存储 SizeOfExaminedFiles,uploaded_size 存储备份操作中实际上传/传输的大小。