@realtimex/folio 0.1.16 → 0.1.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/api/src/middleware/auth.ts +77 -0
- package/api/src/routes/chat.ts +7 -1
- package/api/src/routes/index.ts +2 -0
- package/api/src/routes/ingestions.ts +45 -5
- package/api/src/routes/policies.ts +50 -7
- package/api/src/routes/stats.ts +9 -5
- package/api/src/routes/workspaces.ts +290 -0
- package/api/src/services/ChatService.ts +8 -2
- package/api/src/services/IngestionService.ts +38 -26
- package/api/src/services/PolicyEngine.ts +4 -1
- package/api/src/services/PolicyLearningService.ts +31 -6
- package/api/src/services/PolicyLoader.ts +44 -25
- package/api/src/services/RAGService.ts +52 -12
- package/dist/api/src/middleware/auth.js +59 -0
- package/dist/api/src/routes/chat.js +1 -1
- package/dist/api/src/routes/index.js +2 -0
- package/dist/api/src/routes/ingestions.js +45 -8
- package/dist/api/src/routes/policies.js +49 -7
- package/dist/api/src/routes/stats.js +9 -5
- package/dist/api/src/routes/workspaces.js +220 -0
- package/dist/api/src/services/ChatService.js +7 -2
- package/dist/api/src/services/IngestionService.js +35 -30
- package/dist/api/src/services/PolicyEngine.js +2 -1
- package/dist/api/src/services/PolicyLearningService.js +28 -6
- package/dist/api/src/services/PolicyLoader.js +29 -25
- package/dist/api/src/services/RAGService.js +43 -11
- package/dist/assets/index-CTn5FcC4.js +113 -0
- package/dist/assets/index-Dq9sxoZK.css +1 -0
- package/dist/index.html +2 -2
- package/package.json +1 -1
- package/supabase/functions/workspace-invite/index.ts +110 -0
- package/supabase/migrations/20260223000000_initial_foundation.sql +5 -0
- package/supabase/migrations/20260224000004_add_avatars_storage.sql +4 -0
- package/supabase/migrations/20260224000006_add_policies_table.sql +5 -0
- package/supabase/migrations/20260224000008_add_ingestions_table.sql +2 -0
- package/supabase/migrations/20260225000000_setup_compatible_mode.sql +17 -4
- package/supabase/migrations/20260225000003_add_baseline_configs.sql +4 -3
- package/supabase/migrations/20260226000000_add_processing_events.sql +1 -0
- package/supabase/migrations/20260226000002_add_dynamic_rag.sql +1 -0
- package/supabase/migrations/20260226000005_add_chat_tables.sql +3 -0
- package/supabase/migrations/20260228000001_add_policy_match_feedback.sql +4 -0
- package/supabase/migrations/20260302064608_add_ingestion_llm_settings_compat.sql +15 -0
- package/supabase/migrations/20260303000000_add_workspaces_phase1.sql +459 -0
- package/supabase/migrations/20260303010000_add_workspace_management_rpc.sql +310 -0
- package/supabase/migrations/20260303020000_workspace_scope_document_chunks.sql +139 -0
- package/dist/assets/index-DzN8-j-e.css +0 -1
- package/dist/assets/index-dnBz6SWG.js +0 -113
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
-- Phase 1 multi-user foundations:
|
|
2
|
+
-- - Introduce workspaces + workspace_members
|
|
3
|
+
-- - Scope policies, ingestions, and policy learning feedback by workspace_id
|
|
4
|
+
-- - Keep user_id for actor/audit trails and backward compatibility
|
|
5
|
+
|
|
6
|
+
-- ---------------------------------------------------------------------------
|
|
7
|
+
-- Workspace primitives
|
|
8
|
+
-- ---------------------------------------------------------------------------
|
|
9
|
+
|
|
10
|
+
CREATE TABLE IF NOT EXISTS public.workspaces (
|
|
11
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
12
|
+
name TEXT NOT NULL,
|
|
13
|
+
owner_user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
|
|
14
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
15
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
CREATE TABLE IF NOT EXISTS public.workspace_members (
|
|
19
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
20
|
+
workspace_id UUID NOT NULL REFERENCES public.workspaces(id) ON DELETE CASCADE,
|
|
21
|
+
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
|
|
22
|
+
role TEXT NOT NULL DEFAULT 'member' CHECK (role IN ('owner', 'admin', 'member')),
|
|
23
|
+
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'invited', 'disabled')),
|
|
24
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
25
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
26
|
+
UNIQUE (workspace_id, user_id)
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
CREATE INDEX IF NOT EXISTS workspace_members_user_id_status_idx
|
|
30
|
+
ON public.workspace_members(user_id, status);
|
|
31
|
+
|
|
32
|
+
CREATE INDEX IF NOT EXISTS workspace_members_workspace_id_status_idx
|
|
33
|
+
ON public.workspace_members(workspace_id, status);
|
|
34
|
+
|
|
35
|
+
DROP TRIGGER IF EXISTS workspaces_updated_at ON public.workspaces;
|
|
36
|
+
CREATE TRIGGER workspaces_updated_at
|
|
37
|
+
BEFORE UPDATE ON public.workspaces
|
|
38
|
+
FOR EACH ROW EXECUTE FUNCTION public.update_updated_at_column();
|
|
39
|
+
|
|
40
|
+
DROP TRIGGER IF EXISTS workspace_members_updated_at ON public.workspace_members;
|
|
41
|
+
CREATE TRIGGER workspace_members_updated_at
|
|
42
|
+
BEFORE UPDATE ON public.workspace_members
|
|
43
|
+
FOR EACH ROW EXECUTE FUNCTION public.update_updated_at_column();
|
|
44
|
+
|
|
45
|
+
ALTER TABLE public.workspaces ENABLE ROW LEVEL SECURITY;
|
|
46
|
+
ALTER TABLE public.workspace_members ENABLE ROW LEVEL SECURITY;
|
|
47
|
+
|
|
48
|
+
DROP POLICY IF EXISTS "Members can read workspaces" ON public.workspaces;
|
|
49
|
+
CREATE POLICY "Members can read workspaces"
|
|
50
|
+
ON public.workspaces FOR SELECT
|
|
51
|
+
USING (
|
|
52
|
+
EXISTS (
|
|
53
|
+
SELECT 1
|
|
54
|
+
FROM public.workspace_members wm
|
|
55
|
+
WHERE wm.workspace_id = workspaces.id
|
|
56
|
+
AND wm.user_id = auth.uid()
|
|
57
|
+
AND wm.status = 'active'
|
|
58
|
+
)
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
DROP POLICY IF EXISTS "Users can create owned workspaces" ON public.workspaces;
|
|
62
|
+
CREATE POLICY "Users can create owned workspaces"
|
|
63
|
+
ON public.workspaces FOR INSERT
|
|
64
|
+
WITH CHECK (owner_user_id = auth.uid());
|
|
65
|
+
|
|
66
|
+
DROP POLICY IF EXISTS "Admins can update workspaces" ON public.workspaces;
|
|
67
|
+
CREATE POLICY "Admins can update workspaces"
|
|
68
|
+
ON public.workspaces FOR UPDATE
|
|
69
|
+
USING (
|
|
70
|
+
EXISTS (
|
|
71
|
+
SELECT 1
|
|
72
|
+
FROM public.workspace_members wm
|
|
73
|
+
WHERE wm.workspace_id = workspaces.id
|
|
74
|
+
AND wm.user_id = auth.uid()
|
|
75
|
+
AND wm.status = 'active'
|
|
76
|
+
AND wm.role IN ('owner', 'admin')
|
|
77
|
+
)
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
DROP POLICY IF EXISTS "Owners can delete workspaces" ON public.workspaces;
|
|
81
|
+
CREATE POLICY "Owners can delete workspaces"
|
|
82
|
+
ON public.workspaces FOR DELETE
|
|
83
|
+
USING (
|
|
84
|
+
EXISTS (
|
|
85
|
+
SELECT 1
|
|
86
|
+
FROM public.workspace_members wm
|
|
87
|
+
WHERE wm.workspace_id = workspaces.id
|
|
88
|
+
AND wm.user_id = auth.uid()
|
|
89
|
+
AND wm.status = 'active'
|
|
90
|
+
AND wm.role = 'owner'
|
|
91
|
+
)
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
DROP POLICY IF EXISTS "Users can read their workspace memberships" ON public.workspace_members;
|
|
95
|
+
CREATE POLICY "Users can read their workspace memberships"
|
|
96
|
+
ON public.workspace_members FOR SELECT
|
|
97
|
+
USING (user_id = auth.uid());
|
|
98
|
+
|
|
99
|
+
DROP POLICY IF EXISTS "Users can manage their workspace memberships" ON public.workspace_members;
|
|
100
|
+
CREATE POLICY "Users can manage their workspace memberships"
|
|
101
|
+
ON public.workspace_members FOR ALL
|
|
102
|
+
USING (user_id = auth.uid())
|
|
103
|
+
WITH CHECK (user_id = auth.uid());
|
|
104
|
+
|
|
105
|
+
CREATE OR REPLACE FUNCTION public.is_workspace_member(
|
|
106
|
+
p_workspace_id UUID,
|
|
107
|
+
p_user_id UUID DEFAULT auth.uid()
|
|
108
|
+
)
|
|
109
|
+
RETURNS BOOLEAN
|
|
110
|
+
LANGUAGE sql
|
|
111
|
+
STABLE
|
|
112
|
+
SECURITY DEFINER
|
|
113
|
+
SET search_path = public
|
|
114
|
+
AS $$
|
|
115
|
+
SELECT EXISTS (
|
|
116
|
+
SELECT 1
|
|
117
|
+
FROM public.workspace_members wm
|
|
118
|
+
WHERE wm.workspace_id = p_workspace_id
|
|
119
|
+
AND wm.user_id = p_user_id
|
|
120
|
+
AND wm.status = 'active'
|
|
121
|
+
);
|
|
122
|
+
$$;
|
|
123
|
+
|
|
124
|
+
GRANT EXECUTE ON FUNCTION public.is_workspace_member(UUID, UUID) TO authenticated;
|
|
125
|
+
|
|
126
|
+
-- ---------------------------------------------------------------------------
|
|
127
|
+
-- Backfill one default workspace per existing user
|
|
128
|
+
-- ---------------------------------------------------------------------------
|
|
129
|
+
|
|
130
|
+
WITH users_without_workspace AS (
|
|
131
|
+
SELECT u.id AS user_id
|
|
132
|
+
FROM auth.users u
|
|
133
|
+
WHERE NOT EXISTS (
|
|
134
|
+
SELECT 1
|
|
135
|
+
FROM public.workspace_members wm
|
|
136
|
+
WHERE wm.user_id = u.id
|
|
137
|
+
AND wm.status = 'active'
|
|
138
|
+
)
|
|
139
|
+
)
|
|
140
|
+
INSERT INTO public.workspaces (name, owner_user_id)
|
|
141
|
+
SELECT
|
|
142
|
+
COALESCE(
|
|
143
|
+
NULLIF(TRIM(COALESCE(p.first_name, '') || ' ' || COALESCE(p.last_name, '')), ''),
|
|
144
|
+
NULLIF(SPLIT_PART(COALESCE(u.email, ''), '@', 1), ''),
|
|
145
|
+
'Workspace'
|
|
146
|
+
) || '''s Workspace',
|
|
147
|
+
uw.user_id
|
|
148
|
+
FROM users_without_workspace uw
|
|
149
|
+
JOIN auth.users u ON u.id = uw.user_id
|
|
150
|
+
LEFT JOIN public.profiles p ON p.id = uw.user_id;
|
|
151
|
+
|
|
152
|
+
INSERT INTO public.workspace_members (workspace_id, user_id, role, status)
|
|
153
|
+
SELECT w.id, w.owner_user_id, 'owner', 'active'
|
|
154
|
+
FROM public.workspaces w
|
|
155
|
+
WHERE NOT EXISTS (
|
|
156
|
+
SELECT 1
|
|
157
|
+
FROM public.workspace_members wm
|
|
158
|
+
WHERE wm.workspace_id = w.id
|
|
159
|
+
AND wm.user_id = w.owner_user_id
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
-- ---------------------------------------------------------------------------
|
|
163
|
+
-- Workspace scoping columns
|
|
164
|
+
-- ---------------------------------------------------------------------------
|
|
165
|
+
|
|
166
|
+
ALTER TABLE public.policies ADD COLUMN IF NOT EXISTS workspace_id UUID;
|
|
167
|
+
ALTER TABLE public.ingestions ADD COLUMN IF NOT EXISTS workspace_id UUID;
|
|
168
|
+
ALTER TABLE public.policy_match_feedback ADD COLUMN IF NOT EXISTS workspace_id UUID;
|
|
169
|
+
|
|
170
|
+
WITH user_primary_workspace AS (
|
|
171
|
+
SELECT DISTINCT ON (wm.user_id)
|
|
172
|
+
wm.user_id,
|
|
173
|
+
wm.workspace_id
|
|
174
|
+
FROM public.workspace_members wm
|
|
175
|
+
WHERE wm.status = 'active'
|
|
176
|
+
ORDER BY
|
|
177
|
+
wm.user_id,
|
|
178
|
+
CASE wm.role
|
|
179
|
+
WHEN 'owner' THEN 0
|
|
180
|
+
WHEN 'admin' THEN 1
|
|
181
|
+
ELSE 2
|
|
182
|
+
END,
|
|
183
|
+
wm.created_at,
|
|
184
|
+
wm.workspace_id
|
|
185
|
+
)
|
|
186
|
+
UPDATE public.policies p
|
|
187
|
+
SET workspace_id = upw.workspace_id
|
|
188
|
+
FROM user_primary_workspace upw
|
|
189
|
+
WHERE p.workspace_id IS NULL
|
|
190
|
+
AND p.user_id = upw.user_id;
|
|
191
|
+
|
|
192
|
+
WITH user_primary_workspace AS (
|
|
193
|
+
SELECT DISTINCT ON (wm.user_id)
|
|
194
|
+
wm.user_id,
|
|
195
|
+
wm.workspace_id
|
|
196
|
+
FROM public.workspace_members wm
|
|
197
|
+
WHERE wm.status = 'active'
|
|
198
|
+
ORDER BY
|
|
199
|
+
wm.user_id,
|
|
200
|
+
CASE wm.role
|
|
201
|
+
WHEN 'owner' THEN 0
|
|
202
|
+
WHEN 'admin' THEN 1
|
|
203
|
+
ELSE 2
|
|
204
|
+
END,
|
|
205
|
+
wm.created_at,
|
|
206
|
+
wm.workspace_id
|
|
207
|
+
)
|
|
208
|
+
UPDATE public.ingestions i
|
|
209
|
+
SET workspace_id = upw.workspace_id
|
|
210
|
+
FROM user_primary_workspace upw
|
|
211
|
+
WHERE i.workspace_id IS NULL
|
|
212
|
+
AND i.user_id = upw.user_id;
|
|
213
|
+
|
|
214
|
+
UPDATE public.policy_match_feedback pmf
|
|
215
|
+
SET workspace_id = i.workspace_id
|
|
216
|
+
FROM public.ingestions i
|
|
217
|
+
WHERE pmf.workspace_id IS NULL
|
|
218
|
+
AND pmf.ingestion_id = i.id;
|
|
219
|
+
|
|
220
|
+
WITH user_primary_workspace AS (
|
|
221
|
+
SELECT DISTINCT ON (wm.user_id)
|
|
222
|
+
wm.user_id,
|
|
223
|
+
wm.workspace_id
|
|
224
|
+
FROM public.workspace_members wm
|
|
225
|
+
WHERE wm.status = 'active'
|
|
226
|
+
ORDER BY
|
|
227
|
+
wm.user_id,
|
|
228
|
+
CASE wm.role
|
|
229
|
+
WHEN 'owner' THEN 0
|
|
230
|
+
WHEN 'admin' THEN 1
|
|
231
|
+
ELSE 2
|
|
232
|
+
END,
|
|
233
|
+
wm.created_at,
|
|
234
|
+
wm.workspace_id
|
|
235
|
+
)
|
|
236
|
+
UPDATE public.policy_match_feedback pmf
|
|
237
|
+
SET workspace_id = upw.workspace_id
|
|
238
|
+
FROM user_primary_workspace upw
|
|
239
|
+
WHERE pmf.workspace_id IS NULL
|
|
240
|
+
AND pmf.user_id = upw.user_id;
|
|
241
|
+
|
|
242
|
+
ALTER TABLE public.policies
|
|
243
|
+
ALTER COLUMN workspace_id SET NOT NULL;
|
|
244
|
+
|
|
245
|
+
ALTER TABLE public.ingestions
|
|
246
|
+
ALTER COLUMN workspace_id SET NOT NULL;
|
|
247
|
+
|
|
248
|
+
ALTER TABLE public.policy_match_feedback
|
|
249
|
+
ALTER COLUMN workspace_id SET NOT NULL;
|
|
250
|
+
|
|
251
|
+
DO $$
|
|
252
|
+
BEGIN
|
|
253
|
+
IF NOT EXISTS (
|
|
254
|
+
SELECT 1 FROM pg_constraint
|
|
255
|
+
WHERE conname = 'policies_workspace_id_fkey'
|
|
256
|
+
) THEN
|
|
257
|
+
ALTER TABLE public.policies
|
|
258
|
+
ADD CONSTRAINT policies_workspace_id_fkey
|
|
259
|
+
FOREIGN KEY (workspace_id) REFERENCES public.workspaces(id) ON DELETE CASCADE;
|
|
260
|
+
END IF;
|
|
261
|
+
|
|
262
|
+
IF NOT EXISTS (
|
|
263
|
+
SELECT 1 FROM pg_constraint
|
|
264
|
+
WHERE conname = 'ingestions_workspace_id_fkey'
|
|
265
|
+
) THEN
|
|
266
|
+
ALTER TABLE public.ingestions
|
|
267
|
+
ADD CONSTRAINT ingestions_workspace_id_fkey
|
|
268
|
+
FOREIGN KEY (workspace_id) REFERENCES public.workspaces(id) ON DELETE CASCADE;
|
|
269
|
+
END IF;
|
|
270
|
+
|
|
271
|
+
IF NOT EXISTS (
|
|
272
|
+
SELECT 1 FROM pg_constraint
|
|
273
|
+
WHERE conname = 'policy_match_feedback_workspace_id_fkey'
|
|
274
|
+
) THEN
|
|
275
|
+
ALTER TABLE public.policy_match_feedback
|
|
276
|
+
ADD CONSTRAINT policy_match_feedback_workspace_id_fkey
|
|
277
|
+
FOREIGN KEY (workspace_id) REFERENCES public.workspaces(id) ON DELETE CASCADE;
|
|
278
|
+
END IF;
|
|
279
|
+
END $$;
|
|
280
|
+
|
|
281
|
+
ALTER TABLE public.policies
|
|
282
|
+
DROP CONSTRAINT IF EXISTS policies_user_id_policy_id_key;
|
|
283
|
+
|
|
284
|
+
ALTER TABLE public.policies
|
|
285
|
+
DROP CONSTRAINT IF EXISTS policies_workspace_id_policy_id_key;
|
|
286
|
+
|
|
287
|
+
ALTER TABLE public.policies
|
|
288
|
+
ADD CONSTRAINT policies_workspace_id_policy_id_key UNIQUE (workspace_id, policy_id);
|
|
289
|
+
|
|
290
|
+
ALTER TABLE public.policy_match_feedback
|
|
291
|
+
DROP CONSTRAINT IF EXISTS policy_match_feedback_user_id_ingestion_id_policy_id_key;
|
|
292
|
+
|
|
293
|
+
ALTER TABLE public.policy_match_feedback
|
|
294
|
+
DROP CONSTRAINT IF EXISTS policy_match_feedback_workspace_id_ingestion_id_policy_id_key;
|
|
295
|
+
|
|
296
|
+
ALTER TABLE public.policy_match_feedback
|
|
297
|
+
ADD CONSTRAINT policy_match_feedback_workspace_id_ingestion_id_policy_id_key
|
|
298
|
+
UNIQUE (workspace_id, ingestion_id, policy_id);
|
|
299
|
+
|
|
300
|
+
CREATE INDEX IF NOT EXISTS policies_workspace_id_priority_idx
|
|
301
|
+
ON public.policies(workspace_id, priority DESC);
|
|
302
|
+
|
|
303
|
+
CREATE INDEX IF NOT EXISTS ingestions_workspace_id_created_at_idx
|
|
304
|
+
ON public.ingestions(workspace_id, created_at DESC);
|
|
305
|
+
|
|
306
|
+
CREATE INDEX IF NOT EXISTS idx_ingestions_workspace_file_hash
|
|
307
|
+
ON public.ingestions(workspace_id, file_hash)
|
|
308
|
+
WHERE file_hash IS NOT NULL;
|
|
309
|
+
|
|
310
|
+
CREATE INDEX IF NOT EXISTS policy_match_feedback_workspace_policy_idx
|
|
311
|
+
ON public.policy_match_feedback(workspace_id, policy_id, created_at DESC);
|
|
312
|
+
|
|
313
|
+
CREATE INDEX IF NOT EXISTS policy_match_feedback_workspace_created_idx
|
|
314
|
+
ON public.policy_match_feedback(workspace_id, created_at DESC);
|
|
315
|
+
|
|
316
|
+
-- ---------------------------------------------------------------------------
|
|
317
|
+
-- Update RLS for workspace-scoped tables
|
|
318
|
+
-- ---------------------------------------------------------------------------
|
|
319
|
+
|
|
320
|
+
DROP POLICY IF EXISTS "Users can read own policies" ON public.policies;
|
|
321
|
+
DROP POLICY IF EXISTS "Users can insert own policies" ON public.policies;
|
|
322
|
+
DROP POLICY IF EXISTS "Users can update own policies" ON public.policies;
|
|
323
|
+
DROP POLICY IF EXISTS "Users can delete own policies" ON public.policies;
|
|
324
|
+
DROP POLICY IF EXISTS "Workspace members can read policies" ON public.policies;
|
|
325
|
+
DROP POLICY IF EXISTS "Workspace members can insert policies" ON public.policies;
|
|
326
|
+
DROP POLICY IF EXISTS "Workspace members can update policies" ON public.policies;
|
|
327
|
+
DROP POLICY IF EXISTS "Workspace members can delete policies" ON public.policies;
|
|
328
|
+
|
|
329
|
+
CREATE POLICY "Workspace members can read policies"
|
|
330
|
+
ON public.policies FOR SELECT
|
|
331
|
+
USING (public.is_workspace_member(workspace_id));
|
|
332
|
+
|
|
333
|
+
CREATE POLICY "Workspace members can insert policies"
|
|
334
|
+
ON public.policies FOR INSERT
|
|
335
|
+
WITH CHECK (public.is_workspace_member(workspace_id) AND user_id = auth.uid());
|
|
336
|
+
|
|
337
|
+
CREATE POLICY "Workspace members can update policies"
|
|
338
|
+
ON public.policies FOR UPDATE
|
|
339
|
+
USING (public.is_workspace_member(workspace_id))
|
|
340
|
+
WITH CHECK (public.is_workspace_member(workspace_id));
|
|
341
|
+
|
|
342
|
+
CREATE POLICY "Workspace members can delete policies"
|
|
343
|
+
ON public.policies FOR DELETE
|
|
344
|
+
USING (public.is_workspace_member(workspace_id));
|
|
345
|
+
|
|
346
|
+
DROP POLICY IF EXISTS "Users can manage their own ingestions" ON public.ingestions;
|
|
347
|
+
DROP POLICY IF EXISTS "Workspace members can read ingestions" ON public.ingestions;
|
|
348
|
+
DROP POLICY IF EXISTS "Workspace members can insert ingestions" ON public.ingestions;
|
|
349
|
+
DROP POLICY IF EXISTS "Workspace members can update ingestions" ON public.ingestions;
|
|
350
|
+
DROP POLICY IF EXISTS "Workspace members can delete ingestions" ON public.ingestions;
|
|
351
|
+
|
|
352
|
+
CREATE POLICY "Workspace members can read ingestions"
|
|
353
|
+
ON public.ingestions FOR SELECT
|
|
354
|
+
USING (public.is_workspace_member(workspace_id));
|
|
355
|
+
|
|
356
|
+
CREATE POLICY "Workspace members can insert ingestions"
|
|
357
|
+
ON public.ingestions FOR INSERT
|
|
358
|
+
WITH CHECK (public.is_workspace_member(workspace_id) AND user_id = auth.uid());
|
|
359
|
+
|
|
360
|
+
CREATE POLICY "Workspace members can update ingestions"
|
|
361
|
+
ON public.ingestions FOR UPDATE
|
|
362
|
+
USING (public.is_workspace_member(workspace_id))
|
|
363
|
+
WITH CHECK (public.is_workspace_member(workspace_id));
|
|
364
|
+
|
|
365
|
+
CREATE POLICY "Workspace members can delete ingestions"
|
|
366
|
+
ON public.ingestions FOR DELETE
|
|
367
|
+
USING (public.is_workspace_member(workspace_id));
|
|
368
|
+
|
|
369
|
+
DROP POLICY IF EXISTS "Users can read own policy match feedback" ON public.policy_match_feedback;
|
|
370
|
+
DROP POLICY IF EXISTS "Users can insert own policy match feedback" ON public.policy_match_feedback;
|
|
371
|
+
DROP POLICY IF EXISTS "Users can update own policy match feedback" ON public.policy_match_feedback;
|
|
372
|
+
DROP POLICY IF EXISTS "Users can delete own policy match feedback" ON public.policy_match_feedback;
|
|
373
|
+
DROP POLICY IF EXISTS "Workspace members can read policy match feedback" ON public.policy_match_feedback;
|
|
374
|
+
DROP POLICY IF EXISTS "Workspace members can insert policy match feedback" ON public.policy_match_feedback;
|
|
375
|
+
DROP POLICY IF EXISTS "Workspace members can update policy match feedback" ON public.policy_match_feedback;
|
|
376
|
+
DROP POLICY IF EXISTS "Workspace members can delete policy match feedback" ON public.policy_match_feedback;
|
|
377
|
+
|
|
378
|
+
CREATE POLICY "Workspace members can read policy match feedback"
|
|
379
|
+
ON public.policy_match_feedback FOR SELECT
|
|
380
|
+
USING (public.is_workspace_member(workspace_id));
|
|
381
|
+
|
|
382
|
+
CREATE POLICY "Workspace members can insert policy match feedback"
|
|
383
|
+
ON public.policy_match_feedback FOR INSERT
|
|
384
|
+
WITH CHECK (public.is_workspace_member(workspace_id) AND user_id = auth.uid());
|
|
385
|
+
|
|
386
|
+
CREATE POLICY "Workspace members can update policy match feedback"
|
|
387
|
+
ON public.policy_match_feedback FOR UPDATE
|
|
388
|
+
USING (public.is_workspace_member(workspace_id))
|
|
389
|
+
WITH CHECK (public.is_workspace_member(workspace_id));
|
|
390
|
+
|
|
391
|
+
CREATE POLICY "Workspace members can delete policy match feedback"
|
|
392
|
+
ON public.policy_match_feedback FOR DELETE
|
|
393
|
+
USING (public.is_workspace_member(workspace_id));
|
|
394
|
+
|
|
395
|
+
-- ---------------------------------------------------------------------------
|
|
396
|
+
-- Ensure new auth users automatically get a default workspace + membership
|
|
397
|
+
-- ---------------------------------------------------------------------------
|
|
398
|
+
|
|
399
|
+
CREATE OR REPLACE FUNCTION public.handle_new_auth_user()
|
|
400
|
+
RETURNS TRIGGER
|
|
401
|
+
LANGUAGE plpgsql
|
|
402
|
+
SECURITY DEFINER
|
|
403
|
+
SET search_path = 'pg_catalog', 'public'
|
|
404
|
+
AS $$
|
|
405
|
+
DECLARE
|
|
406
|
+
should_be_admin BOOLEAN;
|
|
407
|
+
workspace_name TEXT;
|
|
408
|
+
created_workspace_id UUID;
|
|
409
|
+
BEGIN
|
|
410
|
+
-- Serialize first-user admin assignment and profile bootstrap.
|
|
411
|
+
PERFORM pg_advisory_xact_lock(602240003);
|
|
412
|
+
|
|
413
|
+
SELECT NOT EXISTS (
|
|
414
|
+
SELECT 1
|
|
415
|
+
FROM public.profiles p
|
|
416
|
+
WHERE p.is_admin = true
|
|
417
|
+
)
|
|
418
|
+
INTO should_be_admin;
|
|
419
|
+
|
|
420
|
+
INSERT INTO public.profiles (id, first_name, last_name, email, is_admin)
|
|
421
|
+
VALUES (
|
|
422
|
+
NEW.id,
|
|
423
|
+
NEW.raw_user_meta_data ->> 'first_name',
|
|
424
|
+
NEW.raw_user_meta_data ->> 'last_name',
|
|
425
|
+
NEW.email,
|
|
426
|
+
should_be_admin
|
|
427
|
+
)
|
|
428
|
+
ON CONFLICT (id) DO UPDATE
|
|
429
|
+
SET
|
|
430
|
+
first_name = COALESCE(EXCLUDED.first_name, public.profiles.first_name),
|
|
431
|
+
last_name = COALESCE(EXCLUDED.last_name, public.profiles.last_name),
|
|
432
|
+
email = EXCLUDED.email,
|
|
433
|
+
updated_at = NOW();
|
|
434
|
+
|
|
435
|
+
INSERT INTO public.user_settings (user_id)
|
|
436
|
+
VALUES (NEW.id)
|
|
437
|
+
ON CONFLICT (user_id) DO NOTHING;
|
|
438
|
+
|
|
439
|
+
workspace_name := COALESCE(
|
|
440
|
+
NULLIF(TRIM(COALESCE(NEW.raw_user_meta_data ->> 'first_name', '') || ' ' || COALESCE(NEW.raw_user_meta_data ->> 'last_name', '')), ''),
|
|
441
|
+
NULLIF(SPLIT_PART(COALESCE(NEW.email, ''), '@', 1), ''),
|
|
442
|
+
'Workspace'
|
|
443
|
+
) || '''s Workspace';
|
|
444
|
+
|
|
445
|
+
INSERT INTO public.workspaces (name, owner_user_id)
|
|
446
|
+
VALUES (workspace_name, NEW.id)
|
|
447
|
+
RETURNING id INTO created_workspace_id;
|
|
448
|
+
|
|
449
|
+
INSERT INTO public.workspace_members (workspace_id, user_id, role, status)
|
|
450
|
+
VALUES (created_workspace_id, NEW.id, 'owner', 'active')
|
|
451
|
+
ON CONFLICT (workspace_id, user_id) DO UPDATE
|
|
452
|
+
SET
|
|
453
|
+
role = EXCLUDED.role,
|
|
454
|
+
status = EXCLUDED.status,
|
|
455
|
+
updated_at = NOW();
|
|
456
|
+
|
|
457
|
+
RETURN NEW;
|
|
458
|
+
END;
|
|
459
|
+
$$;
|