aliyun sensor server

This commit is contained in:
tamakyi
2026-09-10 18:04:19 +08:00
parent e3e59e7c69
commit 8e2325eda5
38 changed files with 3147 additions and 344 deletions
+28 -15
View File
@@ -18,7 +18,7 @@ import (
"github.com/tamakyi/TamaBox/internal/i18n"
"github.com/tamakyi/TamaBox/internal/imagecheck"
"github.com/tamakyi/TamaBox/internal/mail"
"github.com/tamakyi/TamaBox/internal/security/censor"
"github.com/tamakyi/TamaBox/internal/security/moderation"
"github.com/tamakyi/TamaBox/internal/shared"
"github.com/tamakyi/TamaBox/internal/storage"
)
@@ -27,10 +27,18 @@ const (
MaxMessageImageSize = 5 * 1024 * 1024 // 5MB
)
// ConversationPage 私聊会话页(GET 入口)。审核拦截后的就地重渲染由 renderConversation 统一处理。
func ConversationPage(ctx context.Context) {
domain := ctx.Param("domain")
token := ctx.Param("token")
renderConversation(ctx, ctx.Param("domain"), ctx.Param("token"), "", nil)
}
// renderConversation 渲染私聊会话页,并可选地回填审核拦截提示与已输入内容。
//
// prefillContent 非空时回填到消息输入框:审核拦截(以及其它服务端校验失败)后保留用户已输入内容,
// 避免 Redirect 重渲染导致输入丢失。modErr 非空时作为错误提示展示(与提问链路 ctx.SetError
// 一致的就地重渲染,而非 flash + Redirect),提示通过 templates/base/alert 渲染。
// 函数内部在所有出口都会发起 Redirect 或 Success 渲染,调用方在调用后应直接 return。
func renderConversation(ctx context.Context, domain, token, prefillContent string, modErr error) {
pageUser, err := db.Users.GetByDomain(ctx.Request().Context(), domain)
if err != nil {
ctx.Redirect("/")
@@ -119,8 +127,19 @@ func ConversationPage(ctx context.Context) {
// 单条消息字数上限,供发送 / 回复 textarea 的 maxlength 与后端校验同源
// (conf.Limits.ChatMessageLen,[limits] 配置段)
ctx.Data["ChatMessageLimit"] = conf.Limits.ChatMessageLen
ctx.Data["CanSend"] = !conv.IsClosed && !conv.IsDeletedByOwner &&
canSend := !conv.IsClosed && !conv.IsDeletedByOwner &&
((isOwner && conv.MessageCount < conv.MaxMessages) || (isAsker && conv.MessageCount < conv.MaxMessages && !isBlocked))
ctx.Data["CanSend"] = canSend
// 回填审核拦截时已输入内容(仅当对话仍可发送,避免关闭/删除场景下回填到隐藏输入框)。
if canSend {
ctx.Data["Content"] = prefillContent
}
// 审核拦截等错误提示:就地通过 base/alert 渲染,不再走 flash + Redirect(否则内容丢失且无提示)。
if modErr != nil {
ctx.Data["HasError"] = true
ctx.Data["Error"] = modErr.Error()
}
ctx.Success("chat/conversation")
}
@@ -254,18 +273,12 @@ func SendMessage(ctx context.Context, cache cache.Cache) {
}
// 内容安全检测(仅文本)
// 统一守卫:与提问 / 投票共用同一份档位判定,箱主的「审核严格度」决定放行与否。
if content != "" {
censorResponse, err := censor.Text(ctx.Request().Context(), content)
if err != nil {
// 审核源全部不可用:fail-closed,拒绝发送而非免审放行。
logrus.WithContext(ctx.Request().Context()).WithError(err).Error("Failed to censor text")
ctx.SetErrorFlash(i18n.T(ctx.Lang(), "censor.unavailable"))
ctx.Redirect(fmt.Sprintf("/_/%s/chat/%s", domain, token))
return
}
if !censorResponse.Pass {
ctx.SetErrorFlash(censorResponse.ErrorMessage())
ctx.Redirect(fmt.Sprintf("/_/%s/chat/%s", domain, token))
if _, err := moderation.Check(ctx.Request().Context(), moderation.StrictnessFor(pageUser.ModerationStrictness), ctx.Lang(), content); err != nil {
// 审核拦截:就地重渲染会话页,回填已输入内容并展示拦截原因。
// 不复用 SetErrorFlash + Redirect:那会导致输入框内容在刷新后丢失(用户已确认的无提示+内容消失问题)。
renderConversation(ctx, domain, token, content, err)
return
}
}