mcpField
G
grove-account-deletion

grove-account-deletion

github.com/autumnsgrove数据与数据库667 次安装4 天前更新
代码仓库

关于 grove-account-deletion

面向 Grove 平台维护者的账号删除技能,按步骤清除租户及关联数据。流程强调删除前确认、检查订阅状态,并核验数据库与 R2 清理结果。

主要功能

  1. 01查找并确认租户身份
  2. 02删除租户及关联记录
  3. 03清理 R2 媒体对象
  4. 04核验删除结果
  5. 05检查订阅与多租户情形

适用场景

  1. 01移除演示或测试账号
  2. 02处理用户账号删除请求
  3. 03清理租户遗留数据

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/autumnsgrove/lattice --skill grove-account-deletion

安装后在 Claude Code 中输入/grove-account-deletion确认已加载。

详细介绍

Grove 账户删除

使用 wrangler CLI 命令,从 Grove 平台安全地删除租户账户及其所有关联数据。

何时启用

  • 用户说类似“删除 Alice 的账户”或“Alice 不用了”
  • 用户提到要移除测试账户
  • 用户要求在演示或试用后清理数据
  • 用户明确调用 /grove-account-deletion
  • 用户说“把那个账户彻底删掉”或“清除他们的数据”

流程

Identify → Verify → Snapshot → Delete Tenant → Clean Orphans → Purge R2 → Verify
步骤 1:识别租户

用户会提供用户名(子域名)、电子邮件或显示名称。查询对应租户:

# By username/subdomain (most common)
npx wrangler d1 execute grove-engine-db --remote --command="SELECT id, subdomain, display_name, email, plan, created_at FROM tenants WHERE subdomain = 'USERNAME';"

# By email (if username unknown)
npx wrangler d1 execute grove-engine-db --remote --command="SELECT id, subdomain, display_name, email, plan, created_at FROM tenants WHERE email = 'EMAIL';"

# By display name (fuzzy match)
npx wrangler d1 execute grove-engine-db --remote --command="SELECT id, subdomain, display_name, email, plan, created_at FROM tenants WHERE display_name LIKE '%NAME%';"

请在项目根目录(/Users/mini/Documents/Projects/Lattice)运行这些命令。

步骤 2:与用户确认

删除任何内容之前,向用户展示查到的信息,并征求明确确认:

Found tenant:
  ID:       abc-123-def
  Username: alice
  Name:     Alice Wonderland
  Email:    [email protected]
  Plan:     seedling
  Created:  2026-01-24

This will permanently delete:
  - All blog posts and pages
  - All media files (R2 storage)
  - All sessions and settings
  - Commerce data (orders, products, customers)
  - Curio configurations (timeline, journey, gallery)
  - Onboarding and billing records

Proceed with deletion?

绝不能跳过此确认步骤。 即使是测试账户也不例外。

步骤 3:删除前快照(可选,但建议执行)

对于非测试账户,快速统计即将删除的数据:

npx wrangler d1 execute grove-engine-db --remote --command="
  SELECT
    (SELECT COUNT(*) FROM posts WHERE tenant_id = 'TENANT_ID') as posts,
    (SELECT COUNT(*) FROM pages WHERE tenant_id = 'TENANT_ID') as pages,
    (SELECT COUNT(*) FROM media WHERE tenant_id = 'TENANT_ID') as media_files,
    (SELECT COUNT(*) FROM sessions WHERE tenant_id = 'TENANT_ID') as sessions;
"
步骤 4:删除租户(级联删除会处理 29 张表)

执行此单条 DELETE 命令,会级联删除:posts、pages、media 记录、tenant_settings、sessions、products、product_variants、customers、orders、order_line_items、subscriptions、connect_accounts、platform_billing、refunds、discount_codes、post_views、timeline_curio_config、timeline_summaries、timeline_activity、timeline_ai_usage、journey_curio_config、journey_snapshots、journey_summaries、journey_jobs、gallery_curio_config、gallery_images、gallery_tags、gallery_collections、git_dashboard_config。

npx wrangler d1 execute grove-engine-db --remote --command="DELETE FROM tenants WHERE id = 'TENANT_ID';"
步骤 5:清理孤立记录

以下表不会从 tenants 级联删除,需要手动清理:

# Remove onboarding record (linked by username, not FK)
npx wrangler d1 execute grove-engine-db --remote --command="DELETE FROM user_onboarding WHERE username = 'USERNAME';"

# Remove email verification codes for that user (cascades from user_onboarding via FK, but verify)
npx wrangler d1 execute grove-engine-db --remote --command="DELETE FROM email_verification_codes WHERE user_id IN (SELECT id FROM user_onboarding WHERE username = 'USERNAME');"

清理旧版 users 表:

npx wrangler d1 execute grove-engine-db --remote --command="DELETE FROM users WHERE LOWER(email) = LOWER('USER_EMAIL') AND tenant_id = 'TENANT_ID';"
步骤 5b:清理 Heartwood 身份验证记录

重要: 如果不执行此步骤,用户重新注册时会看到“欢迎回来”消息,因为 Heartwood 中仍然存在 ba_user 记录。

# Check if user has other tenants first
npx wrangler d1 execute grove-engine-db --remote --command="SELECT id, subdomain FROM tenants WHERE email = 'USER_EMAIL';"

# If no other tenants exist, clean Heartwood auth records:
# Delete sessions
npx wrangler d1 execute groveauth --remote --command="DELETE FROM ba_session WHERE user_id IN (SELECT id FROM ba_user WHERE email = 'USER_EMAIL');"

# Delete OAuth account linkages (Google, etc.)
npx wrangler d1 execute groveauth --remote --command="DELETE FROM ba_account WHERE user_id IN (SELECT id FROM ba_user WHERE email = 'USER_EMAIL');"

# Delete verification records
npx wrangler d1 execute groveauth --remote --command="DELETE FROM ba_verification WHERE identifier = 'USER_EMAIL';"

# Delete the auth user record
npx wrangler d1 execute groveauth --remote --command="DELETE FROM ba_user WHERE email = 'USER_EMAIL';"
步骤 6:清除 R2 媒体文件(如果存在媒体文件)

R2 键的格式为 {tenant_id}/{filename}。D1 media 表中的记录已被删除(级联删除),但实际的 R2 对象仍然存在。

# List objects with tenant prefix
npx wrangler r2 object list grove-media --prefix="TENANT_ID/" --remote

# Delete each object (wrangler doesn't support bulk delete, so iterate)
# For a small number of files:
npx wrangler r2 object delete grove-media --remote "TENANT_ID/filename1.webp"
npx wrangler r2 object delete grove-media --remote "TENANT_ID/filename2.webp"

如果文件较多,请生成删除脚本:

# List all keys, then delete in a loop
npx wrangler r2 object list grove-media --prefix="TENANT_ID/" --remote 2>/dev/null | jq -r '.[] .key' | while read key; do
  echo "Deleting: $key"
  npx wrangler r2 object delete grove-media --remote "$key"
done

如果删除前快照显示媒体文件数量为 0,请跳过此步骤。

步骤 7:验证删除结果

确认租户已被彻底删除:

# Verify tenant deleted
npx wrangler d1 execute grove-engine-db --remote --command="SELECT COUNT(*) as remaining FROM tenants WHERE subdomain = 'USERNAME';"

# Verify cascade worked (spot-check a few tables)
npx wrangler d1 execute grove-engine-db --remote --command="
  SELECT
    (SELECT COUNT(*) FROM posts WHERE tenant_id = 'TENANT_ID') as posts,
    (SELECT COUNT(*) FROM media WHERE tenant_id = 'TENANT_ID') as media,
    (SELECT COUNT(*) FROM sessions WHERE tenant_id = 'TENANT_ID') as sessions;
"

# Verify onboarding cleaned
npx wrangler d1 execute grove-engine-db --remote --command="SELECT COUNT(*) as remaining FROM user_onboarding WHERE username = 'USERNAME';"
步骤 8:报告结果

向用户提供简洁的摘要:

Tenant "alice" (Alice Wonderland) has been fully deleted.

Cleaned up:
  - Tenant record + 29 cascaded tables
  - Onboarding record
  - 3 R2 media files
  - Email verification codes

The subdomain "alice.grove.place" is now available for reuse.

安全规则

  1. 删除前务必确认 - 即使用户看起来很确定,也要先向他们展示租户详情
  2. 绝不能仅凭 ID 删除 - 务必先解析为便于识别的信息(子域名/电子邮件),以供确认
  3. 检查是否有有效订阅 - 如果 platform_billing.status = 'active' 或存在 LemonSqueezy 订阅,请发出警告
  4. 测试账户仍然是账户 - 同样的流程、同样的确认、同样的验证
  5. R2 中的数据无法恢复 - 删除 R2 对象后无法恢复。D1 media 记录(其中保存了键)已被级联删除,因此请在无法追踪已存储内容之前完成 R2 清理

快速模式(测试账户)

对于用户明确称为“测试账户”的账户,或在当前会话中创建的账户:

  • 仍需确认用户名/电子邮件
  • 跳过删除前快照
  • 如果未上传媒体文件,则跳过 R2 清理
  • 仍需验证删除已完成

边缘情况

用户有多个租户

一个电子邮件地址可以关联多个子域名。只删除指定的租户,不要动其他租户。

未找到租户

如果子域名/电子邮件与任何租户都不匹配,请告知用户。不要猜测。

Heartwood 会话清理

Heartwood 会话存储在 KV 中,而非 D1。D1 sessions 表用于旧版/本地会话。Heartwood 会话会自然过期(TTL 为 24 小时)。除非用户说明情况紧急,否则无需手动清理。

LemonSqueezy 订阅处于有效状态

如果已设置 user_onboarding.lemonsqueezy_subscription_id,请提醒用户,他们可能需要在 LemonSqueezy 控制面板中单独取消订阅。删除 D1 数据库不会取消外部账单。


环境

所有命令均从项目根目录运行:/Users/mini/Documents/Projects/Lattice

数据库:grove-engine-db(D1) 媒体存储桶:grove-media(R2)


干净利落地删除,是一种体面的告别。不要留下任何孤儿。

评论

支持 Markdown