#!/usr/bin/env bash # # 清空演示部署里的邮件数据,保留账号与接入配置。 # # sudo ./deploy/reset-demo.sh # 交互确认 # sudo ./deploy/reset-demo.sh --yes # 跳过确认(脚本化调用) # # 会清掉:邮件、会话、附件(含磁盘文件)、权限请求、免配额转发记录、限速计数 # 会保留:用户与密码、Agent 注册与默认预算、Agent 密钥、用户密钥、登录会话 # # 保留账号是刻意的:演示环境重置一次就要重新配一遍密钥与插件, # 那比手工删邮件还费事。要连账号一起清就直接删掉 .db 文件重启。 set -euo pipefail PREFIX=${AGENTMAIL_PREFIX:-/opt/agentmail} DEFAULT_DB=$PREFIX/data/agentmail.db DB=${AGENTMAIL_DB:-$DEFAULT_DB} ATTACH=${AGENTMAIL_ATTACHMENTS:-$PREFIX/data/attachments} BACKUPS=$PREFIX/backups SERVICE=agentmail-gateway.service # 只有在清理服务真正在用的那个库时才碰 systemd。 # 演练时把 AGENTMAIL_DB 指到副本,却把生产服务停了 —— 这个坏事已经发生过一次。 MANAGE_SERVICE=no [[ $DB == "$DEFAULT_DB" ]] && MANAGE_SERVICE=yes [[ $EUID -eq 0 ]] || { echo "需要 root:sudo $0" >&2; exit 1; } [[ -f $DB ]] || { echo "找不到数据库:$DB" >&2; exit 1; } command -v sqlite3 >/dev/null || { echo "需要 sqlite3" >&2; exit 1; } count() { sqlite3 "$DB" "SELECT COUNT(*) FROM $1;" 2>/dev/null || echo 0; } echo "数据库:$DB" echo echo "将清除:" printf ' 邮件 %s\n' "$(count mails)" printf ' 会话 %s\n' "$(count sessions)" printf ' 附件记录 %s\n' "$(count attachments)" printf ' 附件文件 %s\n' "$(find "$ATTACH" -type f 2>/dev/null | wc -l)" printf ' 权限请求 %s\n' "$(count permission_requests)" printf ' 转发幂等键 %s\n' "$(count relayed_mails)" echo "将保留:" printf ' 用户 %s\n' "$(count users)" printf ' Agent %s\n' "$(count agents)" printf ' Agent 密钥 %s\n' "$(count agent_keys)" printf ' 用户密钥 %s\n' "$(count user_keys)" echo if [[ ${1:-} != "--yes" ]]; then read -rp "确认清除?输入 yes 继续:" ans [[ $ans == "yes" ]] || { echo "已取消"; exit 0; } fi STAMP=$(date +%Y%m%d-%H%M%S) install -d "$BACKUPS" echo "==> 备份到 $BACKUPS/agentmail-$STAMP.db" # .backup 而不是 cp:cp 会漏掉还在 WAL 里没落盘的最新写入 sqlite3 "$DB" ".backup '$BACKUPS/agentmail-$STAMP.db'" if [[ -d $ATTACH ]] && [[ -n $(find "$ATTACH" -type f 2>/dev/null | head -1) ]]; then tar czf "$BACKUPS/attachments-$STAMP.tar.gz" -C "$(dirname "$ATTACH")" "$(basename "$ATTACH")" fi # 停服务再清:DELETE 本身在 WAL 下可以并发跑,但清理期间进来的一封邮件 # 会插进一个正被删空的会话里,留下没有父会话的孤儿行。 RESTART=no if [[ $MANAGE_SERVICE == yes ]] && systemctl is-active --quiet "$SERVICE" 2>/dev/null; then echo "==> 停止 $SERVICE" systemctl stop "$SERVICE" RESTART=yes elif [[ $MANAGE_SERVICE == no ]]; then echo "==> 非默认数据库,不动 systemd(目标:$DB)" fi echo "==> 清空邮件数据" # 手工按依赖顺序删:SQLite 的外键默认不开(PRAGMA foreign_keys=0), # 声明了 REFERENCES 也不会级联,倒过来删就会留下引用已删行的孤儿。 sqlite3 "$DB" <<'SQL' PRAGMA foreign_keys = ON; BEGIN; DELETE FROM attachments; DELETE FROM relayed_mails; DELETE FROM permission_requests; DELETE FROM mails; DELETE FROM sessions; -- 限速计数是运行态噪声,留着只会让演示时的第一次登录/建会话莫名被拦 DELETE FROM rate_limits; -- used_rounds 是累计发信统计,邮件都删了它就是一个对不上任何记录的数字 UPDATE agents SET used_rounds = 0; COMMIT; SQL echo "==> 删除附件文件" if [[ -d $ATTACH ]]; then # 只删内容文件与空壳目录,不删 $ATTACH 本身: # 它的所有者与权限是部署时设好的,重建会丢掉。 find "$ATTACH" -mindepth 1 -delete fi echo "==> 回收数据库空间" sqlite3 "$DB" "VACUUM;" if [[ $RESTART == yes ]]; then echo "==> 启动 $SERVICE" systemctl start "$SERVICE" sleep 2 curl -sf -m 5 http://127.0.0.1:8180/health >/dev/null \ && echo " 健康检查通过" \ || echo " 健康检查失败,查看:journalctl -u $SERVICE -n 50" fi echo echo "完成。备份在 $BACKUPS/agentmail-$STAMP.db" echo "账号与密钥未改动,插件无需重新配置。"