fix: 封堵 ask-status domain 枚举、私聊写路径 fail-open、压缩兜底重复提交
三处小修 + 两个格式清理,均来自全盘复查: 1. ask-status domain 枚举:ErrUserNotExists 分支原返回空数组、正常分支返回带元素 数组,凭空/非空可枚举 domain 是否存在(含被 L1 隐藏或设了页面密码的半公开箱)。 改为箱主不存在时也回显 ok:false,四种失败响应完全同形。 2. 私聊 SendMessage 写路径用 FeatureStateOf(fail-open 变体):读配置失败会回退默认值, 与「写路径必须 FeatureStateOfStrict(失败即拒)」的约定不符。此前靠 chat 默认关闭 的巧合侥幸安全,现统一改 Strict。 3. 图片压缩 submit 兜底可重复提交:capture 监听器里 tbBeginSubmit 从 Promise 回调提前 到 preventDefault 同步栈,物理阻止压缩窗口内双击重复提交。 顺带:box_views.go / message_image.go / insights 两个文件补末尾换行与 struct 字段对齐 (gofmt 干净)。
This commit is contained in:
@@ -231,4 +231,4 @@ func (db *boxViews) Purge(ctx context.Context, before time.Time) (int64, error)
|
||||
return 0, errors.Wrap(res.Error, "purge box views")
|
||||
}
|
||||
return res.RowsAffected, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,4 +60,4 @@ func (db *messageImages) GetByMessageIDs(ctx context.Context, messageIDs []uint)
|
||||
result[img.MessageID] = append(result[img.MessageID], img)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,13 +77,13 @@ type Window struct {
|
||||
|
||||
// Day 趋势图里的一天。
|
||||
type Day struct {
|
||||
Key string // 2026-10-29
|
||||
Label string // 10-29
|
||||
Weekday string
|
||||
PV int64
|
||||
UV int64
|
||||
Key string // 2026-10-29
|
||||
Label string // 10-29
|
||||
Weekday string
|
||||
PV int64
|
||||
UV int64
|
||||
Questions int64
|
||||
Answers int64
|
||||
Answers int64
|
||||
// 高度百分比(0~100),直接当 CSS 高度用。
|
||||
PVHeight float64
|
||||
UVHeight float64
|
||||
|
||||
@@ -51,13 +51,13 @@ func TestFormatInt(t *testing.T) {
|
||||
func TestAggregateWindows(t *testing.T) {
|
||||
loc := time.Local
|
||||
today := time.Date(2026, 10, 29, 0, 0, 0, 0, loc)
|
||||
weekFrom := today.AddDate(0, 0, -(WeekDays - 1)) // 10-23
|
||||
prevFrom := today.AddDate(0, 0, -(2*WeekDays - 1)) // 10-16
|
||||
prevTo := today.AddDate(0, 0, -WeekDays) // 10-22
|
||||
weekFrom := today.AddDate(0, 0, -(WeekDays - 1)) // 10-23
|
||||
prevFrom := today.AddDate(0, 0, -(2*WeekDays - 1)) // 10-16
|
||||
prevTo := today.AddDate(0, 0, -WeekDays) // 10-22
|
||||
|
||||
p := &Page{}
|
||||
for i := 0; i < TrendDays; i++ {
|
||||
d := today.AddDate(0, 0, -(TrendDays - 1) + i)
|
||||
d := today.AddDate(0, 0, -(TrendDays-1)+i)
|
||||
p.Days = append(p.Days, Day{Key: d.Format("2006-01-02")})
|
||||
}
|
||||
set := func(offsetDays int, pv, uv, q, a int64) {
|
||||
@@ -72,11 +72,11 @@ func TestAggregateWindows(t *testing.T) {
|
||||
t.Fatalf("日期 %s 不在骨架里", key)
|
||||
}
|
||||
|
||||
set(0, 5, 3, 1, 0) // 今天:本周 + 今日
|
||||
set(-1, 2, 2, 0, 1) // 昨天:本周
|
||||
set(-6, 4, 1, 2, 0) // 10-23:本周起点
|
||||
set(-7, 9, 6, 0, 0) // 10-22:上周末日
|
||||
set(-13, 6, 5, 0, 0) // 10-16:上周起点
|
||||
set(0, 5, 3, 1, 0) // 今天:本周 + 今日
|
||||
set(-1, 2, 2, 0, 1) // 昨天:本周
|
||||
set(-6, 4, 1, 2, 0) // 10-23:本周起点
|
||||
set(-7, 9, 6, 0, 0) // 10-22:上周末日
|
||||
set(-13, 6, 5, 0, 0) // 10-16:上周起点
|
||||
|
||||
p.aggregateWindows(today, weekFrom, prevFrom, prevTo)
|
||||
|
||||
@@ -111,7 +111,7 @@ func TestAggregateWindowsNoPrev(t *testing.T) {
|
||||
|
||||
p := &Page{}
|
||||
for i := 0; i < TrendDays; i++ {
|
||||
d := today.AddDate(0, 0, -(TrendDays - 1) + i)
|
||||
d := today.AddDate(0, 0, -(TrendDays-1)+i)
|
||||
p.Days = append(p.Days, Day{Key: d.Format("2006-01-02")})
|
||||
}
|
||||
p.Days[len(p.Days)-1].PV = 3
|
||||
@@ -167,13 +167,13 @@ func TestVisitorID(t *testing.T) {
|
||||
|
||||
func TestIsCrawler(t *testing.T) {
|
||||
cases := map[string]bool{
|
||||
"": true, // 空 UA 基本是脚本
|
||||
"": true, // 空 UA 基本是脚本
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0 Safari/537.36": false,
|
||||
"Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)": true,
|
||||
"curl/8.4.0": true,
|
||||
"python-requests/2.31.0": true,
|
||||
"curl/8.4.0": true,
|
||||
"python-requests/2.31.0": true,
|
||||
"Mozilla/5.0 (compatible; bingbot/2.0)": true,
|
||||
"facebookexternalhit/1.1": true,
|
||||
"facebookexternalhit/1.1": true,
|
||||
"Mozilla/5.0 (iPhone; CPU iPhone OS 17_0) AppleWebKit/605.1.15 Version/17.0 Mobile Safari/604.1": false,
|
||||
}
|
||||
for ua, want := range cases {
|
||||
|
||||
@@ -252,7 +252,17 @@ func SendMessage(ctx context.Context, cache cache.Cache) {
|
||||
|
||||
// 私聊功能开关:发送前重新校验箱主私聊功能是否仍开启(创建会话时已查过,
|
||||
// 但箱主可能在会话中途关闭私聊,此时应阻止继续发消息)。
|
||||
chatState := db.FeatureStateOf(ctx.Request().Context(), conv.OwnerID, db.FeatureChat)
|
||||
//
|
||||
// 用 FeatureStateOfStrict(写路径约定):FeatureStateOf 在配置读失败时回退
|
||||
// 默认值,对提问/投票是「开启」——等于一次读配置失败就绕过箱主的关闭。
|
||||
// 私聊恰好因默认关闭而侥幸安全,但这里仍统一用 Strict,避免依赖那个巧合。
|
||||
chatState, err := db.FeatureStateOfStrict(ctx.Request().Context(), conv.OwnerID, db.FeatureChat)
|
||||
if err != nil {
|
||||
// 读配置失败即拒(fail-closed):不能让一次配置抖动绕过箱主的关闭。
|
||||
ctx.SetErrorFlash(i18n.T(ctx.Lang(), "chat.errChatClosed"))
|
||||
ctx.Redirect(fmt.Sprintf("/_/%s/chat/%s", domain, token))
|
||||
return
|
||||
}
|
||||
if !chatState.Open {
|
||||
ctx.SetErrorFlash(i18n.T(ctx.Lang(), "chat.errChatClosed"))
|
||||
ctx.Redirect(fmt.Sprintf("/_/%s/chat/%s", domain, token))
|
||||
|
||||
Reference in New Issue
Block a user