-- 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);