Files
tamabox/route/user/harassment.go
T

127 lines
4.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Copyright 2022 E99p1ant. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package user
import (
"strings"
"github.com/sirupsen/logrus"
"github.com/tamakyi/TamaBox/internal/conf"
"github.com/tamakyi/TamaBox/internal/context"
"github.com/tamakyi/TamaBox/internal/db"
"github.com/tamakyi/TamaBox/internal/form"
"github.com/tamakyi/TamaBox/internal/i18n"
)
// returnTab 表单提交后回跳的面板:action 携带 ?tab= 来源参数,保存后停留原面板。
func returnTab(ctx context.Context) string {
return profileURLWithTab(ProfileTab(ctx.Query("tab")))
}
// abortOnFormError 处理表单校验/反序列化失败:返回 true 表示已中止本次写入,不触碰既有配置。
//
// ⚠️ form.Bind 反序列化失败(请求体超限、body 畸形)时只置位错误并继续走 handler 链
// (flamego 语义:响应未写即自动续链),零值结构体会进入业务动作,把用户既有配置静默清空:
// 防骚扰被改回 none、屏蔽词/标签被清空、访问与提问密码被解除。
// 错误信息已由 Bind 写入 Data["Error"],此处转入 flash 以便 PRG 重定向后仍可展示。
func abortOnFormError(ctx context.Context, redirectTo string) bool {
if !ctx.HasError() {
return false
}
if msg, ok := ctx.Data["Error"].(string); ok && msg != "" {
ctx.SetErrorFlash(msg)
}
ctx.Redirect(redirectTo)
return true
}
// UpdateHarassment 仅更新防骚扰类型(none / register_only)。只写 harassment_setting 列:
// 全局屏蔽词已拆分为 UpdateBlockWords,二者互不覆盖(同表单一次写两列时,
// 面板拆分后单侧保存会误清空另一侧)。
func UpdateHarassment(ctx context.Context, f form.UpdateHarassment) {
// 零值 RegisterOnly="" 会把防骚扰静默改回 none(见 abortOnFormError)。
if abortOnFormError(ctx, returnTab(ctx)) {
return
}
harassmentSettingType := db.HarassmentSettingNone
if f.RegisterOnly != "" {
harassmentSettingType = db.HarassmentSettingTypeRegisterOnly
}
if err := db.Users.UpdateHarassmentType(ctx.Request().Context(), ctx.User.ID, harassmentSettingType); err != nil {
logrus.WithContext(ctx.Request().Context()).WithError(err).Error("Failed to update harassment setting")
ctx.SetInternalErrorFlash()
ctx.Redirect(returnTab(ctx))
return
}
ctx.SetSuccessFlash(i18n.T(ctx.Lang(), "admin.flash.harassmentUpdated"))
ctx.Redirect(returnTab(ctx))
}
// UpdateBlockWords 更新全局屏蔽词表:提问、私聊(对方发给我的消息)与投票提交的
// 开放文本共用同一份词表(users.block_words)。
func UpdateBlockWords(ctx context.Context, f form.UpdateBlockWords) {
// 零值 BlockWords="" 会经 sanitizeBlockWords 得到空词表,静默清空全部屏蔽词。
if abortOnFormError(ctx, returnTab(ctx)) {
return
}
words, errKey, errWord := sanitizeBlockWords(f.BlockWords)
if errKey != "" {
if errKey == "admin.flash.blockWordTooLong" {
ctx.SetErrorFlash(i18n.T(ctx.Lang(), errKey, errWord))
} else {
ctx.SetErrorFlash(i18n.T(ctx.Lang(), errKey))
}
ctx.Redirect(returnTab(ctx))
return
}
if err := db.Users.UpdateBlockWords(ctx.Request().Context(), ctx.User.ID, strings.Join(words, ",")); err != nil {
logrus.WithContext(ctx.Request().Context()).WithError(err).Error("Failed to update block words")
ctx.SetInternalErrorFlash()
ctx.Redirect(returnTab(ctx))
return
}
ctx.SetSuccessFlash(i18n.T(ctx.Lang(), "admin.flash.blockWordsUpdated"))
ctx.Redirect(returnTab(ctx))
}
// sanitizeBlockWords 把输入词串规整为去重词表:全角逗号转半角、逐词 TrimSpace、
// 跳过空词与重复词;单词长度超限(conf.Limits.MaxBlockWordLen)或总数超限
// (conf.Limits.MaxBlockWords)返回对应提示。成功时 errKey 为空,errWord 为超长的具体词。
func sanitizeBlockWords(raw string) (words []string, errKey string, errWord string) {
blockWords := strings.ReplaceAll(raw, ",", ",")
blockWords = strings.TrimSpace(blockWords)
words = make([]string, 0)
wordSet := make(map[string]struct{})
for _, word := range strings.Split(blockWords, ",") {
word := strings.TrimSpace(word)
if word == "" {
continue
}
if _, ok := wordSet[word]; ok {
continue
}
wordSet[word] = struct{}{}
// 按 rune 计数:早期用 len(word) 按字节算,中文一词 3 字节,
// 导致中文屏蔽词实际只能输 3 个字,与界面文案「最大长度为 10」不符。
if len([]rune(word)) > conf.Limits.MaxBlockWordLen {
return nil, "admin.flash.blockWordTooLong", word
}
words = append(words, word)
}
if len(words) > conf.Limits.MaxBlockWords {
return nil, "admin.flash.blockWordsTooMany", ""
}
return words, "", ""
}