Skip to content

Latest commit

 

History

History
65 lines (42 loc) · 11.3 KB

File metadata and controls

65 lines (42 loc) · 11.3 KB

账号与数据生命周期

本文定义 USTS ACM Land 首版的账号停用、注销、数据保留和匿名化边界,并与站内隐私页及根目录 PRIVACY.md 保持一致。面向成员的公开说明以隐私页为准,工程实现变更必须同步更新两处。

数据分类

  • 公开资料:姓名、年级、专业,以及已验证的平台账号、当前榜单统计和由成功历史快照计算的时间范围刷题增量。
  • 队内私有资料:邮箱、QQ、账号状态、验证错误和管理员操作记录。
  • 头像数据:QQ 仅由服务端用于请求腾讯头像接口;规范化 WebP 对象保存在私有 member-avatars Bucket,公开读取代理只接受成员 UUID,并在每次读取时重新确认成员仍公开启用。
  • 认证数据:密码、会话和密码重置信息仅由 Supabase Auth 处理,业务表不保存密码。
  • 第三方数据:平台统计来自对应公开接口或认证查询;XCPC ELO 学校缓存来自第三方公开数据集,不是成员资料的副本。
  • 训练数据:每日题目只保存第三方题目链接和必要元数据;完成记录属于成员私有活动,讨论内容仅向启用成员和管理员提供。
  • AI 对话数据:会话标题、成员问题和模型可见回复保存在 Supabase 私有 Schema;关闭态仅允许当前登录账号通过不接受目标用户 ID 的 RPC 读取或删除本人历史,创建、重命名、归档和消息 upsert 已对应用角色撤权,管理员默认没有跨成员正文读取接口。额度账本与正文分表保存,只记录请求状态、不可逆指纹和聚合 Token。用户上传的图片对象保存在私有 webchat-images Storage Bucket,数据库只保存归属、消息关联、校验摘要和生命周期状态;浏览器不能列举 Bucket 或取得长期公开 URL。
  • 推荐计划数据:邀请码、绑定关系和奖励次数保存在私有 Schema;成员只能读取自己的邀请码和聚合奖励状态,管理员只能看到脱敏后的奖励审计,不直接读取被邀请人的邮箱、QQ 或其他私有标识。

正常使用

账号启用期间,资料、绑定、当前统计和历史快照持续保留,用于公开页面、成员管理、同步幂等和审计。成员解绑单个平台后,该平台绑定及其当前统计、历史快照和洛谷增量状态删除;不再继续同步该平台。

成员修改 QQ 后会重新生成头像缓存;清空 QQ 会移除当前头像元数据和对象。成员关闭公开展示或被停用后,既有私有对象不会被匿名代理读取;再次公开前仍保持私有。头像对象路径按同步租约版本化,公开页面只得到成员 UUID 与更新时间构造的代理 URL,不得到 QQ 或 Storage 路径。

浏览器会为防止页面切换丢失内容而保存当前用户未提交的资料草稿。保存成功、主动退出或注销账号后会清除该用户的本地草稿。

登录成员可以通过不接受目标用户 ID 的 export_own_data() RPC 下载版本化 JSON 副本。函数在数据库内重新绑定 auth.uid(),管理员调用时也只能导出本人;显式导出的范围包括 Auth 基本资料、Profile、平台绑定、统计与快照、安全裁剪后的同步记录、每日一题活动、WebChat 授权限额、聚合账本及本人会话正文。密码哈希、登录令牌、服务密钥、中转站配置、请求指纹、租约 owner token、管理员标识和其他成员数据不进入文件。浏览器使用临时 Blob URL 直接下载并立即释放,服务端不建立导出副本。

推荐计划导出同样只返回本人邀请码、全局计划状态、累计奖励次数、累计奖励 Token、是否曾被绑定和绑定时间,不暴露其他成员身份信息;全局关闭时导出中的邀请码为 null,历史奖励与绑定摘要仍保留。

AI 学习助手的历史实现为每个账号保存最多 100 个会话;单会话最多 120 条消息,序列化内容上限为 1 MiB,单条消息上限为 64 KiB。浏览器过去只在本机保存当前会话 ID 以便刷新恢复,生产对话正文由私有数据库保存。当前关闭态不提供会话浏览或单会话删除入口,也不再创建或更新会话;底层仍向登录成员保留绑定当前账号的历史读取和删除 RPC,用于数据保护兼容,但不构成成员产品入口。登录成员仍可通过个人数据导出取得本人历史,数据库每天清理最后活动超过 180 天的会话,永久注销会级联删除全部会话。会话列表索引、消息计数和体积约束继续保留,避免历史数据边界回归。

AI 图片附件只有完成服务端格式、大小、尺寸和摘要校验后才能绑定到本人消息。数据库按账号限制最多 200 个未删除附件、合计 64 MiB,并以滚动窗口限制每小时 30 个新附件;这些限制在持有账号状态行锁时执行,不能由多标签页绕过。删除消息、会话或账号时,数据库先把对象加入受控删除队列;Storage worker 确认对象删除后才把附件标记为已删除,已删除元数据再保留 7 天用于故障对账。账号注销会冻结新上传,并在仍有未确认对象时失败关闭,避免删除 Profile 后留下失去归属的私有文件。

成员修改密码时,服务端会把登录令牌对应用户与当前密码重新认证后再更新 Auth 密码,并全局撤销该用户的刷新会话;本设备随后清除本地会话,要求使用新密码重新登录。密码更新与会话撤销是两个 Auth 操作,若密码已更新但撤销接口未能确认,接口会返回部分成功状态,前端仍先退出本设备并在登录页明确提示检查其他设备会话,不会把结果误报成“密码未修改”。

停用

停用是可恢复的管理状态,不等于删除:

  • 成员立即从公开成员页和榜单隐藏。
  • 计划任务和手动同步不再更新该成员;若停用与在途第三方请求并发,最终数据库提交会锁定 Profile 并再次要求成员仍为启用,否则统计、快照和运行状态整笔回滚。
  • 成员不能修改业务资料,但 Auth 账号和已有业务数据继续保留。
  • 数据保留到管理员恢复账号,或成员完成永久注销;首版不进行自动到期删除。

永久注销

普通成员可在账号页输入当前密码并二次确认后永久注销。服务端只从登录会话推导目标用户,不接受客户端指定其他用户 ID,并再次使用 Supabase Auth 验证当前密码。管理员账号必须先在后台完成带审计原因的权限交接和降级,数据库拒绝移除最后一名启用管理员,并在 Profile 最终删除点再次拒绝当前角色仍为管理员的账号;降级后才能按普通成员流程注销。该终点检查与角色变更共享目标 Profile 行锁,因此注销流程中途发生的晋升也会失败关闭。

硬删除成功后:

  • 删除 Supabase Auth 用户和 Profile。
  • 级联删除全部平台绑定、当前统计、历史快照、同步任务、运行记录和洛谷增量状态。
  • 邀请关系按删谁保谁:邀请人删除时其邀请码、绑定和奖励记录一并消失;被邀请人删除时只匿名化绑定身份,已发奖励和累计次数保留。
  • 公告等团队公共内容保留,但创建者外键置空。
  • 每日题目作为团队公共内容保留,管理员创建者外键置空;成员的完成记录和讨论内容随 Profile 级联删除,其他成员的讨论不受影响。
  • AI 历史会话、消息正文、会话标题、图片附件元数据和当前会话关联随 Profile 级联删除;对应 Storage 对象必须先由删除队列确认清理,浏览器中的当前会话 ID 在账号退出后不再赋予任何数据访问能力。
  • QQ 头像同步先被冻结,成员头像目录全部清空后才允许 Auth 用户删除;清理失败时注销整体失败,不留下可公开读取的孤立头像。
  • 与该成员相关的审计行清除操作者、目标 ID、删除前后数据和 metadata,只保留动作、表名、时间及 account_deletion 匿名标记。
  • 匿名审计事件不包含可关联成员的 UUID、哈希、姓名、QQ、邮箱、平台账号或自由文本,因而可长期保留用于验证删除流程。

如果同步任务仍在运行,注销会失败并保留账号,成员应在同步结束后重试。应用层先做友好检查,数据库在 profile 最终删除点再次检查 queued/running 任务,因而检查后的并发任务也会让整次删除回滚。服务端的数据库全局租约覆盖完整注销临界区:取得绑定 owner 和 target user 的租约 → 把一个不含用户 ID、姓名、邮箱、平台账号或删除次数的 UTC 恢复下限写入 GitHub 仓库变量 BACKUP_RECOVERY_NOT_BEFORE 并回读确认 → 续期并确认租约仍由当前请求持有 → 停止并等待外部阶段心跳 → 调用仅限 service role 的数据库 RPC。最终 RPC 对租约单例行和目标 Profile 执行 FOR UPDATE,再次验证 owner、target、有效期、普通成员角色与无活动同步,设置仅在当前事务有效的 owner/target fence 标记,并在同一事务内删除 auth.users 与消费租约;Auth 删除触发器拒绝没有匹配标记的旧 HTTP 或旁路删除。Profile、平台数据、同步记录与审计匿名化随 Auth 外键和触发器一并提交或回滚。这样即使 Edge Runtime 暂停计时器,或旧 Edge 请求跨越 migration 部署窗口,其他删除也不能越过最终数据库边界。租约冲突、取得或删除前续期失败、变量写入或回读确认失败时,服务端不会执行 Auth 删除并返回 503;管理员、活动同步、Storage 所有权或其他受控约束拒绝删除时返回 409 并保留账号。外部恢复下限阶段仍以每分钟心跳续期;最终删除阶段的安全 fencing 由数据库事务锁和触发器保证,不依赖 JavaScript 心跳。RPC 响应在传输中丢失时,调用方不能断言账号一定未删除,而应通过重新认证和业务数据状态对账。 Supabase 官方说明直接删除 auth.users 受支持,但已签发 JWT 会持续到自身过期;本站所有业务授权还要求存在启用 Profile,因此删除后旧 JWT 无法读取或写入成员业务数据。

外部保留边界

数据库与 webchat-images 对象的加密快照、Supabase Auth/Edge 日志、GitHub Actions 日志和 Firecrawl 作业记录由对应供应商按生产配置保留,硬删除不会改写已经生成的备份。仓库备份只纳入同次数据库快照中处于 ready/attached 状态的精确对象引用,并把数据库与图片作为一个不可拆分的加密 Artifact 发布。本项目选择“禁止恢复到最近一次注销事件之前”的策略:每次注销把恢复下限保存在数据库之外,备份 metadata 同时记录生成时可见的下限;恢复工具必须使用 GitHub 当前变量复核,早于当前下限的备份一律拒绝。正式发布前仍须配置生产 Secret、核验供应商实际保留窗口,并在隔离项目演练该拒绝路径和一次允许恢复路径。

成员注销不会要求第三方平台删除其公开账号,也不会从 XCPC ELO 上游公开数据集中删除同名选手。本站缓存按数据源刷新策略失效,不再与已删除的站内成员关联。