This commit is contained in:
Tomas Dvorak
2025-11-02 01:04:02 +01:00
parent ac886502e0
commit b9cea0cd77
153 changed files with 43713 additions and 1700 deletions
+22
View File
@@ -0,0 +1,22 @@
package models
import (
"time"
)
type Comment struct {
BaseModel
TargetType string `json:"target_type" gorm:"size:30;index:idx_target"`
TargetID string `json:"target_id" gorm:"size:128;index:idx_target"`
UserID uint `json:"user_id" gorm:"index"`
User User `json:"user" gorm:"foreignKey:UserID"`
ParentID *uint `json:"parent_id,omitempty" gorm:"index"`
Content string `json:"content" gorm:"type:text;not null"`
Status string `json:"status" gorm:"size:20;default:'visible';index"`
SpamScore float32 `json:"spam_score" gorm:"type:real;default:0"`
SpamRules string `json:"spam_rules" gorm:"type:text"`
IsEdited bool `json:"is_edited" gorm:"default:false"`
EditedAt *time.Time `json:"edited_at"`
}
func (Comment) TableName() string { return "comments" }
+24
View File
@@ -0,0 +1,24 @@
package models
import "time"
type CommentBan struct {
BaseModel
UserID uint `json:"user_id" gorm:"index;not null"`
Reason string `json:"reason" gorm:"type:text"`
Until *time.Time `json:"until" gorm:"index"` // nil = permanent
CreatedByID uint `json:"created_by_id" gorm:"index"`
}
func (CommentBan) TableName() string { return "comment_bans" }
type UnbanRequest struct {
BaseModel
UserID uint `json:"user_id" gorm:"index;not null"`
Message string `json:"message" gorm:"type:text"`
Status string `json:"status" gorm:"size:20;default:'pending';index"` // pending|approved|rejected
ResolvedByID *uint `json:"resolved_by_id" gorm:"index"`
ResolvedAt *time.Time `json:"resolved_at"`
}
func (UnbanRequest) TableName() string { return "unban_requests" }
+10
View File
@@ -0,0 +1,10 @@
package models
type CommentReaction struct {
BaseModel
CommentID uint `json:"comment_id" gorm:"index;not null"`
UserID uint `json:"user_id" gorm:"index;not null"`
Type string `json:"type" gorm:"size:24;not null;index"` // like|heart|smile|laugh|thumbs_up|thumbs_down|sad|angry
}
func (CommentReaction) TableName() string { return "comment_reactions" }
+10
View File
@@ -0,0 +1,10 @@
package models
type CommentReport struct {
BaseModel
CommentID uint `json:"comment_id" gorm:"index;not null"`
UserID uint `json:"user_id" gorm:"index;not null"`
Reason string `json:"reason" gorm:"size:255"`
}
func (CommentReport) TableName() string { return "comment_reports" }
+68
View File
@@ -0,0 +1,68 @@
package models
import "gorm.io/datatypes"
// PointsTransaction logs changes in points/xp
// Reason examples: comment_create, poll_vote, newsletter_subscribe, redeem, admin_adjust
// Meta can hold ids like {"comment_id":123}
type PointsTransaction struct {
BaseModel
UserID uint `json:"user_id" gorm:"index;not null"`
Delta int64 `json:"delta"`
XPDelta int64 `json:"xp_delta"`
Reason string `json:"reason" gorm:"size:64;index"`
Meta datatypes.JSONMap `json:"meta" gorm:"type:jsonb"`
}
func (PointsTransaction) TableName() string { return "points_transactions" }
// Achievement definition managed in DB for flexibility
// Condition is a code string handled by service (e.g., first_comment, first_vote, votes_10, comments_25)
type Achievement struct {
BaseModel
Code string `json:"code" gorm:"size:64;uniqueIndex"`
Title string `json:"title"`
Description string `json:"description"`
Points int64 `json:"points"`
XP int64 `json:"xp"`
Icon string `json:"icon"`
Active bool `json:"active" gorm:"default:true"`
}
func (Achievement) TableName() string { return "achievements" }
type UserAchievement struct {
BaseModel
UserID uint `json:"user_id" gorm:"index;not null"`
AchievementID uint `json:"achievement_id" gorm:"index;not null"`
}
func (UserAchievement) TableName() string { return "user_achievements" }
// Reward items configured by admin (e.g., avatars, merch vouchers)
type RewardItem struct {
BaseModel
Name string `json:"name"`
Type string `json:"type" gorm:"size:32;index"` // avatar_static, avatar_animated, merch_coupon, custom
CostPoints int64 `json:"cost_points"`
ImageURL string `json:"image_url"`
Stock int `json:"stock"`
Active bool `json:"active" gorm:"default:true"`
Metadata datatypes.JSONMap `json:"metadata" gorm:"type:jsonb"`
}
func (RewardItem) TableName() string { return "reward_items" }
// Redemption log for rewards
type RewardRedemption struct {
BaseModel
UserID uint `json:"user_id" gorm:"index;not null"`
RewardID uint `json:"reward_id" gorm:"index;not null"`
Status string `json:"status" gorm:"size:24;default:'pending';index"`
}
func (RewardRedemption) TableName() string { return "reward_redemptions" }
+10 -2
View File
@@ -60,6 +60,11 @@ type Article struct {
// Match link (loaded separately, not stored in this table)
// Removed omitempty to always include in JSON (even if null)
MatchLink *ArticleMatchLink `gorm:"-" json:"match_link"`
// Computed helpers (not persisted)
CategorySlug string `gorm:"-" json:"category_slug,omitempty"`
CompetitionAlias string `gorm:"-" json:"competition_alias,omitempty"`
NormalizedCategory string `gorm:"-" json:"normalized_category,omitempty"`
URL string `gorm:"-" json:"url,omitempty"`
}
// ArticleTeamLink represents a link from an article to a team identified by an external FACR ID
@@ -253,11 +258,14 @@ type Settings struct {
LocationLatitude float64 `json:"location_latitude"`
LocationLongitude float64 `json:"location_longitude"`
MapZoomLevel int `gorm:"default:15" json:"map_zoom_level"`
MapStyle string `json:"map_style"` // OpenStreetMap style URL or preset: default, dark, satellite
MapStyle string `json:"map_style"`
ShowMapOnHomepage bool `json:"show_map_on_homepage"`
// Homepage matches display configuration
FinishedMatchDisplayDays int `gorm:"default:2" json:"finished_match_display_days"` // Number of days to show finished matches with scores on homepage
FinishedMatchDisplayDays int `gorm:"default:2" json:"finished_match_display_days"`
StorageQuotaMB int `json:"storage_quota_mb"`
StorageWarnThreshold int `json:"storage_warn_threshold"`
StorageCriticalThreshold int `json:"storage_critical_threshold"`
}
// TableName specifies table name for Settings model
+13
View File
@@ -0,0 +1,13 @@
package models
type UserProfile struct {
BaseModel
UserID uint `json:"user_id" gorm:"uniqueIndex;not null"`
Points int64 `json:"points" gorm:"default:0;index"`
Level int `json:"level" gorm:"default:1"`
XP int64 `json:"xp" gorm:"default:0"`
AvatarURL string `json:"avatar_url" gorm:"type:varchar(500)"`
AnimatedAvatarURL string `json:"animated_avatar_url" gorm:"type:varchar(500)"`
}
func (UserProfile) TableName() string { return "user_profiles" }