This commit is contained in:
Tomas Dvorak
2025-11-02 21:31:00 +01:00
parent b9cea0cd77
commit 087f30e82c
130 changed files with 20104 additions and 34330 deletions
@@ -0,0 +1,7 @@
-- Drop engagement system tables in reverse order
DROP TABLE IF EXISTS reward_redemptions;
DROP TABLE IF EXISTS reward_items;
DROP TABLE IF EXISTS user_achievements;
DROP TABLE IF EXISTS achievements;
DROP TABLE IF EXISTS points_transactions;
DROP TABLE IF EXISTS user_profiles;
@@ -0,0 +1,122 @@
-- Create user profiles table for gamification
CREATE TABLE IF NOT EXISTS user_profiles (
id BIGSERIAL PRIMARY KEY,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
user_id BIGINT NOT NULL UNIQUE,
points BIGINT NOT NULL DEFAULT 0,
level INTEGER NOT NULL DEFAULT 1,
xp BIGINT NOT NULL DEFAULT 0,
username VARCHAR(32) UNIQUE NOT NULL,
avatar_url VARCHAR(500),
animated_avatar_url VARCHAR(500),
avatar_upload_unlocked BOOLEAN NOT NULL DEFAULT FALSE,
CONSTRAINT fk_user_profiles_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
CREATE INDEX idx_user_profiles_user_id ON user_profiles(user_id);
CREATE INDEX idx_user_profiles_points ON user_profiles(points DESC);
CREATE INDEX idx_user_profiles_level ON user_profiles(level DESC);
CREATE INDEX idx_user_profiles_xp ON user_profiles(xp DESC);
CREATE INDEX idx_user_profiles_username ON user_profiles(LOWER(username));
-- Create points transactions log
CREATE TABLE IF NOT EXISTS points_transactions (
id BIGSERIAL PRIMARY KEY,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
user_id BIGINT NOT NULL,
delta BIGINT NOT NULL,
xp_delta BIGINT NOT NULL DEFAULT 0,
reason VARCHAR(64) NOT NULL,
meta JSONB,
CONSTRAINT fk_points_tx_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
CREATE INDEX idx_points_tx_user ON points_transactions(user_id, created_at DESC);
CREATE INDEX idx_points_tx_reason ON points_transactions(reason);
CREATE INDEX idx_points_tx_created ON points_transactions(created_at DESC);
-- Create achievements table
CREATE TABLE IF NOT EXISTS achievements (
id BIGSERIAL PRIMARY KEY,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
code VARCHAR(64) UNIQUE NOT NULL,
title VARCHAR(255) NOT NULL,
description TEXT,
points BIGINT NOT NULL DEFAULT 0,
xp BIGINT NOT NULL DEFAULT 0,
icon VARCHAR(255),
active BOOLEAN NOT NULL DEFAULT TRUE
);
CREATE INDEX idx_achievements_code ON achievements(code);
CREATE INDEX idx_achievements_active ON achievements(active);
-- Create user achievements junction table
CREATE TABLE IF NOT EXISTS user_achievements (
id BIGSERIAL PRIMARY KEY,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
user_id BIGINT NOT NULL,
achievement_id BIGINT NOT NULL,
CONSTRAINT fk_user_ach_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
CONSTRAINT fk_user_ach_achievement FOREIGN KEY (achievement_id) REFERENCES achievements(id) ON DELETE CASCADE,
UNIQUE (user_id, achievement_id)
);
CREATE INDEX idx_user_ach_user ON user_achievements(user_id);
CREATE INDEX idx_user_ach_achievement ON user_achievements(achievement_id);
-- Create reward items table
CREATE TABLE IF NOT EXISTS reward_items (
id BIGSERIAL PRIMARY KEY,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
name VARCHAR(255) NOT NULL,
type VARCHAR(32) NOT NULL,
cost_points BIGINT NOT NULL,
image_url VARCHAR(500),
stock INTEGER NOT NULL DEFAULT 0,
active BOOLEAN NOT NULL DEFAULT TRUE,
metadata JSONB
);
CREATE INDEX idx_reward_items_type ON reward_items(type);
CREATE INDEX idx_reward_items_active ON reward_items(active);
CREATE INDEX idx_reward_items_cost ON reward_items(cost_points);
-- Create reward redemptions table
CREATE TABLE IF NOT EXISTS reward_redemptions (
id BIGSERIAL PRIMARY KEY,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
user_id BIGINT NOT NULL,
reward_id BIGINT NOT NULL,
status VARCHAR(24) NOT NULL DEFAULT 'pending',
CONSTRAINT fk_reward_red_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
CONSTRAINT fk_reward_red_reward FOREIGN KEY (reward_id) REFERENCES reward_items(id) ON DELETE CASCADE
);
CREATE INDEX idx_reward_red_user ON reward_redemptions(user_id);
CREATE INDEX idx_reward_red_reward ON reward_redemptions(reward_id);
CREATE INDEX idx_reward_red_status ON reward_redemptions(status);
CREATE INDEX idx_reward_red_created ON reward_redemptions(created_at DESC);
-- Insert default achievements
INSERT INTO achievements (code, title, description, points, xp, active) VALUES
('first_comment', 'První komentář', 'Napsal/a jste první komentář.', 10, 10, TRUE),
('first_vote', 'První hlasování', 'Poprvé jste hlasoval/a v anketě.', 8, 8, TRUE),
('newsletter_sub', 'Odběr novinek', 'Přihlášení k odběru newsletteru.', 12, 12, TRUE),
('comments_10', 'Komentátor', '10 komentářů!', 20, 20, TRUE),
('votes_10', 'Hlasující', '10 hlasování!', 20, 20, TRUE),
('comments_50', 'Aktivní člen', '50 komentářů!', 50, 50, TRUE),
('votes_50', 'Věrný fanoušek', '50 hlasování!', 50, 50, TRUE),
('comments_100', 'Veterán diskuzí', '100 komentářů!', 100, 100, TRUE)
ON CONFLICT (code) DO NOTHING;
-- Create default avatar upload unlock reward
INSERT INTO reward_items (name, type, cost_points, stock, active) VALUES
('Odemknout vlastní avatar (upload)', 'avatar_upload_unlock', 100, -1, TRUE)
ON CONFLICT DO NOTHING;
@@ -0,0 +1,6 @@
-- Drop comments system tables in reverse order
DROP TABLE IF EXISTS comment_reactions;
DROP TABLE IF EXISTS comment_reports;
DROP TABLE IF EXISTS unban_requests;
DROP TABLE IF EXISTS comment_bans;
DROP TABLE IF EXISTS comments;
@@ -0,0 +1,93 @@
-- Create comments table
CREATE TABLE IF NOT EXISTS comments (
id BIGSERIAL PRIMARY KEY,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
target_type VARCHAR(30) NOT NULL,
target_id VARCHAR(128) NOT NULL,
user_id BIGINT NOT NULL,
parent_id BIGINT,
content TEXT NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'visible',
spam_score REAL NOT NULL DEFAULT 0,
spam_rules TEXT,
is_edited BOOLEAN NOT NULL DEFAULT FALSE,
edited_at TIMESTAMP WITH TIME ZONE,
CONSTRAINT fk_comments_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
CONSTRAINT fk_comments_parent FOREIGN KEY (parent_id) REFERENCES comments(id) ON DELETE CASCADE
);
CREATE INDEX idx_comments_target ON comments(target_type, target_id);
CREATE INDEX idx_comments_user ON comments(user_id);
CREATE INDEX idx_comments_parent ON comments(parent_id);
CREATE INDEX idx_comments_status ON comments(status);
CREATE INDEX idx_comments_created ON comments(created_at DESC);
CREATE INDEX idx_comments_spam ON comments(spam_score DESC) WHERE spam_score > 0.5;
-- Create comment bans table
CREATE TABLE IF NOT EXISTS comment_bans (
id BIGSERIAL PRIMARY KEY,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
user_id BIGINT NOT NULL,
reason TEXT,
until TIMESTAMP WITH TIME ZONE,
created_by_id BIGINT NOT NULL,
CONSTRAINT fk_bans_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
CONSTRAINT fk_bans_creator FOREIGN KEY (created_by_id) REFERENCES users(id) ON DELETE CASCADE
);
CREATE INDEX idx_comment_bans_user ON comment_bans(user_id);
CREATE INDEX idx_comment_bans_until ON comment_bans(until);
CREATE INDEX idx_comment_bans_creator ON comment_bans(created_by_id);
-- Create unban requests table
CREATE TABLE IF NOT EXISTS unban_requests (
id BIGSERIAL PRIMARY KEY,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
user_id BIGINT NOT NULL,
message TEXT,
status VARCHAR(20) NOT NULL DEFAULT 'pending',
resolved_by_id BIGINT,
resolved_at TIMESTAMP WITH TIME ZONE,
CONSTRAINT fk_unban_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
CONSTRAINT fk_unban_resolver FOREIGN KEY (resolved_by_id) REFERENCES users(id) ON DELETE SET NULL
);
CREATE INDEX idx_unban_user ON unban_requests(user_id);
CREATE INDEX idx_unban_status ON unban_requests(status);
CREATE INDEX idx_unban_created ON unban_requests(created_at DESC);
-- Create comment reports table
CREATE TABLE IF NOT EXISTS comment_reports (
id BIGSERIAL PRIMARY KEY,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
comment_id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
reason VARCHAR(255),
CONSTRAINT fk_reports_comment FOREIGN KEY (comment_id) REFERENCES comments(id) ON DELETE CASCADE,
CONSTRAINT fk_reports_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
UNIQUE (comment_id, user_id)
);
CREATE INDEX idx_comment_reports_comment ON comment_reports(comment_id);
CREATE INDEX idx_comment_reports_user ON comment_reports(user_id);
-- Create comment reactions table
CREATE TABLE IF NOT EXISTS comment_reactions (
id BIGSERIAL PRIMARY KEY,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
comment_id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
type VARCHAR(24) NOT NULL,
CONSTRAINT fk_reactions_comment FOREIGN KEY (comment_id) REFERENCES comments(id) ON DELETE CASCADE,
CONSTRAINT fk_reactions_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
UNIQUE (comment_id, user_id)
);
CREATE INDEX idx_comment_reactions_comment ON comment_reactions(comment_id);
CREATE INDEX idx_comment_reactions_user ON comment_reactions(user_id);
CREATE INDEX idx_comment_reactions_type ON comment_reactions(type);
@@ -0,0 +1,5 @@
-- Drop sweepstakes system tables (reverse)
DROP TABLE IF EXISTS sweepstake_winners;
DROP TABLE IF EXISTS sweepstake_entries;
DROP TABLE IF EXISTS sweepstake_prizes;
DROP TABLE IF EXISTS sweepstakes;
@@ -0,0 +1,85 @@
-- Sweepstakes/Lottery system
-- Tables: sweepstakes, sweepstake_prizes, sweepstake_entries, sweepstake_winners
-- Main sweepstakes table
CREATE TABLE IF NOT EXISTS sweepstakes (
id BIGSERIAL PRIMARY KEY,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
title VARCHAR(255) NOT NULL,
description TEXT,
image_url VARCHAR(500),
rules_url VARCHAR(500),
start_at TIMESTAMPTZ NOT NULL,
end_at TIMESTAMPTZ NOT NULL,
status VARCHAR(16) NOT NULL DEFAULT 'draft', -- draft|scheduled|active|locked|finalized|archived
picker_style VARCHAR(16) NOT NULL DEFAULT 'wheel', -- wheel|cycler
total_prizes INTEGER NOT NULL DEFAULT 1,
prize_summary TEXT,
winners_selected_at TIMESTAMPTZ,
visibility_until TIMESTAMPTZ, -- set to end_at + 3 days at finalize
draw_seed VARCHAR(64),
max_entries_per_user INTEGER NOT NULL DEFAULT 1
);
CREATE INDEX IF NOT EXISTS idx_sweepstakes_status ON sweepstakes(status);
CREATE INDEX IF NOT EXISTS idx_sweepstakes_start ON sweepstakes(start_at);
CREATE INDEX IF NOT EXISTS idx_sweepstakes_end ON sweepstakes(end_at);
-- Prizes (per sweepstake)
CREATE TABLE IF NOT EXISTS sweepstake_prizes (
id BIGSERIAL PRIMARY KEY,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
sweepstake_id BIGINT NOT NULL REFERENCES sweepstakes(id) ON DELETE CASCADE,
name VARCHAR(255) NOT NULL,
description TEXT,
image_url VARCHAR(500),
value VARCHAR(255),
quantity INTEGER NOT NULL DEFAULT 1,
display_order INTEGER NOT NULL DEFAULT 0
);
CREATE INDEX IF NOT EXISTS idx_prizes_sweepstake ON sweepstake_prizes(sweepstake_id);
-- Entries (unique per user per sweepstake)
CREATE TABLE IF NOT EXISTS sweepstake_entries (
id BIGSERIAL PRIMARY KEY,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
sweepstake_id BIGINT NOT NULL REFERENCES sweepstakes(id) ON DELETE CASCADE,
user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
status VARCHAR(16) NOT NULL DEFAULT 'valid', -- valid|invalid|withdrawn
ip_hash VARCHAR(64),
visual_played_at TIMESTAMPTZ,
view_count INTEGER NOT NULL DEFAULT 0,
UNIQUE (sweepstake_id, user_id)
);
CREATE INDEX IF NOT EXISTS idx_entries_sweepstake ON sweepstake_entries(sweepstake_id);
CREATE INDEX IF NOT EXISTS idx_entries_user ON sweepstake_entries(user_id);
-- Winners (one per prize unit, unique winner per sweepstake)
CREATE TABLE IF NOT EXISTS sweepstake_winners (
id BIGSERIAL PRIMARY KEY,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
sweepstake_id BIGINT NOT NULL REFERENCES sweepstakes(id) ON DELETE CASCADE,
entry_id BIGINT NOT NULL REFERENCES sweepstake_entries(id) ON DELETE CASCADE,
user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
prize_id BIGINT REFERENCES sweepstake_prizes(id) ON DELETE SET NULL,
prize_name VARCHAR(255), -- denormalized for safety
announced_at TIMESTAMPTZ,
notified_user_at TIMESTAMPTZ,
notified_admin_at TIMESTAMPTZ,
claim_status VARCHAR(16) NOT NULL DEFAULT 'pending', -- pending|claimed|delivered
claim_note TEXT,
UNIQUE (sweepstake_id, user_id)
);
CREATE INDEX IF NOT EXISTS idx_winners_sweepstake ON sweepstake_winners(sweepstake_id);
CREATE INDEX IF NOT EXISTS idx_winners_user ON sweepstake_winners(user_id);
@@ -0,0 +1,8 @@
-- Add non-physical prize support and winner award tracking
ALTER TABLE sweepstake_prizes
ADD COLUMN IF NOT EXISTS kind VARCHAR(16) NOT NULL DEFAULT 'physical',
ADD COLUMN IF NOT EXISTS points BIGINT NOT NULL DEFAULT 0,
ADD COLUMN IF NOT EXISTS xp BIGINT NOT NULL DEFAULT 0;
ALTER TABLE sweepstake_winners
ADD COLUMN IF NOT EXISTS awarded_at TIMESTAMPTZ;
@@ -0,0 +1,3 @@
-- Remove animated avatar upload unlock from user_profiles
ALTER TABLE user_profiles
DROP COLUMN IF EXISTS animated_avatar_upload_unlocked;
@@ -0,0 +1,3 @@
-- Add animated avatar upload unlock to user_profiles
ALTER TABLE user_profiles
ADD COLUMN IF NOT EXISTS animated_avatar_upload_unlocked BOOLEAN NOT NULL DEFAULT FALSE;