This commit is contained in:
Tomas Dvorak
2025-11-11 10:29:30 +01:00
parent d5b4faea61
commit 8762bde4bf
139 changed files with 7240 additions and 2870 deletions
+63 -3
View File
@@ -103,6 +103,8 @@ func (cc *CommentController) Unreact(c *gin.Context) {
// Admin: list all comments with filters
func (cc *CommentController) AdminList(c *gin.Context) {
// Ensure tables exist (best-effort)
_ = cc.DB.AutoMigrate(&models.Comment{}, &models.CommentReport{}, &models.CommentReaction{})
var items []models.Comment
q := cc.DB.Preload("User").Model(&models.Comment{})
if v := strings.TrimSpace(c.Query("status")); v != "" { q = q.Where("status = ?", v) }
@@ -125,8 +127,21 @@ func (cc *CommentController) AdminList(c *gin.Context) {
_ = cc.DB.Table("comment_reports").Select("comment_id, COUNT(*) as cnt").Where("comment_id IN ?", ids).Group("comment_id").Scan(&rows).Error
for _, r := range rows { repCounts[r.CommentID] = r.Cnt }
}
// Compute admin likes (thumbs_up/like) per comment
adminLiked := map[uint]bool{}
if len(ids) > 0 {
type al struct{ CommentID uint }
var rows []al
_ = cc.DB.Table("comment_reactions as cr").
Select("cr.comment_id").
Joins("JOIN users u ON u.id = cr.user_id").
Where("cr.comment_id IN ? AND u.role = ? AND cr.type IN ?", ids, "admin", []string{"thumbs_up", "like"}).
Group("cr.comment_id").
Scan(&rows).Error
for _, r := range rows { adminLiked[r.CommentID] = true }
}
out := make([]commentOutput, 0, len(items))
for _, r := range items { co := toOutput(r); if v, ok := repCounts[r.ID]; ok { co.Reports = v }; out = append(out, co) }
for _, r := range items { co := toOutput(r); if v, ok := repCounts[r.ID]; ok { co.Reports = v }; if adminLiked[r.ID] { co.AdminLiked = true }; out = append(out, co) }
c.JSON(http.StatusOK, gin.H{"items": out, "total": total, "page": page, "page_size": size})
}
@@ -216,6 +231,7 @@ type commentOutput struct {
SpamScore float32 `json:"spam_score,omitempty"`
SpamRules []string `json:"spam_rules,omitempty"`
Reports int `json:"reports,omitempty"`
AdminLiked bool `json:"admin_liked,omitempty"`
}
type userSlim struct {
@@ -273,14 +289,32 @@ func (cc *CommentController) GetComments(c *gin.Context) {
if page < 1 { page = 1 }
var total int64
q := cc.DB.Model(&models.Comment{}).Where("target_type = ? AND target_id = ? AND status = ?", targetType, targetID, "visible")
// Visibility rules:
// - Public/unauthenticated: only visible
// - Authenticated non-admin: visible + own hidden (awaiting moderation)
// - Admin: all
role, _ := c.Get("userRole")
uid, _ := c.Get("userID")
var where string
var args []interface{}
if role == "admin" {
where = "target_type = ? AND target_id = ?"
args = []interface{}{targetType, targetID}
} else if uid != nil {
where = "target_type = ? AND target_id = ? AND (status = 'visible' OR (status = 'hidden' AND user_id = ?))"
args = []interface{}{targetType, targetID, uid}
} else {
where = "target_type = ? AND target_id = ? AND status = 'visible'"
args = []interface{}{targetType, targetID}
}
q := cc.DB.Model(&models.Comment{}).Where(where, args...)
if err := q.Count(&total).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Database error"})
return
}
var rows []models.Comment
if err := cc.DB.Preload("User").Where("target_type = ? AND target_id = ? AND status = ?", targetType, targetID, "visible").
if err := cc.DB.Preload("User").Where(where, args...).
Order("created_at ASC").
Offset((page-1)*pageSize).Limit(pageSize).
Find(&rows).Error; err != nil {
@@ -316,6 +350,19 @@ func (cc *CommentController) GetComments(c *gin.Context) {
for _, r := range rs { myReactions[r.CommentID] = r.Type }
}
}
// Admin liked map
adminLiked := map[uint]bool{}
if len(ids) > 0 {
type al struct{ CommentID uint }
var rows []al
_ = cc.DB.Table("comment_reactions as cr").
Select("cr.comment_id").
Joins("JOIN users u ON u.id = cr.user_id").
Where("cr.comment_id IN ? AND u.role = ? AND cr.type IN ?", ids, "admin", []string{"thumbs_up", "like"}).
Group("cr.comment_id").
Scan(&rows).Error
for _, r := range rows { adminLiked[r.CommentID] = true }
}
// Preload user profiles for username + avatar (prefer animated when available)
type up struct{ UserID uint; AvatarURL string; AnimatedAvatarURL string; Username string }
profByUser := map[uint]up{}
@@ -337,6 +384,7 @@ func (cc *CommentController) GetComments(c *gin.Context) {
}
if rc, ok := reactionCounts[r.ID]; ok { co.Reactions = rc } else { co.Reactions = map[string]int{} }
if myReactions != nil { if t, ok := myReactions[r.ID]; ok { co.MyReaction = t } }
if adminLiked[r.ID] { co.AdminLiked = true }
out = append(out, co)
}
@@ -381,6 +429,18 @@ func (cc *CommentController) CreateComment(c *gin.Context) {
return
}
if in.ParentID != nil {
var parent models.Comment
if err := cc.DB.First(&parent, *in.ParentID).Error; err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Rodičovský komentář nenalezen"})
return
}
if parent.TargetType != in.TargetType || parent.TargetID != in.TargetID {
c.JSON(http.StatusBadRequest, gin.H{"error": "Rodičovský komentář neodpovídá cíli"})
return
}
}
userIDv, _ := c.Get("userID")
userID := userIDv.(uint)