@realtimex/email-automator 2.26.0 → 2.28.0
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/server.ts +19 -4
- package/api/src/routes/auth.ts +45 -18
- package/dist/api/server.js +18 -4
- package/dist/api/src/routes/auth.js +42 -17
- package/dist/assets/{index-BiE9QzV0.js → index-DpVG-8N2.js} +27 -27
- package/dist/index.html +1 -1
- package/package.json +1 -1
- package/supabase/migrations/20260117000003_add_action_constraint.sql +4 -0
- package/supabase/migrations/20260131170000_add_rule_categories.sql +65 -0
- package/supabase/migrations/20260206000010_fix_trigger_naming_conflict.sql +49 -4
- package/supabase/migrations/20260206000016_update_init_function_negative_conditions.sql +95 -78
- package/supabase/migrations/20260117000003_seed_default_rules.sql +0 -77
- package/supabase/migrations/20260120100000_fix_auth_triggers.sql +0 -44
- package/supabase/migrations/20260131110000_auto_init_user_data.sql +0 -90
- package/supabase/migrations/20260131150000_fix_trigger_error_handling.sql +0 -71
- package/supabase/migrations/20260131170000_fix_rule_categories.sql +0 -140
package/dist/index.html
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<title>Email Automator</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-DpVG-8N2.js"></script>
|
|
9
9
|
<link rel="stylesheet" crossorigin href="/assets/index-fVuZ4_5V.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
package/package.json
CHANGED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
-- Migration: Add rule categories
|
|
2
|
+
-- Adds category column and backfills existing rules
|
|
3
|
+
|
|
4
|
+
-- Step 1: Add category column to rules table if it doesn't exist
|
|
5
|
+
ALTER TABLE public.rules
|
|
6
|
+
ADD COLUMN IF NOT EXISTS category VARCHAR(50);
|
|
7
|
+
|
|
8
|
+
-- Step 2: Update existing user rules to get categories from their templates
|
|
9
|
+
UPDATE public.rules r
|
|
10
|
+
SET category = rt.category
|
|
11
|
+
FROM public.rule_templates rt
|
|
12
|
+
WHERE r.rule_template_id = rt.rule_id
|
|
13
|
+
AND r.is_system_managed = true
|
|
14
|
+
AND (r.category IS NULL OR r.category != rt.category);
|
|
15
|
+
|
|
16
|
+
-- Step 3: Create backfill function to install rules for existing users
|
|
17
|
+
CREATE OR REPLACE FUNCTION public.install_all_rules_for_user(target_user_id UUID)
|
|
18
|
+
RETURNS void AS $$
|
|
19
|
+
DECLARE
|
|
20
|
+
existing_rules_count INTEGER;
|
|
21
|
+
BEGIN
|
|
22
|
+
-- Check if user already has rules installed
|
|
23
|
+
SELECT COUNT(*) INTO existing_rules_count
|
|
24
|
+
FROM public.rules
|
|
25
|
+
WHERE user_id = target_user_id AND is_system_managed = true;
|
|
26
|
+
|
|
27
|
+
IF existing_rules_count > 0 THEN
|
|
28
|
+
RAISE NOTICE 'User % already has % system-managed rules installed', target_user_id, existing_rules_count;
|
|
29
|
+
RETURN;
|
|
30
|
+
END IF;
|
|
31
|
+
|
|
32
|
+
-- Copy ALL rules from templates with category
|
|
33
|
+
INSERT INTO public.rules (
|
|
34
|
+
user_id,
|
|
35
|
+
name,
|
|
36
|
+
intent,
|
|
37
|
+
condition,
|
|
38
|
+
action,
|
|
39
|
+
actions,
|
|
40
|
+
is_enabled,
|
|
41
|
+
category,
|
|
42
|
+
rule_template_id,
|
|
43
|
+
is_system_managed,
|
|
44
|
+
created_at
|
|
45
|
+
)
|
|
46
|
+
SELECT
|
|
47
|
+
target_user_id,
|
|
48
|
+
rt.name,
|
|
49
|
+
rt.intent,
|
|
50
|
+
rt.condition,
|
|
51
|
+
rt.actions[1],
|
|
52
|
+
rt.actions,
|
|
53
|
+
rt.is_enabled_by_default,
|
|
54
|
+
rt.category,
|
|
55
|
+
rt.rule_id,
|
|
56
|
+
true,
|
|
57
|
+
NOW()
|
|
58
|
+
FROM public.rule_templates rt
|
|
59
|
+
ORDER BY rt.category, rt.sort_order;
|
|
60
|
+
|
|
61
|
+
RAISE NOTICE '✓ Installed % rules for user %', (SELECT COUNT(*) FROM rule_templates), target_user_id;
|
|
62
|
+
END;
|
|
63
|
+
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
|
64
|
+
|
|
65
|
+
COMMENT ON FUNCTION public.install_all_rules_for_user(UUID) IS 'Backfill function to install all rules from templates for an existing user';
|
|
@@ -17,7 +17,7 @@ CREATE OR REPLACE FUNCTION public.handle_new_auth_user()
|
|
|
17
17
|
RETURNS TRIGGER
|
|
18
18
|
LANGUAGE plpgsql
|
|
19
19
|
SECURITY DEFINER
|
|
20
|
-
SET search_path
|
|
20
|
+
SET search_path TO 'pg_catalog', 'public'
|
|
21
21
|
AS $$
|
|
22
22
|
DECLARE
|
|
23
23
|
profile_count INT;
|
|
@@ -46,10 +46,11 @@ CREATE OR REPLACE FUNCTION public.handle_new_profile()
|
|
|
46
46
|
RETURNS TRIGGER
|
|
47
47
|
LANGUAGE plpgsql
|
|
48
48
|
SECURITY DEFINER
|
|
49
|
-
SET search_path
|
|
49
|
+
SET search_path TO 'pg_catalog', 'public'
|
|
50
50
|
AS $$
|
|
51
51
|
DECLARE
|
|
52
52
|
v_encryption_key TEXT;
|
|
53
|
+
v_new_encryption_key TEXT;
|
|
53
54
|
v_rules_created INTEGER := 0;
|
|
54
55
|
BEGIN
|
|
55
56
|
-- Wrap in exception handler to prevent profile creation failure
|
|
@@ -60,7 +61,17 @@ BEGIN
|
|
|
60
61
|
WHERE encryption_key IS NOT NULL
|
|
61
62
|
LIMIT 1;
|
|
62
63
|
|
|
63
|
-
--
|
|
64
|
+
-- Generate new encryption key if none exists (first user case)
|
|
65
|
+
IF v_encryption_key IS NULL THEN
|
|
66
|
+
-- Use built-in gen_random_uuid() (no extension required) - convert to 64-char hex
|
|
67
|
+
v_new_encryption_key := md5(random()::text || clock_timestamp()::text) || md5(random()::text || clock_timestamp()::text);
|
|
68
|
+
RAISE NOTICE '[handle_new_profile] Generated new encryption key for first user %', NEW.id;
|
|
69
|
+
ELSE
|
|
70
|
+
v_new_encryption_key := v_encryption_key;
|
|
71
|
+
RAISE NOTICE '[handle_new_profile] Using existing encryption key for user %', NEW.id;
|
|
72
|
+
END IF;
|
|
73
|
+
|
|
74
|
+
-- 1. Create user_settings with encryption key
|
|
64
75
|
INSERT INTO public.user_settings (
|
|
65
76
|
user_id,
|
|
66
77
|
llm_provider,
|
|
@@ -72,7 +83,7 @@ BEGIN
|
|
|
72
83
|
NEW.id,
|
|
73
84
|
'realtimexai',
|
|
74
85
|
'gpt-4o-mini',
|
|
75
|
-
|
|
86
|
+
v_new_encryption_key,
|
|
76
87
|
NOW(),
|
|
77
88
|
NOW()
|
|
78
89
|
)
|
|
@@ -154,6 +165,40 @@ GRANT EXECUTE ON FUNCTION public.handle_new_auth_user() TO service_role;
|
|
|
154
165
|
GRANT EXECUTE ON FUNCTION public.handle_new_profile() TO authenticated;
|
|
155
166
|
GRANT EXECUTE ON FUNCTION public.handle_new_profile() TO service_role;
|
|
156
167
|
|
|
168
|
+
-- ============================================================================
|
|
169
|
+
-- FIX RLS POLICIES TO ALLOW TRIGGER INSERTS
|
|
170
|
+
-- ============================================================================
|
|
171
|
+
-- Drop the overly restrictive "FOR ALL" policy
|
|
172
|
+
DROP POLICY IF EXISTS "Users can only access their own settings" ON user_settings;
|
|
173
|
+
|
|
174
|
+
-- Create granular policies that allow trigger inserts (when auth.uid() IS NULL)
|
|
175
|
+
CREATE POLICY "Users can view their own settings" ON user_settings
|
|
176
|
+
FOR SELECT USING (auth.uid() = user_id);
|
|
177
|
+
|
|
178
|
+
CREATE POLICY "Users can update their own settings" ON user_settings
|
|
179
|
+
FOR UPDATE USING (auth.uid() = user_id);
|
|
180
|
+
|
|
181
|
+
CREATE POLICY "Users can delete their own settings" ON user_settings
|
|
182
|
+
FOR DELETE USING (auth.uid() = user_id);
|
|
183
|
+
|
|
184
|
+
CREATE POLICY "Users and triggers can insert settings" ON user_settings
|
|
185
|
+
FOR INSERT WITH CHECK (auth.uid() = user_id OR auth.uid() IS NULL);
|
|
186
|
+
|
|
187
|
+
-- Fix RLS for rules table (same issue - triggers need to insert)
|
|
188
|
+
DROP POLICY IF EXISTS "Users can only access their own rules" ON rules;
|
|
189
|
+
|
|
190
|
+
CREATE POLICY "Users can view their own rules" ON rules
|
|
191
|
+
FOR SELECT USING (auth.uid() = user_id);
|
|
192
|
+
|
|
193
|
+
CREATE POLICY "Users can update their own rules" ON rules
|
|
194
|
+
FOR UPDATE USING (auth.uid() = user_id);
|
|
195
|
+
|
|
196
|
+
CREATE POLICY "Users can delete their own rules" ON rules
|
|
197
|
+
FOR DELETE USING (auth.uid() = user_id);
|
|
198
|
+
|
|
199
|
+
CREATE POLICY "Users and triggers can insert rules" ON rules
|
|
200
|
+
FOR INSERT WITH CHECK (auth.uid() = user_id OR auth.uid() IS NULL);
|
|
201
|
+
|
|
157
202
|
-- ============================================================================
|
|
158
203
|
-- COMMENTS
|
|
159
204
|
-- ============================================================================
|
|
@@ -1,104 +1,121 @@
|
|
|
1
|
-
-- Migration: Update
|
|
1
|
+
-- Migration: Update handle_new_profile() to copy negative_condition from templates
|
|
2
2
|
-- Purpose: Ensure new users get negative conditions when rules are initialized
|
|
3
3
|
--
|
|
4
4
|
-- This completes the zero-config UX by ensuring negative_condition flows from
|
|
5
5
|
-- rule_templates → user rules during signup
|
|
6
6
|
|
|
7
7
|
-- Update the function to include negative_condition
|
|
8
|
-
CREATE OR REPLACE FUNCTION public.
|
|
9
|
-
RETURNS TRIGGER
|
|
8
|
+
CREATE OR REPLACE FUNCTION public.handle_new_profile()
|
|
9
|
+
RETURNS TRIGGER
|
|
10
|
+
LANGUAGE plpgsql
|
|
11
|
+
SECURITY DEFINER
|
|
12
|
+
SET search_path TO 'pg_catalog', 'public'
|
|
13
|
+
AS $$
|
|
10
14
|
DECLARE
|
|
11
|
-
|
|
12
|
-
|
|
15
|
+
v_encryption_key TEXT;
|
|
16
|
+
v_new_encryption_key TEXT;
|
|
17
|
+
v_rules_created INTEGER := 0;
|
|
13
18
|
BEGIN
|
|
14
|
-
--
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
19
|
+
-- Wrap in exception handler to prevent profile creation failure
|
|
20
|
+
BEGIN
|
|
21
|
+
-- Get shared encryption key (sandbox mode)
|
|
22
|
+
SELECT encryption_key INTO v_encryption_key
|
|
23
|
+
FROM public.user_settings
|
|
24
|
+
WHERE encryption_key IS NOT NULL
|
|
25
|
+
LIMIT 1;
|
|
18
26
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
27
|
+
-- Generate new encryption key if none exists (first user case)
|
|
28
|
+
IF v_encryption_key IS NULL THEN
|
|
29
|
+
-- Use built-in functions (no extension required) - generates 64-char hex string
|
|
30
|
+
v_new_encryption_key := md5(random()::text || clock_timestamp()::text) || md5(random()::text || clock_timestamp()::text);
|
|
31
|
+
RAISE NOTICE '[handle_new_profile] Generated new encryption key for first user %', NEW.id;
|
|
32
|
+
ELSE
|
|
33
|
+
v_new_encryption_key := v_encryption_key;
|
|
34
|
+
RAISE NOTICE '[handle_new_profile] Using existing encryption key for user %', NEW.id;
|
|
35
|
+
END IF;
|
|
23
36
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
user_id,
|
|
27
|
-
llm_provider,
|
|
28
|
-
llm_model,
|
|
29
|
-
user_role,
|
|
30
|
-
onboarding_completed,
|
|
31
|
-
created_at,
|
|
32
|
-
updated_at
|
|
33
|
-
) VALUES (
|
|
34
|
-
NEW.id,
|
|
35
|
-
'realtimexai',
|
|
36
|
-
'gpt-4o-mini',
|
|
37
|
-
NULL,
|
|
38
|
-
FALSE,
|
|
39
|
-
NOW(),
|
|
40
|
-
NOW()
|
|
41
|
-
)
|
|
42
|
-
ON CONFLICT (user_id) DO NOTHING;
|
|
43
|
-
|
|
44
|
-
-- 2. Install ALL rules from templates
|
|
45
|
-
-- First, check if rule_templates table exists (for backwards compatibility)
|
|
46
|
-
IF EXISTS (SELECT FROM information_schema.tables WHERE table_name = 'rule_templates') THEN
|
|
47
|
-
|
|
48
|
-
-- Copy ALL rules from templates (some enabled by default, others disabled)
|
|
49
|
-
-- Now includes negative_condition, min_confidence, description, instructions, and priority
|
|
50
|
-
INSERT INTO public.rules (
|
|
37
|
+
-- 1. Create user_settings with encryption key
|
|
38
|
+
INSERT INTO public.user_settings (
|
|
51
39
|
user_id,
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
min_confidence,
|
|
59
|
-
action,
|
|
60
|
-
actions,
|
|
61
|
-
instructions,
|
|
62
|
-
is_enabled,
|
|
63
|
-
pack,
|
|
64
|
-
rule_template_id,
|
|
65
|
-
is_system_managed,
|
|
66
|
-
created_at
|
|
67
|
-
)
|
|
68
|
-
SELECT
|
|
40
|
+
llm_provider,
|
|
41
|
+
llm_model,
|
|
42
|
+
encryption_key,
|
|
43
|
+
created_at,
|
|
44
|
+
updated_at
|
|
45
|
+
) VALUES (
|
|
69
46
|
NEW.id,
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
rt.condition,
|
|
75
|
-
rt.negative_condition, -- ✅ NEW: Copy negative conditions
|
|
76
|
-
0.7, -- Default min_confidence (can be overridden by template if column added)
|
|
77
|
-
rt.actions[1], -- First action as primary (for backwards compat)
|
|
78
|
-
rt.actions, -- All actions array
|
|
79
|
-
rt.instructions, -- Draft generation instructions
|
|
80
|
-
rt.is_enabled_by_default, -- Some enabled, others disabled
|
|
81
|
-
rt.pack_id, -- Keep for backwards compat
|
|
82
|
-
rt.rule_id,
|
|
83
|
-
true, -- is_system_managed
|
|
47
|
+
'realtimexai',
|
|
48
|
+
'gpt-4o-mini',
|
|
49
|
+
v_new_encryption_key,
|
|
50
|
+
NOW(),
|
|
84
51
|
NOW()
|
|
85
|
-
|
|
86
|
-
|
|
52
|
+
)
|
|
53
|
+
ON CONFLICT (user_id) DO NOTHING;
|
|
54
|
+
|
|
55
|
+
-- 2. Install rules from templates (including negative_condition)
|
|
56
|
+
IF EXISTS (SELECT FROM information_schema.tables WHERE table_name = 'rule_templates') THEN
|
|
57
|
+
INSERT INTO public.rules (
|
|
58
|
+
user_id,
|
|
59
|
+
name,
|
|
60
|
+
description,
|
|
61
|
+
intent,
|
|
62
|
+
priority,
|
|
63
|
+
condition,
|
|
64
|
+
negative_condition,
|
|
65
|
+
min_confidence,
|
|
66
|
+
action,
|
|
67
|
+
actions,
|
|
68
|
+
instructions,
|
|
69
|
+
is_enabled,
|
|
70
|
+
category,
|
|
71
|
+
rule_template_id,
|
|
72
|
+
is_system_managed,
|
|
73
|
+
created_at
|
|
74
|
+
)
|
|
75
|
+
SELECT
|
|
76
|
+
NEW.id,
|
|
77
|
+
rt.name,
|
|
78
|
+
rt.description,
|
|
79
|
+
rt.intent,
|
|
80
|
+
rt.priority,
|
|
81
|
+
rt.condition,
|
|
82
|
+
rt.negative_condition,
|
|
83
|
+
0.7,
|
|
84
|
+
rt.actions[1],
|
|
85
|
+
rt.actions,
|
|
86
|
+
rt.instructions,
|
|
87
|
+
rt.is_enabled_by_default,
|
|
88
|
+
rt.category,
|
|
89
|
+
rt.rule_id,
|
|
90
|
+
true,
|
|
91
|
+
NOW()
|
|
92
|
+
FROM public.rule_templates rt
|
|
93
|
+
ORDER BY rt.category, rt.sort_order
|
|
94
|
+
ON CONFLICT (user_id, rule_template_id) DO NOTHING;
|
|
95
|
+
|
|
96
|
+
GET DIAGNOSTICS v_rules_created = ROW_COUNT;
|
|
97
|
+
RAISE NOTICE '✓ User % initialized: settings created, % rules installed', NEW.id, v_rules_created;
|
|
98
|
+
ELSE
|
|
99
|
+
RAISE WARNING '⚠ rule_templates table not found for user %', NEW.id;
|
|
100
|
+
END IF;
|
|
87
101
|
|
|
88
|
-
|
|
102
|
+
EXCEPTION
|
|
103
|
+
WHEN OTHERS THEN
|
|
104
|
+
RAISE WARNING '✗ handle_new_profile failed for user %: % (SQLSTATE: %)', NEW.id, SQLERRM, SQLSTATE;
|
|
105
|
+
END;
|
|
89
106
|
|
|
90
107
|
RETURN NEW;
|
|
91
108
|
END;
|
|
92
|
-
|
|
109
|
+
$$;
|
|
93
110
|
|
|
94
111
|
-- Add comment
|
|
95
|
-
COMMENT ON FUNCTION public.
|
|
112
|
+
COMMENT ON FUNCTION public.handle_new_profile() IS 'Step 2: Initializes user_settings with encryption key and installs rules from templates (including negative_condition)';
|
|
96
113
|
|
|
97
114
|
-- Log success
|
|
98
115
|
DO $$
|
|
99
116
|
BEGIN
|
|
100
|
-
RAISE NOTICE '✓ Updated
|
|
117
|
+
RAISE NOTICE '✓ Updated handle_new_profile() function to copy negative_condition from templates';
|
|
101
118
|
RAISE NOTICE ' - New users will automatically get smart exclusion logic';
|
|
102
|
-
RAISE NOTICE ' - Also copies: description, instructions, priority fields';
|
|
119
|
+
RAISE NOTICE ' - Also copies: description, instructions, priority, encryption_key fields';
|
|
103
120
|
RAISE NOTICE ' - Zero-Config UX: Complete!';
|
|
104
121
|
END $$;
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
-- Update rules action check constraint to support new actions
|
|
2
|
-
ALTER TABLE rules DROP CONSTRAINT IF EXISTS rules_action_check;
|
|
3
|
-
ALTER TABLE rules ADD CONSTRAINT rules_action_check
|
|
4
|
-
CHECK (action IN ('delete', 'archive', 'draft', 'star', 'read'));
|
|
5
|
-
|
|
6
|
-
-- Seed default rules for existing users
|
|
7
|
-
DO $$
|
|
8
|
-
DECLARE
|
|
9
|
-
user_rec RECORD;
|
|
10
|
-
BEGIN
|
|
11
|
-
FOR user_rec IN SELECT id FROM auth.users LOOP
|
|
12
|
-
-- Archive Newsletters
|
|
13
|
-
IF NOT EXISTS (SELECT 1 FROM rules WHERE user_id = user_rec.id AND name = 'Archive Newsletters') THEN
|
|
14
|
-
INSERT INTO rules (user_id, name, condition, action, is_enabled)
|
|
15
|
-
VALUES (user_rec.id, 'Archive Newsletters', '{"category": "newsletter"}', 'archive', true);
|
|
16
|
-
END IF;
|
|
17
|
-
|
|
18
|
-
-- Archive Receipts
|
|
19
|
-
IF NOT EXISTS (SELECT 1 FROM rules WHERE user_id = user_rec.id AND name = 'Archive Receipts') THEN
|
|
20
|
-
INSERT INTO rules (user_id, name, condition, action, is_enabled)
|
|
21
|
-
VALUES (user_rec.id, 'Archive Receipts', '{"category": "transactional"}', 'archive', true);
|
|
22
|
-
END IF;
|
|
23
|
-
|
|
24
|
-
-- Trash Promotions (Disabled by default)
|
|
25
|
-
IF NOT EXISTS (SELECT 1 FROM rules WHERE user_id = user_rec.id AND name = 'Trash Promotions') THEN
|
|
26
|
-
INSERT INTO rules (user_id, name, condition, action, is_enabled)
|
|
27
|
-
VALUES (user_rec.id, 'Trash Promotions', '{"category": "promotional"}', 'delete', false);
|
|
28
|
-
END IF;
|
|
29
|
-
|
|
30
|
-
-- Flag Important
|
|
31
|
-
IF NOT EXISTS (SELECT 1 FROM rules WHERE user_id = user_rec.id AND name = 'Flag Important') THEN
|
|
32
|
-
INSERT INTO rules (user_id, name, condition, action, is_enabled)
|
|
33
|
-
VALUES (user_rec.id, 'Flag Important', '{"priority": "High"}', 'star', true);
|
|
34
|
-
END IF;
|
|
35
|
-
|
|
36
|
-
-- Auto-Trash Old Newsletters (30 days)
|
|
37
|
-
IF NOT EXISTS (SELECT 1 FROM rules WHERE user_id = user_rec.id AND name = 'Auto-Trash Old Newsletters') THEN
|
|
38
|
-
INSERT INTO rules (user_id, name, condition, action, is_enabled)
|
|
39
|
-
VALUES (user_rec.id, 'Auto-Trash Old Newsletters', '{"category": "newsletter", "older_than_days": 30}', 'delete', true);
|
|
40
|
-
END IF;
|
|
41
|
-
END LOOP;
|
|
42
|
-
END $$;
|
|
43
|
-
|
|
44
|
-
-- Update trigger for new users to include default rules
|
|
45
|
-
CREATE OR REPLACE FUNCTION public.handle_new_user()
|
|
46
|
-
RETURNS TRIGGER
|
|
47
|
-
LANGUAGE plpgsql
|
|
48
|
-
SECURITY DEFINER SET search_path = ''
|
|
49
|
-
AS $$
|
|
50
|
-
DECLARE
|
|
51
|
-
profile_count int;
|
|
52
|
-
BEGIN
|
|
53
|
-
SELECT count(id) INTO profile_count
|
|
54
|
-
FROM public.profiles;
|
|
55
|
-
|
|
56
|
-
-- Create Profile
|
|
57
|
-
INSERT INTO public.profiles (id, first_name, last_name, email, is_admin)
|
|
58
|
-
VALUES (
|
|
59
|
-
new.id,
|
|
60
|
-
new.raw_user_meta_data ->> 'first_name',
|
|
61
|
-
new.raw_user_meta_data ->> 'last_name',
|
|
62
|
-
new.email,
|
|
63
|
-
CASE WHEN profile_count > 0 THEN false ELSE true END
|
|
64
|
-
);
|
|
65
|
-
|
|
66
|
-
-- Insert Default Rules
|
|
67
|
-
INSERT INTO public.rules (user_id, name, condition, action, is_enabled)
|
|
68
|
-
VALUES
|
|
69
|
-
(new.id, 'Archive Newsletters', '{"category": "newsletter"}', 'archive', true),
|
|
70
|
-
(new.id, 'Archive Receipts', '{"category": "transactional"}', 'archive', true),
|
|
71
|
-
(new.id, 'Trash Promotions', '{"category": "promotional"}', 'delete', false),
|
|
72
|
-
(new.id, 'Flag Important', '{"priority": "High"}', 'star', true),
|
|
73
|
-
(new.id, 'Auto-Trash Old Newsletters', '{"category": "newsletter", "older_than_days": 30}', 'delete', true);
|
|
74
|
-
|
|
75
|
-
RETURN new;
|
|
76
|
-
END;
|
|
77
|
-
$$;
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
-- Fix Auth triggers by setting robust search_path
|
|
2
|
-
-- This resolves "Database error creating new user" (unexpected_failure) during setup
|
|
3
|
-
|
|
4
|
-
CREATE OR REPLACE FUNCTION public.handle_new_user()
|
|
5
|
-
RETURNS TRIGGER
|
|
6
|
-
LANGUAGE plpgsql
|
|
7
|
-
SECURITY DEFINER SET search_path = public
|
|
8
|
-
AS $$
|
|
9
|
-
DECLARE
|
|
10
|
-
profile_count int;
|
|
11
|
-
BEGIN
|
|
12
|
-
SELECT count(id) INTO profile_count
|
|
13
|
-
FROM public.profiles;
|
|
14
|
-
|
|
15
|
-
INSERT INTO public.profiles (id, first_name, last_name, email, is_admin)
|
|
16
|
-
VALUES (
|
|
17
|
-
new.id,
|
|
18
|
-
new.raw_user_meta_data ->> 'first_name',
|
|
19
|
-
new.raw_user_meta_data ->> 'last_name',
|
|
20
|
-
new.email,
|
|
21
|
-
-- If it's the first user, make them admin. Otherwise, standard user.
|
|
22
|
-
CASE WHEN profile_count > 0 THEN false ELSE true END
|
|
23
|
-
);
|
|
24
|
-
RETURN new;
|
|
25
|
-
END;
|
|
26
|
-
$$;
|
|
27
|
-
|
|
28
|
-
CREATE OR REPLACE FUNCTION public.handle_update_user()
|
|
29
|
-
RETURNS TRIGGER
|
|
30
|
-
LANGUAGE plpgsql
|
|
31
|
-
SECURITY DEFINER SET search_path = public
|
|
32
|
-
AS $$
|
|
33
|
-
BEGIN
|
|
34
|
-
UPDATE public.profiles
|
|
35
|
-
SET
|
|
36
|
-
first_name = new.raw_user_meta_data ->> 'first_name',
|
|
37
|
-
last_name = new.raw_user_meta_data ->> 'last_name',
|
|
38
|
-
email = new.email,
|
|
39
|
-
updated_at = now()
|
|
40
|
-
WHERE id = new.id;
|
|
41
|
-
|
|
42
|
-
RETURN new;
|
|
43
|
-
END;
|
|
44
|
-
$$;
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
-- Migration: Auto-initialize user data on signup
|
|
2
|
-
-- Creates user_settings record and installs Universal Pack automatically
|
|
3
|
-
|
|
4
|
-
-- Function to initialize user settings and install Universal Pack
|
|
5
|
-
CREATE OR REPLACE FUNCTION public.handle_new_user()
|
|
6
|
-
RETURNS TRIGGER AS $$
|
|
7
|
-
DECLARE
|
|
8
|
-
pack_install_id UUID;
|
|
9
|
-
user_exists BOOLEAN;
|
|
10
|
-
BEGIN
|
|
11
|
-
-- Check if user exists in auth.users to avoid foreign key violation
|
|
12
|
-
SELECT EXISTS (
|
|
13
|
-
SELECT 1 FROM auth.users WHERE id = NEW.id
|
|
14
|
-
) INTO user_exists;
|
|
15
|
-
|
|
16
|
-
IF NOT user_exists THEN
|
|
17
|
-
RAISE NOTICE 'User % does not exist in auth.users yet, skipping initialization', NEW.id;
|
|
18
|
-
RETURN NEW;
|
|
19
|
-
END IF;
|
|
20
|
-
-- 1. Create user_settings record with defaults (with conflict handling)
|
|
21
|
-
INSERT INTO public.user_settings (
|
|
22
|
-
user_id,
|
|
23
|
-
llm_provider,
|
|
24
|
-
llm_model,
|
|
25
|
-
user_role,
|
|
26
|
-
onboarding_completed,
|
|
27
|
-
created_at,
|
|
28
|
-
updated_at
|
|
29
|
-
) VALUES (
|
|
30
|
-
NEW.id,
|
|
31
|
-
'realtimexai',
|
|
32
|
-
'gpt-4o-mini',
|
|
33
|
-
NULL,
|
|
34
|
-
FALSE,
|
|
35
|
-
NOW(),
|
|
36
|
-
NOW()
|
|
37
|
-
)
|
|
38
|
-
ON CONFLICT (user_id) DO NOTHING;
|
|
39
|
-
|
|
40
|
-
-- 2. Install ALL rules from templates
|
|
41
|
-
-- First, check if rule_templates table exists (for backwards compatibility)
|
|
42
|
-
IF EXISTS (SELECT FROM information_schema.tables WHERE table_name = 'rule_templates') THEN
|
|
43
|
-
|
|
44
|
-
-- Copy ALL rules from templates (some enabled by default, others disabled)
|
|
45
|
-
INSERT INTO public.rules (
|
|
46
|
-
user_id,
|
|
47
|
-
name,
|
|
48
|
-
intent,
|
|
49
|
-
condition,
|
|
50
|
-
action,
|
|
51
|
-
actions,
|
|
52
|
-
is_enabled,
|
|
53
|
-
pack,
|
|
54
|
-
rule_template_id,
|
|
55
|
-
is_system_managed,
|
|
56
|
-
created_at
|
|
57
|
-
)
|
|
58
|
-
SELECT
|
|
59
|
-
NEW.id,
|
|
60
|
-
rt.name,
|
|
61
|
-
rt.intent,
|
|
62
|
-
rt.condition,
|
|
63
|
-
rt.actions[1], -- First action as primary (for backwards compat)
|
|
64
|
-
rt.actions, -- All actions array
|
|
65
|
-
rt.is_enabled_by_default, -- Some enabled, others disabled
|
|
66
|
-
rt.pack_id, -- Keep for backwards compat
|
|
67
|
-
rt.rule_id,
|
|
68
|
-
true, -- is_system_managed
|
|
69
|
-
NOW()
|
|
70
|
-
FROM public.rule_templates rt
|
|
71
|
-
ORDER BY rt.pack_id, rt.sort_order;
|
|
72
|
-
|
|
73
|
-
END IF;
|
|
74
|
-
|
|
75
|
-
RETURN NEW;
|
|
76
|
-
END;
|
|
77
|
-
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
|
78
|
-
|
|
79
|
-
-- Trigger to call the function when a new profile is created
|
|
80
|
-
DROP TRIGGER IF EXISTS on_profile_created ON public.profiles;
|
|
81
|
-
CREATE TRIGGER on_profile_created
|
|
82
|
-
AFTER INSERT ON public.profiles
|
|
83
|
-
FOR EACH ROW
|
|
84
|
-
EXECUTE FUNCTION public.handle_new_user();
|
|
85
|
-
|
|
86
|
-
-- Grant necessary permissions
|
|
87
|
-
GRANT EXECUTE ON FUNCTION public.handle_new_user() TO authenticated;
|
|
88
|
-
|
|
89
|
-
-- Comment
|
|
90
|
-
COMMENT ON FUNCTION public.handle_new_user() IS 'Automatically creates user_settings and installs Universal Pack when a new user signs up';
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
-- Migration: Add error handling to user initialization trigger
|
|
2
|
-
-- Prevents user creation from failing if trigger has issues
|
|
3
|
-
|
|
4
|
-
-- Recreate function with exception handling
|
|
5
|
-
CREATE OR REPLACE FUNCTION public.handle_new_user()
|
|
6
|
-
RETURNS TRIGGER AS $$
|
|
7
|
-
BEGIN
|
|
8
|
-
-- Wrap everything in exception handler to prevent user creation failure
|
|
9
|
-
BEGIN
|
|
10
|
-
-- 1. Create user_settings record with defaults
|
|
11
|
-
INSERT INTO public.user_settings (
|
|
12
|
-
user_id,
|
|
13
|
-
llm_provider,
|
|
14
|
-
llm_model,
|
|
15
|
-
user_role,
|
|
16
|
-
onboarding_completed,
|
|
17
|
-
created_at,
|
|
18
|
-
updated_at
|
|
19
|
-
) VALUES (
|
|
20
|
-
NEW.id,
|
|
21
|
-
'realtimexai',
|
|
22
|
-
'gpt-4o-mini',
|
|
23
|
-
NULL,
|
|
24
|
-
FALSE,
|
|
25
|
-
NOW(),
|
|
26
|
-
NOW()
|
|
27
|
-
)
|
|
28
|
-
ON CONFLICT (user_id) DO NOTHING;
|
|
29
|
-
|
|
30
|
-
-- 2. Install ALL rules from templates
|
|
31
|
-
IF EXISTS (SELECT FROM information_schema.tables WHERE table_name = 'rule_templates') THEN
|
|
32
|
-
INSERT INTO public.rules (
|
|
33
|
-
user_id,
|
|
34
|
-
name,
|
|
35
|
-
intent,
|
|
36
|
-
condition,
|
|
37
|
-
action,
|
|
38
|
-
actions,
|
|
39
|
-
is_enabled,
|
|
40
|
-
pack,
|
|
41
|
-
rule_template_id,
|
|
42
|
-
is_system_managed,
|
|
43
|
-
created_at
|
|
44
|
-
)
|
|
45
|
-
SELECT
|
|
46
|
-
NEW.id,
|
|
47
|
-
rt.name,
|
|
48
|
-
rt.intent,
|
|
49
|
-
rt.condition,
|
|
50
|
-
rt.actions[1],
|
|
51
|
-
rt.actions,
|
|
52
|
-
rt.is_enabled_by_default,
|
|
53
|
-
rt.pack_id,
|
|
54
|
-
rt.rule_id,
|
|
55
|
-
true,
|
|
56
|
-
NOW()
|
|
57
|
-
FROM public.rule_templates rt
|
|
58
|
-
ORDER BY rt.pack_id, rt.sort_order;
|
|
59
|
-
END IF;
|
|
60
|
-
|
|
61
|
-
EXCEPTION
|
|
62
|
-
WHEN OTHERS THEN
|
|
63
|
-
-- Log error but don't fail user creation
|
|
64
|
-
RAISE WARNING 'Failed to initialize user data for %: % (SQLSTATE: %)', NEW.id, SQLERRM, SQLSTATE;
|
|
65
|
-
END;
|
|
66
|
-
|
|
67
|
-
RETURN NEW;
|
|
68
|
-
END;
|
|
69
|
-
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
|
70
|
-
|
|
71
|
-
COMMENT ON FUNCTION public.handle_new_user() IS 'Automatically creates user_settings and installs rules when a new user signs up (with error handling to prevent user creation failure)';
|