343 lines
8.1 KiB
Go
343 lines
8.1 KiB
Go
package models
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"code.nonshy.com/nonshy/website/pkg/log"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// Block table.
|
|
type Block struct {
|
|
ID uint64 `gorm:"primaryKey"`
|
|
SourceUserID uint64 `gorm:"index"`
|
|
TargetUserID uint64 `gorm:"index"`
|
|
CreatedAt time.Time
|
|
UpdatedAt time.Time
|
|
}
|
|
|
|
// AddBlock is sourceUserId adding targetUserId to their block list.
|
|
func AddBlock(sourceUserID, targetUserID uint64) error {
|
|
// Unfriend in the process.
|
|
RemoveFriend(sourceUserID, targetUserID)
|
|
|
|
// Did we already block this user?
|
|
var b *Block
|
|
forward := DB.Where(
|
|
"source_user_id = ? AND target_user_id = ?",
|
|
sourceUserID, targetUserID,
|
|
).First(&b).Error
|
|
|
|
// Update existing.
|
|
if forward == nil {
|
|
return nil
|
|
}
|
|
|
|
// Create the block.
|
|
b = &Block{
|
|
SourceUserID: sourceUserID,
|
|
TargetUserID: targetUserID,
|
|
}
|
|
return DB.Create(b).Error
|
|
}
|
|
|
|
// IsBlocking quickly sees if either user blocks the other.
|
|
func IsBlocking(sourceUserID, targetUserID uint64) bool {
|
|
b := &Block{}
|
|
result := DB.Where(
|
|
"(source_user_id = ? AND target_user_id = ?) OR "+
|
|
"(target_user_id = ? AND source_user_id = ?)",
|
|
sourceUserID, targetUserID,
|
|
sourceUserID, targetUserID,
|
|
).First(&b)
|
|
return result.Error == nil
|
|
}
|
|
|
|
// IsBlocked quickly checks if sourceUserID currently blocks targetUserID.
|
|
func IsBlocked(sourceUserID, targetUserID uint64) bool {
|
|
b := &Block{}
|
|
result := DB.Where(
|
|
"source_user_id = ? AND target_user_id = ?",
|
|
sourceUserID, targetUserID,
|
|
).First(&b)
|
|
return result.Error == nil
|
|
}
|
|
|
|
// PaginateBlockList views a user's blocklist.
|
|
func PaginateBlockList(user *User, pager *Pagination) ([]*User, error) {
|
|
// We paginate over the Block table.
|
|
var (
|
|
bs = []*Block{}
|
|
userIDs = []uint64{}
|
|
query *gorm.DB
|
|
)
|
|
|
|
query = DB.Where(
|
|
"source_user_id = ?",
|
|
user.ID,
|
|
)
|
|
|
|
query = query.Order(pager.Sort)
|
|
query.Model(&Block{}).Count(&pager.Total)
|
|
result := query.Offset(pager.GetOffset()).Limit(pager.PerPage).Find(&bs)
|
|
if result.Error != nil {
|
|
return nil, result.Error
|
|
}
|
|
|
|
// Now of these friends get their User objects.
|
|
for _, b := range bs {
|
|
userIDs = append(userIDs, b.TargetUserID)
|
|
}
|
|
|
|
return GetUsers(user, userIDs)
|
|
}
|
|
|
|
// BlockedUserIDs returns all user IDs blocked by the user (bidirectional, source or target user).
|
|
func BlockedUserIDs(user *User) []uint64 {
|
|
// Have we looked this up already on this request?
|
|
if user.cacheBlockedUserIDs != nil {
|
|
return user.cacheBlockedUserIDs
|
|
}
|
|
|
|
var (
|
|
bs = []*Block{}
|
|
userIDs = []uint64{}
|
|
)
|
|
DB.Where("source_user_id = ? OR target_user_id = ?", user.ID, user.ID).Find(&bs)
|
|
for _, row := range bs {
|
|
for _, uid := range []uint64{row.TargetUserID, row.SourceUserID} {
|
|
if uid != user.ID {
|
|
userIDs = append(userIDs, uid)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Cache the result in the User so we don't query it again.
|
|
user.cacheBlockedUserIDs = userIDs
|
|
|
|
return userIDs
|
|
}
|
|
|
|
// MapBlockedUserIDs returns BlockedUserIDs as a lookup hashmap (not for front-end templates currently).
|
|
func MapBlockedUserIDs(user *User) map[uint64]interface{} {
|
|
var (
|
|
result = map[uint64]interface{}{}
|
|
blockedIDs = BlockedUserIDs(user)
|
|
)
|
|
for _, uid := range blockedIDs {
|
|
result[uid] = nil
|
|
}
|
|
return result
|
|
}
|
|
|
|
// FilterBlockingUserIDs narrows down a set of User IDs to remove ones that block (or are blocked by) the current user.
|
|
func FilterBlockingUserIDs(currentUser *User, userIDs []uint64) []uint64 {
|
|
var (
|
|
// Get the IDs to exclude.
|
|
blockedIDs = MapBlockedUserIDs(currentUser)
|
|
|
|
// Filter the result.
|
|
result = []uint64{}
|
|
)
|
|
for _, uid := range userIDs {
|
|
if _, ok := blockedIDs[uid]; ok {
|
|
continue
|
|
}
|
|
result = append(result, uid)
|
|
}
|
|
return result
|
|
}
|
|
|
|
// BlockedUserIDsByUser returns all user IDs blocked by the user (one directional only)
|
|
func BlockedUserIDsByUser(userId uint64) []uint64 {
|
|
var (
|
|
bs = []*Block{}
|
|
userIDs = []uint64{}
|
|
)
|
|
DB.Where("source_user_id = ?", userId).Find(&bs)
|
|
for _, row := range bs {
|
|
for _, uid := range []uint64{row.TargetUserID, row.SourceUserID} {
|
|
if uid != userId {
|
|
userIDs = append(userIDs, uid)
|
|
}
|
|
}
|
|
}
|
|
return userIDs
|
|
}
|
|
|
|
// GetAllBlockedUserIDs returns the forward and reverse lists of blocked user IDs for the user.
|
|
func GetAllBlockedUserIDs(user *User) (forward, reverse []uint64) {
|
|
var (
|
|
bs = []*Block{}
|
|
)
|
|
DB.Where("source_user_id = ? OR target_user_id = ?", user.ID, user.ID).Find(&bs)
|
|
for _, row := range bs {
|
|
if row.SourceUserID == user.ID {
|
|
forward = append(forward, row.TargetUserID)
|
|
} else if row.TargetUserID == user.ID {
|
|
reverse = append(reverse, row.SourceUserID)
|
|
}
|
|
}
|
|
return forward, reverse
|
|
}
|
|
|
|
// BulkRestoreBlockedUserIDs inserts many blocked user IDs in one query.
|
|
//
|
|
// Returns the count of blocks added.
|
|
func BulkRestoreBlockedUserIDs(user *User, forward, reverse []uint64) (int, error) {
|
|
var bs = []*Block{}
|
|
|
|
// Forward list.
|
|
for _, uid := range forward {
|
|
bs = append(bs, &Block{
|
|
SourceUserID: user.ID,
|
|
TargetUserID: uid,
|
|
})
|
|
}
|
|
|
|
// Reverse list.
|
|
for _, uid := range reverse {
|
|
bs = append(bs, &Block{
|
|
SourceUserID: uid,
|
|
TargetUserID: user.ID,
|
|
})
|
|
}
|
|
|
|
// Anything to do?
|
|
if len(bs) == 0 {
|
|
return 0, nil
|
|
}
|
|
|
|
// Batch create.
|
|
res := DB.Create(bs)
|
|
return len(bs), res.Error
|
|
}
|
|
|
|
// BlockedUsernames returns all usernames blocked by (or blocking) the user.
|
|
func BlockedUsernames(user *User) []string {
|
|
var (
|
|
userIDs = BlockedUserIDs(user)
|
|
usernames = []string{}
|
|
)
|
|
|
|
if len(userIDs) == 0 {
|
|
return usernames
|
|
}
|
|
|
|
if res := DB.Table(
|
|
"users",
|
|
).Select(
|
|
"username",
|
|
).Where(
|
|
"id IN ?", userIDs,
|
|
).Scan(&usernames); res.Error != nil {
|
|
log.Error("BlockedUsernames(%s): %s", user.Username, res.Error)
|
|
}
|
|
|
|
return usernames
|
|
}
|
|
|
|
// GetBlocklistInsights returns detailed block lists (both directions) about a user, for admin insight.
|
|
func GetBlocklistInsights(user *User) (*BlocklistInsight, error) {
|
|
// Collect ALL user IDs (both directions) of this user's blocklist.
|
|
var (
|
|
bs = []*Block{}
|
|
forward = []*Block{} // Users they block
|
|
reverse = []*Block{} // Users who block the target
|
|
userIDs = []uint64{user.ID}
|
|
usernames = map[uint64]string{}
|
|
admins = map[uint64]bool{}
|
|
)
|
|
|
|
// Get the complete blocklist and bucket them into forward and reverse.
|
|
DB.Where("source_user_id = ? OR target_user_id = ?", user.ID, user.ID).Order("created_at desc").Find(&bs)
|
|
for _, row := range bs {
|
|
if row.SourceUserID == user.ID {
|
|
forward = append(forward, row)
|
|
userIDs = append(userIDs, row.TargetUserID)
|
|
} else {
|
|
reverse = append(reverse, row)
|
|
userIDs = append(userIDs, row.SourceUserID)
|
|
}
|
|
}
|
|
|
|
// Map all the user IDs to user names.
|
|
if len(userIDs) > 0 {
|
|
type scanItem struct {
|
|
ID uint64
|
|
Username string
|
|
IsAdmin bool
|
|
}
|
|
var scan = []scanItem{}
|
|
if res := DB.Table(
|
|
"users",
|
|
).Select(
|
|
"id",
|
|
"username",
|
|
"is_admin",
|
|
).Where(
|
|
"id IN ?", userIDs,
|
|
).Scan(&scan); res.Error != nil {
|
|
return nil, fmt.Errorf("GetBlocklistInsights(%s): mapping user IDs to names: %s", user.Username, res.Error)
|
|
}
|
|
|
|
for _, row := range scan {
|
|
usernames[row.ID] = row.Username
|
|
admins[row.ID] = row.IsAdmin
|
|
}
|
|
}
|
|
|
|
// Assemble the final result.
|
|
var result = &BlocklistInsight{
|
|
Blocks: []BlocklistInsightUser{},
|
|
BlockedBy: []BlocklistInsightUser{},
|
|
}
|
|
for _, row := range forward {
|
|
if username, ok := usernames[row.TargetUserID]; ok {
|
|
result.Blocks = append(result.Blocks, BlocklistInsightUser{
|
|
Username: username,
|
|
IsAdmin: admins[row.TargetUserID],
|
|
Date: row.CreatedAt,
|
|
})
|
|
}
|
|
}
|
|
for _, row := range reverse {
|
|
if username, ok := usernames[row.SourceUserID]; ok {
|
|
result.BlockedBy = append(result.BlockedBy, BlocklistInsightUser{
|
|
Username: username,
|
|
IsAdmin: admins[row.SourceUserID],
|
|
Date: row.CreatedAt,
|
|
})
|
|
}
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
type BlocklistInsight struct {
|
|
Blocks []BlocklistInsightUser
|
|
BlockedBy []BlocklistInsightUser
|
|
}
|
|
|
|
type BlocklistInsightUser struct {
|
|
Username string
|
|
IsAdmin bool
|
|
Date time.Time
|
|
}
|
|
|
|
// UnblockUser removes targetUserID from your blocklist.
|
|
func UnblockUser(sourceUserID, targetUserID uint64) error {
|
|
result := DB.Where(
|
|
"source_user_id = ? AND target_user_id = ?",
|
|
sourceUserID, targetUserID,
|
|
).Delete(&Block{})
|
|
return result.Error
|
|
}
|
|
|
|
// Save photo.
|
|
func (b *Block) Save() error {
|
|
result := DB.Save(b)
|
|
return result.Error
|
|
}
|