-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathschema.sql
More file actions
253 lines (192 loc) · 4.87 KB
/
schema.sql
File metadata and controls
253 lines (192 loc) · 4.87 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
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE TYPE user_role AS ENUM (
'admin',
'editor',
'contributor'
);
CREATE TYPE user_status AS ENUM (
'active',
'suspended'
);
CREATE TYPE content_type AS ENUM (
'post',
'page',
'documentation',
'product',
'landing_page'
);
CREATE TYPE content_status AS ENUM (
'draft',
'pending_review',
'scheduled',
'published',
'archived'
);
CREATE TYPE content_visibility AS ENUM (
'public',
'private',
'members_only'
);
CREATE TABLE profiles (
id UUID PRIMARY KEY
REFERENCES auth.users(id) ON DELETE CASCADE,
display_name TEXT NOT NULL,
bio TEXT,
avatar_url TEXT,
role user_role NOT NULL DEFAULT 'contributor',
status user_status NOT NULL DEFAULT 'active',
-- Protection flag for critical users (initial/foundational users)
is_protected BOOLEAN DEFAULT false NOT NULL,
-- User preferences (theme, editor settings, notifications)
preferences JSONB DEFAULT '{}',
last_seen_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE categories (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
name TEXT NOT NULL,
slug TEXT UNIQUE NOT NULL,
description TEXT,
parent_id UUID REFERENCES categories(id),
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE tags (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
name TEXT NOT NULL,
slug TEXT UNIQUE NOT NULL,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE contents (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
type content_type NOT NULL,
title TEXT NOT NULL,
slug TEXT UNIQUE NOT NULL,
excerpt TEXT,
body JSONB NOT NULL,
body_format TEXT NOT NULL,
status content_status NOT NULL DEFAULT 'draft',
visibility content_visibility NOT NULL DEFAULT 'public',
category_id UUID REFERENCES categories(id),
thumbnail_url TEXT,
author_id UUID REFERENCES profiles(id),
published_at TIMESTAMPTZ,
scheduled_at TIMESTAMPTZ,
is_featured BOOLEAN DEFAULT false,
allow_comments BOOLEAN DEFAULT true,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE content_tags (
content_id UUID
REFERENCES contents(id) ON DELETE CASCADE,
tag_id UUID
REFERENCES tags(id) ON DELETE CASCADE,
PRIMARY KEY (content_id, tag_id)
);
CREATE TABLE seo_meta (
content_id UUID PRIMARY KEY
REFERENCES contents(id) ON DELETE CASCADE,
meta_title TEXT,
meta_description TEXT,
og_image_url TEXT,
canonical_url TEXT,
structured_data JSONB,
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE comments (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
content_id UUID
REFERENCES contents(id) ON DELETE CASCADE,
author_name TEXT NOT NULL,
author_email TEXT,
comment_text TEXT NOT NULL,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE settings (
key TEXT PRIMARY KEY,
value JSONB NOT NULL,
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE media (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
filename TEXT NOT NULL,
original_name TEXT NOT NULL,
mime_type TEXT NOT NULL,
size INTEGER NOT NULL,
url TEXT NOT NULL,
storage_path TEXT NOT NULL,
alt_text TEXT,
caption TEXT,
uploaded_by UUID REFERENCES profiles(id),
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE INDEX idx_media_uploaded_by ON media(uploaded_by);
CREATE INDEX idx_media_created_at ON media(created_at DESC);
CREATE INDEX idx_contents_status
ON contents(status);
CREATE INDEX idx_contents_author
ON contents(author_id);
CREATE INDEX idx_contents_category
ON contents(category_id);
CREATE INDEX idx_content_tags_tag
ON content_tags(tag_id);
CREATE INDEX idx_comments_content
ON comments(content_id);
ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;
ALTER TABLE contents ENABLE ROW LEVEL SECURITY;
ALTER TABLE comments ENABLE ROW LEVEL SECURITY;
ALTER TABLE media ENABLE ROW LEVEL SECURITY;
CREATE POLICY public_read_published_content
ON contents
FOR SELECT
USING (status = 'published');
CREATE POLICY author_manage_own_content
ON contents
FOR ALL
USING (author_id = auth.uid());
CREATE POLICY admin_full_access
ON contents
FOR ALL
USING (
EXISTS (
SELECT 1
FROM profiles
WHERE id = auth.uid()
AND role = 'admin'
)
);
-- Media policies: authenticated users can read all, manage their own
CREATE POLICY media_public_read
ON media
FOR SELECT
TO authenticated
USING (true);
CREATE POLICY media_insert_own
ON media
FOR INSERT
TO authenticated
WITH CHECK (uploaded_by = auth.uid());
CREATE POLICY media_update_own
ON media
FOR UPDATE
TO authenticated
USING (uploaded_by = auth.uid());
CREATE POLICY media_delete_own
ON media
FOR DELETE
TO authenticated
USING (uploaded_by = auth.uid());
-- Admins can manage all media
CREATE POLICY media_admin_full_access
ON media
FOR ALL
TO authenticated
USING (
EXISTS (
SELECT 1
FROM profiles
WHERE id = auth.uid()
AND role = 'admin'
)
);