tamakyi
b9060c982b
feat(notify): 新增站级 Webhook 与用户级 Telegram 通知通道
...
在现有邮件通知之外补两条广播/点对点通道:
- 站级 Webhook([notify] webhook_url):新提问 / 新回答 / 新追问 / 新私聊 /
新投票回复统一 POST JSON 到通用 Webhook(Discord / Slack / 企业微信机器人等)。
消息体同时带 content(Discord 直接可用)与结构化 event/title/body/link 字段。
- 用户级 Telegram([notify] telegram_bot_token + users.telegram_chat_id):箱主
自填 Chat ID,新提问 / 新投票回复点对点推送。
设计约束:通知是「尽力而为」旁路 —— 任一通道发送失败只记日志、不向上抛错,
绝不影响提问/回答的主流程;通道未配置时静默跳过。发送放在 shared.SafeGo 异步
执行,与现有邮件通知同一条路径。
6 处业务触发点接入 notify;私聊消息保留仅邮件(已有 5 分钟节流,避免广播刷屏)。
profile 页新增 Telegram Chat ID 输入框 + 四语言字典。app.sample.ini 加 [notify] 段。
测试覆盖 webhook 的 JSON 结构与未配置时的静默跳过。
2026-10-29 19:27:23 +08:00
tamakyi
4e574ac64a
feat(insights): 箱主数据面板——访问量、提问趋势、处理漏斗
...
新增 /user/insights,让箱主第一次能看到「有多少人来过我的箱、提问处理得怎么样」。
数据来源:
- 新表 box_views:一行 = 一访客×一天×一箱,views 累加,唯一索引
(user_id, viewed_on, visitor)。刻意不内嵌 dbutil.Model —— 软删除 + 唯一索引
会让被删行永久占槽,且删过的槽位再也插不进去。
- 访客标识 = HMAC(日盐, IP) 前 32 hex:同日可去重(UV 准)、跨日不可关联(隐私)。
代价是近 7/30 天 UV = 各日 UV 之和,页面上已写明口径。
写入是异步的:buffered channel 512 + 2s 批量 flush,队列满即丢(统计尽力而为,
不该因为记一笔访问就拖慢页面)。过滤箱主本人、空 UA、爬虫 UA、预取请求。
密钥复用 server.xsrf_key;每日清理 400 天前的明细。
页面(纯 CSS 画图,不引图表库):
- 今日/近 7 天/近 30 天 KPI + 上周对比
- 30 天 PV/UV 双柱趋势、24 小时时段分布
- 提问处理漏斗 + 回答率进度条 + 自动结论
聚合层 internal/db/box_analytics.go 复用 analyticsDialectFor 的时区处理,
与全站 stats 分开(那个不含单箱维度、按自然日缓存)。
测试:box_views 4 项(含「同批同键先内存合并」——PG 的 ON CONFLICT 不允许
同条 INSERT 二次命中同一行)、insights 的 pct/窗口切分/visitorID/爬虫过滤。
2026-10-29 18:25:53 +08:00
tamakyi
7802bb2078
fix(security): 修复安全审计的 P1/P2/P3 项,含全局复查新发现的两处越权与外跳
...
P1:
- 外链图片:markdown 渲染层只放行站内与自有 CDN 图片。配置里的
image_bucket_cdn_host 常写成 host/path,统一归一化后再比对,避免把自家图一起剥掉
- 密码:页面/提问密码最小 6 位;密码比对挪到验证码之后;页面密码失败锁定 5 次/10 分钟
- 客户端 IP:CF-Connecting-IP / EO-Client-IP 只在 TCP 对端可信(回环或 trusted_proxies)
时采信,公网对端带来的权威头一律忽略
P2/P3:
- 跳转参数收敛到 shared.SafeRedirectTarget:拒反斜杠与控制字符(url.Parse 会把 %0d%0a
解成 CRLF,伪造 Location),最后再用 path.Clean 验一次前缀
- 注册不再暴露「邮箱已注册 / 已停用」:与全新注册共用同一个成功出口,只在日志留痕
- CSP:默认 enforce,白名单由配置派生,覆盖 umami(运行时注入)、Turnstile 与
按 sentry_dsn 条件放行的 Sentry CDN
- /user 与 /_ 下发 no-store;补 X-XSS-Protection、Permissions-Policy,并把安全头前移到
静态资源中间件之前(原先静态文件拿不到 nosniff/HSTS)
- 封禁事实落库 ban_records;429 统一带 Retry-After 与本地化文案
- token 比较改常量时间;探索页页码上限;JSON 响应显式 Content-Type
全局复查补的两处:
- 私聊 SendMessage 的 POST 缺 conv.OwnerID 校验,被拉黑者可换 domain 继续发消息
- 页面密码 next 与登录 to 同源的反斜杠外跳
验收:verify_p1_fixes 4/4、verify_round2 19/19、verify_page_next 6/6、
verify_chat_owner 3/3(含变异对照);单测与 go build ./... 全通过。
2026-10-27 12:37:40 +08:00
tamakyi
1f29d3eb2e
修设置页主题色块被模板过滤器替换成 ZgotmplZ
...
html/template 对 style 属性做 CSS 过滤,linear-gradient(...) 未标记安全会被整体替换成
ZgotmplZ —— 线上设置页四个主题色块一直是灰底(既有问题,不是这次配色改动引入)。
- FuncMap 新增 safeCSS(string → template.CSS);值只来自 internal/theme.swatchOf,
由两个已校验的色值拼成,不接受用户输入,所以这里放开是安全的
- user/profile.html 的色块改 {{ safeCSS .Swatch }}
- 新增 TestThemeSwatchRenders:渲染 user/profile,断言不含 ZgotmplZ 且含渐变串;
已做变异验证(去掉 safeCSS 后该用例失败)
2026-10-21 18:53:10 +08:00
tamakyi
27856dd4a2
字号阶梯收尾 + 主色补到 WCAG AA(新增 --tb-on-primary)
...
一号事项(剩余令牌化):
- templates/user/profile.html 20 处、base.css 9 处 font-size 全部落到 --tb-fs-*
- base.css 导航四档(0.95/0.88/0.82/0.78)合并成「桌面 md → ≤1000 xs」;logo 三档统一 lg
- 有理由保留的:.tb-pin-flag / .md-body code 的 0.9em(相对父级)、验证码 1.4/1.2rem(等宽字形)、
chat 关闭按钮 40px、password 页 3em、mail/*(邮件客户端不保证 var());行内 12px/0.75rem 等值顺手换掉
二号事项(主色对比度):
- base/default #1e87f0(3.42) → #0e6fd0(4.70);shiroko #4a7fc1(3.69) → #3b6dac(4.74);
sakura #e5678f(2.80) → #ba3e66(4.70,饱和度压到 .5 保持柔和)。同色相只压亮度,目标 4.7 留余量
- 深色块 muted 之前只对 --tb-bg 算(4.7 以为过了),对压平后的卡片其实 4.31/4.17 → 提到 #a18790 / #7f8ea1
- 新增 --tb-on-primary(亮 #ffffff / 暗 #0b1016):深色模式主色偏亮,白字只有 2.2~3.1:1,
用同一个令牌兼顾「链接色要深」和「按钮底要深」是矛盾的。.uk-button-primary 与
.uk-label:not(语义色) 改吃它 → 深色按钮变成浅底深字(6.10 / 8.65 / 7.27)
- 顺手封掉几处主色泄漏:header 验证码 focus、profile .feat-day.is-on、explore 随机投票的 JS outline、
vote-results 行内色,全部改成令牌(否则换主题时不跟着变)
生成器同步:paletteVars 导出 --tb-on-primary(白字/深字择优);?dump=palette 增加
onPrimaryCr / onPrimaryPickedBest / warnings —— 主色卡中间调时只警告不判失败;
配色面板给橙色提示。_scaffold 令牌 29 → 30。StaticVersion → 20261021c。
验证:TestSwatch 断言同步为 #ba3e66;8 种组合(base 亮暗 + 三主题亮暗)对比度全部 ≥4.5;
go test internal/{theme,templates,conf,i18n} 全绿;生成器 validate/fit/quality/palette/layers/fx/fxpipe 全过。
2026-10-21 18:39:08 +08:00
tamakyi
001306d940
模板样式令牌化:字号落到 5 档阶梯、圆角改走 --tb-radius
...
11 个模板里的硬编码字号/圆角换成令牌,为之后的深色与主题一致性打底。
字号(base.css 的 --tb-fs-xs/md/lg/xl/hero = 12/14/16/24/28):
- 0.70~0.88rem、0.72rem 等五六档零碎值 → --tb-fs-xs(标签/meta) / --tb-fs-md(正文、列表、按钮)
- 0.95~1rem → --tb-fs-lg;1.5rem 大数字 → --tb-fs-xl;1.75rem 首页主标题 → --tb-fs-hero
- 以下几处**故意改变了观感**(不是等价替换,先记下来免得以后当 regression 查):
explore 的 .explore-name 1.2rem → 1rem;.hot-vote-title 0.98 → 0.875;
首页 .tb-stat-num 手机端 1.25 → 1rem;.hot-vote-action 0.82 → 0.875(变大)。
圆角:
- 8/9/10px → --tb-radius-sm(8) / --tb-radius(12,对话框与气泡);3/4/5/6px → --tb-radius-xs(6)
- 头像 50%、胶囊 999px 保留原样(语义值,不是视觉随意值)
其它:header 验证码输入框边框 #ccc → var(--tb-border);chat 气泡与图片的圆角走内联 var()。
仍未令牌化(留给下一轮):templates/user/profile.html 还有 16 处 font-size、base.css 自身 11 处
(0.78/0.82/0.88/0.95/1.1),邮件模板 mail/* 保持 px 不动(邮件客户端不保证 var())。
2026-10-21 18:28:26 +08:00
tamakyi
6c014b3ad2
修 test01:窄屏装饰全藏导致素材消失、主题名显示成 i18n key
...
- 生成器:勾「窄屏隐藏全部装饰层」时给橙色警告(几层会消失)、图层行标「窄屏隐藏」、加层与导出结果再提醒;「窄屏」下拉在被全藏覆盖时标注不生效
- 新增 ?narrow=hide 复现该状态(这是「素材在手机上看不到」的头号来源:@media 把 10 槽全写 none,桌面完全正常)
- 主题名:新增 theme.DisplayName + 模板函数 themeName,i18n 未登记 theme.name.<slug> 时回退 slug;原来模板直接调用 i18n.T,缺词条就显示 "theme.name.test01"
- test01.css 删掉 @media 全 none 段:装饰是 fit 单位,浏览器会自己按视口缩,不需要这段
- StaticVersion → 20261020g(主题 css 内容变了,必须换缓存键,否则浏览器还会用旧文件)
2026-10-20 18:23:21 +08:00
tamakyi
e9fba8b9ed
theme v1
2026-10-19 15:23:11 +08:00
tamakyi
5c7707ec0d
全局美化 v3
2026-10-19 11:43:24 +08:00
tamakyi
d47329d505
全局美化 v1
2026-10-19 11:08:30 +08:00
tamakyi
e683e798aa
fix typo error
2026-09-10 19:27:21 +08:00
tamakyi
8e2325eda5
aliyun sensor server
2026-09-10 18:04:19 +08:00
tamakyi
e3e59e7c69
chore: baseline snapshot before moderation-strictness feature
...
Repo .git was damaged (refs/ missing, index truncated, object store near-empty).
Repaired via git init (rebuilt refs), stale index preserved as .git/index.corrupt-20260910.bak,
then full worktree committed as a rollback baseline.
2026-09-10 10:56:27 +08:00