tamakyi d5d25c804b fix(data): 删除改成一个事务且真删、配额与内容同事务
四类问题都出在「多次独立写入 + 失败只打日志」这个模式上。

1) 删除投票的级联没有事务(route/user/vote.go)
   选项 / 表单字段 / 提交记录 / 投票本体是 4 次独立写入,前 3 步失败只 Warn。
   中途失败会留下「选项没了但票还在」这种界面无法修复的状态 —— 重试删除时
   投票已不存在,只能报 not exist,脏数据永远留着。
   → db.Votes.DeleteWithCascade:四张表在同一事务里删完。

2) 删除其实是软删除,个人数据没真删掉
   模型都嵌了 dbutil.Model(含 DeletedAt),原来所有 Delete 都是软删:
   提交记录里参与者填写的文本、voter_ip、voter_token,以及提问里的 FromIP,
   只是「界面上看不见」,库里永久留着。用户点删除的预期不是这样。
   软删还会占住 link_token 唯一索引,删过的链接永远不能复用。
   → 删除投票与删除提问统一 Unscoped 真删;提问的追问/图片关联/配额回退
     原来也是三个独立写入(失败只 Warn),一并收进事务。

3) 配额计数会永久偏低
   「创建内容」与「quota_count+1」是两次写入,后者失败只 Warn:
   箱主看到的「已收集 7/10」偏小,达到上限后因为计数偏小实际还能继续收,
   等于定量收集被静默绕过;重置计数也校准不回来(重置是把计数清零,更脱节)。
   → 抽出 incrementQuotaTx(tx, userID, feature, delta) 作为唯一实现,
     提问 Questions.Create、私聊 CreateWithFirstMessage、投票提交
     VoteSubmissions.Create(新增 QuotaUserID)都在自己的事务里完成 +1。
   注意口径:投票的定量收集是「累计收到的票数」(提交成功时 +1),
   不是「创建了几个投票」—— 所以删除投票不回退这个计数。

4) 改写追问回答每次都发通知邮件
   箱主改措辞、补一句、修错别字都会重新保存,每次都发的话提问者会连着收到
   好几封一模一样的「您的提问有回复」。主回答那边早有 question.Answer == ""
   的判定,追问这里补齐:UpdateAnswerByID 在事务内读旧值返回 wasFirst。

顺带:
- isQuestioner 的 token 比较从 == 改为 subtle.ConstantTimeCompare,
  与 Pager 中间件里的校验同口径(== 按字节短路,可用响应时间逐字节试探)。
- CSV 导出的表头(字段名)与选项内容列补 sanitizeCSVCell。原来只有数据列走它,
  而选项名同样可能是 =HYPERLINK(...) 形式;判断也改成看 trim 后的首字符。
- 写路径的功能开关改 fail-closed:新增 db.FeatureStateOfStrict,读配置失败时
  拒绝而不是回退默认配置 —— 提问/投票的默认值是「开启」,fail-open 等于让一次
  数据库抖动绕过箱主设的关闭与定量上限。读展示仍用宽松版。

验证:全量单测(带 DB)与 go build ./... 通过;verify_round3 6/6(私聊
message_count 与实际消息数一致)、verify_p1 4/4、verify_round2 19/19、
verify_page_next 6/6、verify_chat_owner 3/3。
2026-10-29 17:18:28 +08:00
2026-10-21 12:09:28 +08:00
2026-09-10 18:04:19 +08:00
2026-10-21 12:09:28 +08:00

狼的提问箱

匿名提问箱 / Anonymous Question Box

基于原版NekoBox做了一些微小的修改。主要体现在:

  • 率先引入二维码访问,同时增加更多自定义可行性,方便自行创作。
  • 添加探索页面,允许用户自行选择添加标签并展示,为陌生人提问创造可能。
  • 稍微调整了屏蔽词策略。

截图

QRcode

QRCode

Commit with picture

CWP

Explorer

explore_setting

explore

安装

需求

  • Go (v1.19 或更高版本)
  • MySQL (v5.7 或更高版本)
  • Redis (v6.0 或更高版本)

从源码编译

git clone https://github.com/tamakyi/TamaBox.git

cd TamaBox

go build -o TamaBox

编辑配置文件

cp conf/app.sample.ini conf/app.ini

运行

./TamaBox web

架构

Architecture Architecture

NekoBox 使用 GitHub Actions 进行持续集成和部署。

当用户访问 NekoBox 时,请求将会被发送至阿里云 CDN,CDN 的访问日志将会被实时推送到阿里云日志服务 (SLS)。日志数据将在 SLS 中存储 180 天,用于审计。

用户的信息、提问和回答将被存储在 MySQL 数据库中。

用户的会话、CSRF 令牌和电子邮件验证令牌将被暂时存储在 Redis 中。

用户的整个请求和响应链路将被上传到 Uptrace 用于调试。这些数据将被储存 30 天。管理员可以使用用户提供的 TraceID 来追踪查询指定的请求。

当用户提交提问时,问题的内容将被发送到七牛文本审查服务进行审查。如果提问内容存在问题,该内容将被发送到阿里云文本审查服务进行二次审查。 如果内容审查仍未通过,该提问将被拒绝发送。这是由于七牛文本审查服务不是很准确,一些非冒犯性的内容可能被七牛文本审查误报。

当用户收到新的提问时,阿里云邮件服务(DM)会向用户的邮箱发送一封邮件。

你可以在主页查看 NekoBox 的更新日志,也欢迎访问赞助页面来打钱支持 NekoBox。 更新日志和赞助商名单存储在独立部署的 Pocketbase 服务中。

开源协议

MIT License

备注

  • 基于原版Nekobox修改,基本与原版无异,主要是添加了个人主页二维码和首页统计数,并修改了部分适合自己的参数。
S
Description
No description provided
Readme MIT
41 MiB
Languages
Go 66.5%
HTML 26.6%
Python 4.3%
CSS 1.4%
Shell 1.1%