feat(config): add sensitive word filtering system with database support
Implement sensitive word filtering feature with configurable database and Redis support. Add security enhancements including WebSocket origin validation, improved password strength requirements, timing attack prevention, and production JWT secret validation. Add batch operation validation limits and optimize user blocking queries with subqueries. BREAKING CHANGE: Password validation now requires minimum 8 characters with uppercase, lowercase, and digit (was 6-50 characters without complexity requirements)
This commit is contained in:
@@ -508,7 +508,7 @@ func (r *commentRepository) GetCommentsByCursor(ctx context.Context, postID stri
|
||||
|
||||
if builder.Error() != nil {
|
||||
// 无效游标,返回空列表
|
||||
return cursor.NewCursorPageResult[*model.Comment]([]*model.Comment{}, "", "", false), nil
|
||||
return cursor.NewCursorPageResult([]*model.Comment{}, "", "", false), nil
|
||||
}
|
||||
|
||||
// 执行查询
|
||||
@@ -629,7 +629,7 @@ func (r *commentRepository) GetRepliesByCursor(ctx context.Context, rootID strin
|
||||
|
||||
if builder.Error() != nil {
|
||||
// 无效游标,返回空列表
|
||||
return cursor.NewCursorPageResult[*model.Comment]([]*model.Comment{}, "", "", false), nil
|
||||
return cursor.NewCursorPageResult([]*model.Comment{}, "", "", false), nil
|
||||
}
|
||||
|
||||
// 执行查询
|
||||
|
||||
@@ -492,7 +492,7 @@ func (r *groupRepository) GetGroupsByCursor(ctx context.Context, req *cursor.Pag
|
||||
|
||||
if builder.Error() != nil {
|
||||
// 无效游标,返回空列表
|
||||
return cursor.NewCursorPageResult[*model.Group]([]*model.Group{}, "", "", false), nil
|
||||
return cursor.NewCursorPageResult([]*model.Group{}, "", "", false), nil
|
||||
}
|
||||
|
||||
// 执行查询
|
||||
@@ -551,7 +551,7 @@ func (r *groupRepository) GetUserGroupsByCursor(ctx context.Context, userID stri
|
||||
|
||||
if builder.Error() != nil {
|
||||
// 无效游标,返回空列表
|
||||
return cursor.NewCursorPageResult[*model.Group]([]*model.Group{}, "", "", false), nil
|
||||
return cursor.NewCursorPageResult([]*model.Group{}, "", "", false), nil
|
||||
}
|
||||
|
||||
// 执行查询
|
||||
@@ -605,7 +605,7 @@ func (r *groupRepository) GetMembersByCursor(ctx context.Context, groupID string
|
||||
|
||||
if builder.Error() != nil {
|
||||
// 无效游标,返回空列表
|
||||
return cursor.NewCursorPageResult[*model.GroupMember]([]*model.GroupMember{}, "", "", false), nil
|
||||
return cursor.NewCursorPageResult([]*model.GroupMember{}, "", "", false), nil
|
||||
}
|
||||
|
||||
// 执行查询
|
||||
@@ -660,7 +660,7 @@ func (r *groupRepository) GetAnnouncementsByCursor(ctx context.Context, groupID
|
||||
|
||||
if builder.Error() != nil {
|
||||
// 无效游标,返回空列表
|
||||
return cursor.NewCursorPageResult[*model.GroupAnnouncement]([]*model.GroupAnnouncement{}, "", "", false), nil
|
||||
return cursor.NewCursorPageResult([]*model.GroupAnnouncement{}, "", "", false), nil
|
||||
}
|
||||
|
||||
// 执行查询(置顶的排在前面)
|
||||
|
||||
@@ -747,7 +747,7 @@ func (r *messageRepository) GetMessagesByCursor(ctx context.Context, conversatio
|
||||
|
||||
if builder.Error() != nil {
|
||||
// 无效游标,返回空列表
|
||||
return cursor.NewCursorPageResult[*model.Message]([]*model.Message{}, "", "", false), nil
|
||||
return cursor.NewCursorPageResult([]*model.Message{}, "", "", false), nil
|
||||
}
|
||||
|
||||
// 执行查询
|
||||
@@ -810,7 +810,7 @@ func (r *messageRepository) GetConversationsByCursor(ctx context.Context, userID
|
||||
|
||||
if builder.Error() != nil {
|
||||
// 无效游标,返回空列表
|
||||
return cursor.NewCursorPageResult[*model.Conversation]([]*model.Conversation{}, "", "", false), nil
|
||||
return cursor.NewCursorPageResult([]*model.Conversation{}, "", "", false), nil
|
||||
}
|
||||
|
||||
// 执行查询 - 需要添加置顶排序
|
||||
|
||||
@@ -111,7 +111,7 @@ func (r *notificationRepository) GetNotificationsByCursor(ctx context.Context, u
|
||||
|
||||
if builder.Error() != nil {
|
||||
// 无效游标,返回空列表
|
||||
return cursor.NewCursorPageResult[*model.Notification]([]*model.Notification{}, "", "", false), nil
|
||||
return cursor.NewCursorPageResult([]*model.Notification{}, "", "", false), nil
|
||||
}
|
||||
|
||||
// 执行查询
|
||||
|
||||
@@ -127,9 +127,10 @@ func (r *operationLogRepository) GetStatistics(ctx context.Context, startTime, e
|
||||
// 汇总结果
|
||||
for _, r := range results {
|
||||
stats.TotalCount += r.Count
|
||||
if r.Status == "success" {
|
||||
switch r.Status {
|
||||
case "success":
|
||||
stats.SuccessCount = r.Count
|
||||
} else if r.Status == "fail" {
|
||||
case "fail":
|
||||
stats.FailCount = r.Count
|
||||
}
|
||||
}
|
||||
|
||||
@@ -996,7 +996,7 @@ func (r *postRepository) GetPostsByCursor(ctx context.Context, userID string, in
|
||||
|
||||
if builder.Error() != nil {
|
||||
// 无效游标,返回空列表
|
||||
return cursor.NewCursorPageResult[*model.Post]([]*model.Post{}, "", "", false), nil
|
||||
return cursor.NewCursorPageResult([]*model.Post{}, "", "", false), nil
|
||||
}
|
||||
|
||||
// 执行查询
|
||||
@@ -1064,7 +1064,7 @@ func (r *postRepository) SearchPostsByCursor(ctx context.Context, keyword string
|
||||
|
||||
if builder.Error() != nil {
|
||||
// 无效游标,返回空列表
|
||||
return cursor.NewCursorPageResult[*model.Post]([]*model.Post{}, "", "", false), nil
|
||||
return cursor.NewCursorPageResult([]*model.Post{}, "", "", false), nil
|
||||
}
|
||||
|
||||
// 执行查询
|
||||
@@ -1124,7 +1124,7 @@ func (r *postRepository) GetUserPostsByCursor(ctx context.Context, userID string
|
||||
|
||||
if builder.Error() != nil {
|
||||
// 无效游标,返回空列表
|
||||
return cursor.NewCursorPageResult[*model.Post]([]*model.Post{}, "", "", false), nil
|
||||
return cursor.NewCursorPageResult([]*model.Post{}, "", "", false), nil
|
||||
}
|
||||
|
||||
// 执行查询
|
||||
@@ -1180,7 +1180,7 @@ func (r *postRepository) GetFollowingPostsByCursor(ctx context.Context, userID s
|
||||
|
||||
if builder.Error() != nil {
|
||||
// 无效游标,返回空列表
|
||||
return cursor.NewCursorPageResult[*model.Post]([]*model.Post{}, "", "", false), nil
|
||||
return cursor.NewCursorPageResult([]*model.Post{}, "", "", false), nil
|
||||
}
|
||||
|
||||
// 执行查询
|
||||
@@ -1234,7 +1234,7 @@ func (r *postRepository) GetHotPostsByCursor(ctx context.Context, req *cursor.Pa
|
||||
|
||||
if builder.Error() != nil {
|
||||
// 无效游标,返回空列表
|
||||
return cursor.NewCursorPageResult[*model.Post]([]*model.Post{}, "", "", false), nil
|
||||
return cursor.NewCursorPageResult([]*model.Post{}, "", "", false), nil
|
||||
}
|
||||
|
||||
// 执行查询
|
||||
|
||||
66
internal/repository/sensitive_word_repo.go
Normal file
66
internal/repository/sensitive_word_repo.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"carrot_bbs/internal/model"
|
||||
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
type SensitiveWordRepository interface {
|
||||
Create(ctx context.Context, word *model.SensitiveWord) error
|
||||
GetByWord(ctx context.Context, word string) (*model.SensitiveWord, error)
|
||||
Update(ctx context.Context, word *model.SensitiveWord) error
|
||||
Upsert(ctx context.Context, word *model.SensitiveWord) error
|
||||
DeactivateByWord(ctx context.Context, word string) error
|
||||
ListActive(ctx context.Context) ([]model.SensitiveWord, error)
|
||||
}
|
||||
|
||||
type sensitiveWordRepository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
func NewSensitiveWordRepository(db *gorm.DB) SensitiveWordRepository {
|
||||
return &sensitiveWordRepository{db: db}
|
||||
}
|
||||
|
||||
func (r *sensitiveWordRepository) Create(ctx context.Context, word *model.SensitiveWord) error {
|
||||
return r.db.WithContext(ctx).Create(word).Error
|
||||
}
|
||||
|
||||
func (r *sensitiveWordRepository) GetByWord(ctx context.Context, word string) (*model.SensitiveWord, error) {
|
||||
var sw model.SensitiveWord
|
||||
err := r.db.WithContext(ctx).Where("word = ?", word).First(&sw).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &sw, nil
|
||||
}
|
||||
|
||||
func (r *sensitiveWordRepository) Update(ctx context.Context, word *model.SensitiveWord) error {
|
||||
return r.db.WithContext(ctx).Save(word).Error
|
||||
}
|
||||
|
||||
func (r *sensitiveWordRepository) Upsert(ctx context.Context, word *model.SensitiveWord) error {
|
||||
return r.db.WithContext(ctx).Clauses(clause.OnConflict{
|
||||
Columns: []clause.Column{{Name: "word"}},
|
||||
DoUpdates: clause.AssignmentColumns([]string{
|
||||
"category", "level", "is_active", "updated_by", "remark", "updated_at",
|
||||
}),
|
||||
}).Create(word).Error
|
||||
}
|
||||
|
||||
func (r *sensitiveWordRepository) DeactivateByWord(ctx context.Context, word string) error {
|
||||
return r.db.WithContext(ctx).
|
||||
Model(&model.SensitiveWord{}).
|
||||
Where("word = ?", word).
|
||||
Update("is_active", false).Error
|
||||
}
|
||||
|
||||
func (r *sensitiveWordRepository) ListActive(ctx context.Context) ([]model.SensitiveWord, error) {
|
||||
var words []model.SensitiveWord
|
||||
err := r.db.WithContext(ctx).Where("is_active = ?", true).Find(&words).Error
|
||||
return words, err
|
||||
}
|
||||
@@ -377,21 +377,15 @@ func (r *userRepository) BlockUserAndCleanupRelations(blockerID, blockedID strin
|
||||
}
|
||||
|
||||
for _, uid := range []string{blockerID, blockedID} {
|
||||
var followersCount int64
|
||||
if err := tx.Model(&model.Follow{}).Where("following_id = ?", uid).Count(&followersCount).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Model(&model.User{}).Where("id = ?", uid).
|
||||
UpdateColumn("followers_count", followersCount).Error; err != nil {
|
||||
UpdateColumn("followers_count", tx.Model(&model.Follow{}).
|
||||
Select("COUNT(*)").Where("following_id = ?", uid)).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var followingCount int64
|
||||
if err := tx.Model(&model.Follow{}).Where("follower_id = ?", uid).Count(&followingCount).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Model(&model.User{}).Where("id = ?", uid).
|
||||
UpdateColumn("following_count", followingCount).Error; err != nil {
|
||||
UpdateColumn("following_count", tx.Model(&model.Follow{}).
|
||||
Select("COUNT(*)").Where("follower_id = ?", uid)).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user