restrict v7
This commit is contained in:
@@ -185,12 +185,13 @@ func AnswerFollowUp(ctx context.Context, pageUser *db.User, question *db.Questio
|
||||
|
||||
if err := db.FollowUps.UpdateAnswerByID(ctx.Request().Context(), question.ID, followUpID, f.Answer); err != nil {
|
||||
if errors.Is(err, db.ErrFollowUpNotFound) {
|
||||
ctx.SetErrorFlash(i18n.T(ctx.Lang(), "followup.errNotFound"))
|
||||
// 同请求渲染需用 SetError(SetErrorFlash 要等下一次请求才显示)。
|
||||
ctx.SetError(errors.New(i18n.T(ctx.Lang(), "followup.errNotFound")), f)
|
||||
ctx.Success("question/item")
|
||||
return
|
||||
}
|
||||
logrus.WithContext(ctx.Request().Context()).WithError(err).Error("Failed to answer follow up")
|
||||
ctx.SetInternalErrorFlash()
|
||||
ctx.SetInternalError()
|
||||
ctx.Success("question/item")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -623,7 +623,7 @@ func New(ctx context.Context, f form.NewQuestion, pageUser *db.User, recaptcha r
|
||||
UploaderUserID: askerUserID,
|
||||
}); err != nil {
|
||||
if errors.Is(err, ErrUploadImageSizeTooLarge) || errors.Is(err, imagecheck.ErrImageTooLarge) {
|
||||
ctx.SetErrorFlash(i18n.T(ctx.Lang(), "question.errImageTooLarge"))
|
||||
ctx.SetError(errors.New(i18n.T(ctx.Lang(), "question.errImageTooLarge")), f)
|
||||
ctx.Success("question/list")
|
||||
return
|
||||
} else {
|
||||
|
||||
@@ -217,7 +217,7 @@ func PublishAnswer(ctx context.Context, pageUser *db.User, question *db.Question
|
||||
IsDeletingPrevious: true,
|
||||
}); err != nil {
|
||||
if errors.Is(err, ErrUploadImageSizeTooLarge) || errors.Is(err, imagecheck.ErrImageTooLarge) {
|
||||
ctx.SetErrorFlash(err.Error())
|
||||
ctx.SetError(err, f)
|
||||
ctx.Success("question/item")
|
||||
return
|
||||
} else {
|
||||
|
||||
@@ -7,8 +7,10 @@ package question
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/flamego/recaptcha"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/tamakyi/TamaBox/internal/captcha"
|
||||
"github.com/tamakyi/TamaBox/internal/context"
|
||||
"github.com/tamakyi/TamaBox/internal/db"
|
||||
"github.com/tamakyi/TamaBox/internal/form"
|
||||
@@ -23,9 +25,11 @@ import (
|
||||
// - 理由只接受固定枚举,不做自由文本 —— 否则举报框本身成了新的辱骂输入口;
|
||||
// - 同 IP 同内容重复举报**幂等成功**,且提示与首次完全一致,
|
||||
// 否则「已举报过」会变成「这条内容是否存在」的探测接口;
|
||||
// - **要验证码**:举报是匿名入口,不设成本等于给批量刷量开门(同 IP 唯一索引只挡单来源,
|
||||
// 换 IP 即可绕过);这里与提问/私聊共用同一套验证码通道;
|
||||
// - 举报只是信号:处置仍由箱主(后台删除/设私密)或运维(TamaBox restrict)完成,
|
||||
// 本 handler 不改变任何内容可见性。
|
||||
func Report(ctx context.Context, pageUser *db.User, question *db.Question, f form.ReportQuestion) {
|
||||
func Report(ctx context.Context, pageUser *db.User, question *db.Question, f form.ReportQuestion, recaptcha recaptcha.RecaptchaV3) {
|
||||
back := fmt.Sprintf("/_/%s/%d", pageUser.Domain, question.ID)
|
||||
|
||||
if ctx.HasError() || !db.IsValidReportReason(f.Reason) {
|
||||
@@ -34,6 +38,20 @@ func Report(ctx context.Context, pageUser *db.User, question *db.Question, f for
|
||||
return
|
||||
}
|
||||
|
||||
// 验证码:与提问 / 私聊同源(recaptcha / turnstile / math 三种由配置决定)。
|
||||
ok, err := captcha.Verify(f.Recaptcha, shared.ResolveClientIP(ctx.Request().Request), ctx.Session, recaptcha, ctx.Lang())
|
||||
if err != nil {
|
||||
logrus.WithContext(ctx.Request().Context()).WithError(err).Error("Failed to check captcha for report")
|
||||
ctx.SetErrorFlash(err.Error())
|
||||
ctx.Redirect(back)
|
||||
return
|
||||
}
|
||||
if !ok {
|
||||
ctx.SetErrorFlash(i18n.T(ctx.Lang(), "captcha.error"))
|
||||
ctx.Redirect(back)
|
||||
return
|
||||
}
|
||||
|
||||
// 兜底:只有公开且已回答的内容可被举报(Questioner 中间件已挡住私密/未回答)。
|
||||
if question.Answer == "" || question.IsPrivate {
|
||||
ctx.Redirect(back)
|
||||
|
||||
@@ -91,12 +91,12 @@ func UpdateProfile(ctx context.Context, f form.UpdateProfile) {
|
||||
uploadedURL, err := storage.UploadAvatarToS3(avatarFile, avatarFileHeader)
|
||||
if err != nil {
|
||||
if errors.Is(err, imagecheck.ErrImageTooLarge) {
|
||||
ctx.SetErrorFlash(err.Error())
|
||||
ctx.SetError(err, f)
|
||||
ctx.Success("user/profile")
|
||||
return
|
||||
}
|
||||
if errors.Is(err, imagecheck.ErrBadAspectRatio) {
|
||||
ctx.SetErrorFlash(i18n.T(ctx.Lang(), "profile.err.avatarRatio"))
|
||||
ctx.SetError(errors.New(i18n.T(ctx.Lang(), "profile.err.avatarRatio")), f)
|
||||
ctx.Success("user/profile")
|
||||
return
|
||||
}
|
||||
@@ -122,12 +122,12 @@ func UpdateProfile(ctx context.Context, f form.UpdateProfile) {
|
||||
uploadedURL, err := storage.UploadBackgroundToS3(backgroundFile, backgroundFileHeader)
|
||||
if err != nil {
|
||||
if errors.Is(err, imagecheck.ErrImageTooLarge) {
|
||||
ctx.SetErrorFlash(err.Error())
|
||||
ctx.SetError(err, f)
|
||||
ctx.Success("user/profile")
|
||||
return
|
||||
}
|
||||
if errors.Is(err, imagecheck.ErrBadAspectRatio) {
|
||||
ctx.SetErrorFlash(i18n.T(ctx.Lang(), "profile.err.backgroundRatio"))
|
||||
ctx.SetError(errors.New(i18n.T(ctx.Lang(), "profile.err.backgroundRatio")), f)
|
||||
ctx.Success("user/profile")
|
||||
return
|
||||
}
|
||||
@@ -150,7 +150,7 @@ func UpdateProfile(ctx context.Context, f form.UpdateProfile) {
|
||||
uploadedURL, err := storage.UploadPictureToS3(backgroundimageFile, backgroundimageFileHeader)
|
||||
if err != nil {
|
||||
if errors.Is(err, imagecheck.ErrImageTooLarge) {
|
||||
ctx.SetErrorFlash(err.Error())
|
||||
ctx.SetError(err, f)
|
||||
ctx.Success("user/profile")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -55,6 +55,12 @@ func QuestionList(ctx context.Context) {
|
||||
if statusParam != "all" && statusParam != "answered" && statusParam != "unanswered" {
|
||||
statusParam = "all"
|
||||
}
|
||||
// reported 筛选:只看当前有「待处理举报」的提问。
|
||||
// 依赖下方 reportCounts(同一份查询复用),不额外打库。
|
||||
reportedParam := ctx.Query("reported")
|
||||
if reportedParam != "reported" {
|
||||
reportedParam = "all"
|
||||
}
|
||||
typeParam := ctx.Query("type")
|
||||
// 私聊已独立成"私聊"标签页(见 ReceivedChats),不再参与提问列表的类型筛选;
|
||||
// 旧的 type=chat 链接统一归一为"全部",避免空列表。
|
||||
@@ -196,6 +202,9 @@ func QuestionList(ctx context.Context) {
|
||||
if readStatus == 2 && isRead {
|
||||
continue
|
||||
}
|
||||
if reportedParam == "reported" && reportCounts[q.ID] == 0 {
|
||||
continue
|
||||
}
|
||||
itemType := "question_public"
|
||||
if q.IsPrivate {
|
||||
itemType = "question_private"
|
||||
@@ -352,6 +361,7 @@ func QuestionList(ctx context.Context) {
|
||||
ctx.Data["SelectedStatus"] = statusParam
|
||||
ctx.Data["SelectedRead"] = readParam
|
||||
ctx.Data["SelectedType"] = typeParam
|
||||
ctx.Data["SelectedReported"] = reportedParam
|
||||
|
||||
// 我发起的私聊 — 同样使用批量查询与分页(复用 chat_cursor 游标)
|
||||
askerConvs, err := db.Conversations.ListByAsker(ctx.Request().Context(), ctx.User.ID, chatPageSize, int(chatOffset))
|
||||
|
||||
Reference in New Issue
Block a user