-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsupabase-schema.sql
More file actions
203 lines (168 loc) · 6.79 KB
/
supabase-schema.sql
File metadata and controls
203 lines (168 loc) · 6.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
-- Supabase Database Schema for Polling App
-- Run this in your Supabase SQL Editor
-- Enable UUID extension
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- Create profiles table (extends auth.users)
CREATE TABLE IF NOT EXISTS public.profiles (
id UUID REFERENCES auth.users(id) ON DELETE CASCADE PRIMARY KEY,
full_name TEXT,
avatar_url TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Create polls table
CREATE TABLE IF NOT EXISTS public.polls (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
title TEXT NOT NULL,
description TEXT,
category TEXT NOT NULL DEFAULT 'general',
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'closed', 'draft')),
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
ends_at TIMESTAMP WITH TIME ZONE,
total_votes INTEGER DEFAULT 0,
allow_multiple_votes BOOLEAN DEFAULT false,
anonymous_voting BOOLEAN DEFAULT false,
created_by UUID REFERENCES auth.users(id) ON DELETE CASCADE NOT NULL
);
-- Create poll_options table
CREATE TABLE IF NOT EXISTS public.poll_options (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
poll_id UUID REFERENCES public.polls(id) ON DELETE CASCADE NOT NULL,
text TEXT NOT NULL,
votes INTEGER DEFAULT 0,
order_index INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Create poll_votes table
CREATE TABLE IF NOT EXISTS public.poll_votes (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
poll_id UUID REFERENCES public.polls(id) ON DELETE CASCADE NOT NULL,
option_id UUID REFERENCES public.poll_options(id) ON DELETE CASCADE NOT NULL,
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
ip_address INET,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
-- Ensure one vote per user per poll (for non-multiple vote polls)
UNIQUE(poll_id, user_id, option_id)
);
-- Create indexes for better performance
CREATE INDEX IF NOT EXISTS idx_polls_created_by ON public.polls(created_by);
CREATE INDEX IF NOT EXISTS idx_polls_status ON public.polls(status);
CREATE INDEX IF NOT EXISTS idx_polls_category ON public.polls(category);
CREATE INDEX IF NOT EXISTS idx_polls_created_at ON public.polls(created_at);
CREATE INDEX IF NOT EXISTS idx_poll_options_poll_id ON public.poll_options(poll_id);
CREATE INDEX IF NOT EXISTS idx_poll_votes_poll_id ON public.poll_votes(poll_id);
CREATE INDEX IF NOT EXISTS idx_poll_votes_user_id ON public.poll_votes(user_id);
-- Enable Row Level Security (RLS)
ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.polls ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.poll_options ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.poll_votes ENABLE ROW LEVEL SECURITY;
-- RLS Policies for profiles
CREATE POLICY "Public profiles are viewable by everyone" ON public.profiles
FOR SELECT USING (true);
CREATE POLICY "Users can insert their own profile" ON public.profiles
FOR INSERT WITH CHECK (auth.uid() = id);
CREATE POLICY "Users can update their own profile" ON public.profiles
FOR UPDATE USING (auth.uid() = id);
-- RLS Policies for polls
CREATE POLICY "Polls are viewable by everyone" ON public.polls
FOR SELECT USING (true);
CREATE POLICY "Authenticated users can create polls" ON public.polls
FOR INSERT WITH CHECK (auth.role() = 'authenticated' AND auth.uid() = created_by);
CREATE POLICY "Users can update their own polls" ON public.polls
FOR UPDATE USING (auth.uid() = created_by);
CREATE POLICY "Users can delete their own polls" ON public.polls
FOR DELETE USING (auth.uid() = created_by);
-- RLS Policies for poll_options
CREATE POLICY "Poll options are viewable by everyone" ON public.poll_options
FOR SELECT USING (true);
CREATE POLICY "Poll creators can manage options" ON public.poll_options
FOR ALL USING (
EXISTS (
SELECT 1 FROM public.polls
WHERE polls.id = poll_options.poll_id
AND polls.created_by = auth.uid()
)
);
-- RLS Policies for poll_votes
CREATE POLICY "Poll votes are viewable by everyone" ON public.poll_votes
FOR SELECT USING (true);
CREATE POLICY "Authenticated users can vote" ON public.poll_votes
FOR INSERT WITH CHECK (
auth.role() = 'authenticated' AND
(auth.uid() = user_id OR user_id IS NULL)
);
CREATE POLICY "Users can delete their own votes" ON public.poll_votes
FOR DELETE USING (auth.uid() = user_id);
-- Functions to update vote counts
CREATE OR REPLACE FUNCTION update_poll_vote_counts()
RETURNS TRIGGER AS $$
BEGIN
-- Update option vote count
IF TG_OP = 'INSERT' THEN
UPDATE public.poll_options
SET votes = votes + 1
WHERE id = NEW.option_id;
-- Update total poll votes
UPDATE public.polls
SET total_votes = total_votes + 1
WHERE id = NEW.poll_id;
RETURN NEW;
ELSIF TG_OP = 'DELETE' THEN
UPDATE public.poll_options
SET votes = votes - 1
WHERE id = OLD.option_id;
-- Update total poll votes
UPDATE public.polls
SET total_votes = total_votes - 1
WHERE id = OLD.poll_id;
RETURN OLD;
END IF;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
-- Triggers to automatically update vote counts
DROP TRIGGER IF EXISTS trigger_update_vote_counts ON public.poll_votes;
CREATE TRIGGER trigger_update_vote_counts
AFTER INSERT OR DELETE ON public.poll_votes
FOR EACH ROW EXECUTE FUNCTION update_poll_vote_counts();
-- Function to handle user profile creation
CREATE OR REPLACE FUNCTION public.handle_new_user()
RETURNS TRIGGER AS $$
BEGIN
INSERT INTO public.profiles (id, full_name, avatar_url)
VALUES (
NEW.id,
COALESCE(NEW.raw_user_meta_data->>'full_name', NEW.email),
NEW.raw_user_meta_data->>'avatar_url'
);
RETURN NEW;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- Trigger to create profile on user signup
DROP TRIGGER IF EXISTS on_auth_user_created ON auth.users;
CREATE TRIGGER on_auth_user_created
AFTER INSERT ON auth.users
FOR EACH ROW EXECUTE FUNCTION public.handle_new_user();
-- Function to update updated_at timestamp
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- Triggers for updated_at
CREATE TRIGGER update_profiles_updated_at
BEFORE UPDATE ON public.profiles
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_polls_updated_at
BEFORE UPDATE ON public.polls
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- Insert some sample data (optional)
-- You can remove this section if you don't want sample data
-- Sample categories for reference
COMMENT ON COLUMN public.polls.category IS 'Categories: general, technology, entertainment, sports, politics, education, health, business, lifestyle, other';
-- Sample poll statuses for reference
COMMENT ON COLUMN public.polls.status IS 'Status: active, closed, draft';