mirror of
https://github.com/pezkuwichain/pezkuwi-telegram-miniapp.git
synced 2026-04-22 00:47:55 +00:00
215 lines
7.0 KiB
PL/PgSQL
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);
|