feat(hide): 密码可见隐藏块,发帖双栏预览与门禁体验修复
增加密码解锁与游客签名 cookie;发帖页对齐 1440 并默认双栏预览;修复 locked 解析、按钮对比度与回复聚焦。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -426,8 +426,9 @@ type UpdatePostInput struct {
|
||||
}
|
||||
|
||||
// GetByIDForViewer 获取帖子详情(带状态可见性 + 正文访问控制)。
|
||||
// pwdUnlocked 为密码隐藏块已解锁下标(来自签名 cookie);可为 nil。
|
||||
// 已软删帖返回 tombstone(无正文/附件);已硬删或不存在返回 ErrPostNotFound。
|
||||
func (s *PostService) GetByIDForViewer(id, viewerID uint, loadActor func() *Actor) (*PostDetail, error) {
|
||||
func (s *PostService) GetByIDForViewer(id, viewerID uint, loadActor func() *Actor, pwdUnlocked map[int]bool) (*PostDetail, error) {
|
||||
var post model.Post
|
||||
err := s.db.Preload("Board").Preload("User").First(&post, id).Error
|
||||
if err != nil {
|
||||
@@ -461,7 +462,7 @@ func (s *PostService) GetByIDForViewer(id, viewerID uint, loadActor func() *Acto
|
||||
post.ViewCount++
|
||||
|
||||
detail := buildPostDetail(&post)
|
||||
sanitized, fullyLocked, hint := s.sanitizePostContent(&post, viewerID, loadActor)
|
||||
sanitized, fullyLocked, hint := s.sanitizePostContent(&post, viewerID, loadActor, pwdUnlocked)
|
||||
detail.Content = sanitized
|
||||
detail.ContentLocked = fullyLocked
|
||||
detail.AccessHint = hint
|
||||
@@ -517,8 +518,8 @@ func buildPostDetail(post *model.Post) *PostDetail {
|
||||
}
|
||||
|
||||
// sanitizePostContent 按读者能力对正文 :::hide 块脱敏;fullyLocked 表示无可见正文。
|
||||
func (s *PostService) sanitizePostContent(post *model.Post, viewerID uint, loadActor func() *Actor) (content string, fullyLocked bool, hint string) {
|
||||
caps := s.buildHideViewerCaps(post, viewerID, loadActor)
|
||||
func (s *PostService) sanitizePostContent(post *model.Post, viewerID uint, loadActor func() *Actor, pwdUnlocked map[int]bool) (content string, fullyLocked bool, hint string) {
|
||||
caps := s.buildHideViewerCaps(post, viewerID, loadActor, pwdUnlocked)
|
||||
sanitized, fully := markdown.SanitizeForViewer(post.Content, caps)
|
||||
if !fully {
|
||||
return sanitized, false, ""
|
||||
@@ -527,10 +528,9 @@ func (s *PostService) sanitizePostContent(post *model.Post, viewerID uint, loadA
|
||||
switch access {
|
||||
case model.ContentAccessPoints:
|
||||
return sanitized, true, "支付积分后可见隐藏内容"
|
||||
case model.ContentAccessPassword:
|
||||
return sanitized, true, "输入密码后可见隐藏内容"
|
||||
case model.ContentAccessMixed:
|
||||
if post.AccessPoints > 0 {
|
||||
return sanitized, true, "满足条件后可见隐藏内容"
|
||||
}
|
||||
return sanitized, true, "满足条件后可见隐藏内容"
|
||||
case model.ContentAccessReply:
|
||||
return sanitized, true, "回复本帖后可见隐藏内容"
|
||||
@@ -541,8 +541,8 @@ func (s *PostService) sanitizePostContent(post *model.Post, viewerID uint, loadA
|
||||
}
|
||||
}
|
||||
|
||||
func (s *PostService) buildHideViewerCaps(post *model.Post, viewerID uint, loadActor func() *Actor) markdown.ViewerCaps {
|
||||
caps := markdown.ViewerCaps{}
|
||||
func (s *PostService) buildHideViewerCaps(post *model.Post, viewerID uint, loadActor func() *Actor, pwdUnlocked map[int]bool) markdown.ViewerCaps {
|
||||
caps := markdown.ViewerCaps{PasswordUnlocked: pwdUnlocked}
|
||||
if viewerID > 0 && post.UserID == viewerID {
|
||||
caps.Bypass = true
|
||||
caps.LoggedIn = true
|
||||
@@ -625,6 +625,26 @@ func (s *PostService) listAttachmentDTOs(postID, viewerID, authorID uint) ([]Pos
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// UnlockByPassword 校验密码,返回匹配的隐藏块下标(调用方写 cookie)
|
||||
func (s *PostService) UnlockByPassword(postID uint, password string) (matched []int, err error) {
|
||||
var post model.Post
|
||||
if err := s.db.Select("id", "content", "status").First(&post, postID).Error; err != nil {
|
||||
return nil, ErrPostNotFound
|
||||
}
|
||||
password = strings.TrimSpace(password)
|
||||
if password == "" {
|
||||
return nil, errors.New("请输入密码")
|
||||
}
|
||||
hit, err := markdown.MatchPasswordBlocks(post.Content, password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(hit) == 0 {
|
||||
return nil, errors.New("密码错误")
|
||||
}
|
||||
return hit, nil
|
||||
}
|
||||
|
||||
// UnlockContent 积分解锁正文隐藏块(一次支付解锁本帖全部积分块)
|
||||
func (s *PostService) UnlockContent(userID, postID uint) (*PostDetail, error) {
|
||||
var post model.Post
|
||||
|
||||
Reference in New Issue
Block a user