Files
pezkuwi-telegram-miniapp/supabase/migrations/001_initial_schema.sql
T

215 lines
7.0 KiB
PL/PgSQL

-- Pezkuwi Telegram Mini App Database Schema
-- Run this in Supabase SQL Editor
-- Enable UUID extension
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- Users table (synced from Telegram)
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
telegram_id BIGINT UNIQUE NOT NULL,
username TEXT,
first_name TEXT NOT NULL,
last_name TEXT,
photo_url TEXT,
language_code TEXT DEFAULT 'ku',
is_admin BOOLEAN DEFAULT FALSE,
wallet_address TEXT,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- Announcements (admin posts)
CREATE TABLE announcements (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
title TEXT NOT NULL,
content TEXT NOT NULL,
image_url TEXT,
link_url TEXT,
author_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
likes INTEGER DEFAULT 0,
dislikes INTEGER DEFAULT 0,
views INTEGER DEFAULT 0,
is_published BOOLEAN DEFAULT TRUE,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- Announcement reactions
CREATE TABLE announcement_reactions (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
announcement_id UUID NOT NULL REFERENCES announcements(id) ON DELETE CASCADE,
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
reaction TEXT NOT NULL CHECK (reaction IN ('like', 'dislike')),
created_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(announcement_id, user_id)
);
-- Forum threads
CREATE TABLE threads (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
title TEXT NOT NULL,
content TEXT NOT NULL,
author_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
reply_count INTEGER DEFAULT 0,
likes INTEGER DEFAULT 0,
views INTEGER DEFAULT 0,
last_activity TIMESTAMPTZ DEFAULT NOW(),
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- Thread likes
CREATE TABLE thread_likes (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
thread_id UUID NOT NULL REFERENCES threads(id) ON DELETE CASCADE,
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
created_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(thread_id, user_id)
);
-- Replies to threads
CREATE TABLE replies (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
thread_id UUID NOT NULL REFERENCES threads(id) ON DELETE CASCADE,
content TEXT NOT NULL,
author_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
likes INTEGER DEFAULT 0,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- Reply likes
CREATE TABLE reply_likes (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
reply_id UUID NOT NULL REFERENCES replies(id) ON DELETE CASCADE,
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
created_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(reply_id, user_id)
);
-- Indexes for performance
CREATE INDEX idx_announcements_created_at ON announcements(created_at DESC);
CREATE INDEX idx_announcements_author ON announcements(author_id);
CREATE INDEX idx_threads_created_at ON threads(created_at DESC);
CREATE INDEX idx_threads_last_activity ON threads(last_activity DESC);
CREATE INDEX idx_threads_author ON threads(author_id);
CREATE INDEX idx_replies_thread ON replies(thread_id);
CREATE INDEX idx_replies_author ON replies(author_id);
CREATE INDEX idx_users_telegram_id ON users(telegram_id);
-- Updated at trigger function
CREATE OR REPLACE FUNCTION update_updated_at()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- Apply updated_at triggers
CREATE TRIGGER update_users_updated_at
BEFORE UPDATE ON users
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
CREATE TRIGGER update_announcements_updated_at
BEFORE UPDATE ON announcements
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
CREATE TRIGGER update_threads_updated_at
BEFORE UPDATE ON threads
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
CREATE TRIGGER update_replies_updated_at
BEFORE UPDATE ON replies
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
-- Row Level Security (RLS)
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
ALTER TABLE announcements ENABLE ROW LEVEL SECURITY;
ALTER TABLE announcement_reactions ENABLE ROW LEVEL SECURITY;
ALTER TABLE threads ENABLE ROW LEVEL SECURITY;
ALTER TABLE thread_likes ENABLE ROW LEVEL SECURITY;
ALTER TABLE replies ENABLE ROW LEVEL SECURITY;
ALTER TABLE reply_likes ENABLE ROW LEVEL SECURITY;
-- RLS Policies
-- Users: everyone can read, only self can update
CREATE POLICY "Users are viewable by everyone" ON users
FOR SELECT USING (true);
CREATE POLICY "Users can update own profile" ON users
FOR UPDATE USING (auth.uid()::text = id::text);
-- Announcements: everyone can read published
CREATE POLICY "Published announcements are viewable" ON announcements
FOR SELECT USING (is_published = true);
CREATE POLICY "Admins can manage announcements" ON announcements
FOR ALL USING (
EXISTS (SELECT 1 FROM users WHERE id::text = auth.uid()::text AND is_admin = true)
);
-- Announcement reactions: authenticated users
CREATE POLICY "Anyone can view reactions" ON announcement_reactions
FOR SELECT USING (true);
CREATE POLICY "Authenticated users can react" ON announcement_reactions
FOR INSERT WITH CHECK (auth.uid() IS NOT NULL);
CREATE POLICY "Users can manage own reactions" ON announcement_reactions
FOR DELETE USING (user_id::text = auth.uid()::text);
-- Threads: everyone can read
CREATE POLICY "Threads are viewable by everyone" ON threads
FOR SELECT USING (true);
CREATE POLICY "Authenticated users can create threads" ON threads
FOR INSERT WITH CHECK (auth.uid() IS NOT NULL);
CREATE POLICY "Authors can update own threads" ON threads
FOR UPDATE USING (author_id::text = auth.uid()::text);
CREATE POLICY "Authors and admins can delete threads" ON threads
FOR DELETE USING (
author_id::text = auth.uid()::text OR
EXISTS (SELECT 1 FROM users WHERE id::text = auth.uid()::text AND is_admin = true)
);
-- Thread likes
CREATE POLICY "Anyone can view thread likes" ON thread_likes
FOR SELECT USING (true);
CREATE POLICY "Authenticated users can like threads" ON thread_likes
FOR INSERT WITH CHECK (auth.uid() IS NOT NULL);
CREATE POLICY "Users can remove own likes" ON thread_likes
FOR DELETE USING (user_id::text = auth.uid()::text);
-- Replies
CREATE POLICY "Replies are viewable by everyone" ON replies
FOR SELECT USING (true);
CREATE POLICY "Authenticated users can create replies" ON replies
FOR INSERT WITH CHECK (auth.uid() IS NOT NULL);
CREATE POLICY "Authors can update own replies" ON replies
FOR UPDATE USING (author_id::text = auth.uid()::text);
CREATE POLICY "Authors and admins can delete replies" ON replies
FOR DELETE USING (
author_id::text = auth.uid()::text OR
EXISTS (SELECT 1 FROM users WHERE id::text = auth.uid()::text AND is_admin = true)
);
-- Reply likes
CREATE POLICY "Anyone can view reply likes" ON reply_likes
FOR SELECT USING (true);
CREATE POLICY "Authenticated users can like replies" ON reply_likes
FOR INSERT WITH CHECK (auth.uid() IS NOT NULL);
CREATE POLICY "Users can remove own reply likes" ON reply_likes
FOR DELETE USING (user_id::text = auth.uid()::text);