@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/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-BiE9QzV0.js"></script>
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@realtimex/email-automator",
3
- "version": "2.26.0",
3
+ "version": "2.28.0",
4
4
  "type": "module",
5
5
  "main": "dist/api/server.js",
6
6
  "bin": {
@@ -0,0 +1,4 @@
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', 'label', 'forward'));
@@ -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 = public
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 = public
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
- -- 1. Create user_settings
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
- v_encryption_key,
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 handle_new_user() to copy negative_condition from templates
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.handle_new_user()
9
- RETURNS TRIGGER AS $$
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
- pack_install_id UUID;
12
- user_exists BOOLEAN;
15
+ v_encryption_key TEXT;
16
+ v_new_encryption_key TEXT;
17
+ v_rules_created INTEGER := 0;
13
18
  BEGIN
14
- -- Check if user exists in auth.users to avoid foreign key violation
15
- SELECT EXISTS (
16
- SELECT 1 FROM auth.users WHERE id = NEW.id
17
- ) INTO user_exists;
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
- IF NOT user_exists THEN
20
- RAISE NOTICE 'User % does not exist in auth.users yet, skipping initialization', NEW.id;
21
- RETURN NEW;
22
- END IF;
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
- -- 1. Create user_settings record with defaults (with conflict handling)
25
- INSERT INTO public.user_settings (
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
- name,
53
- description,
54
- intent,
55
- priority,
56
- condition,
57
- negative_condition,
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
- rt.name,
71
- rt.description,
72
- rt.intent,
73
- rt.priority,
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
- FROM public.rule_templates rt
86
- ORDER BY rt.pack_id, rt.sort_order;
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
- END IF;
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
- $$ LANGUAGE plpgsql SECURITY DEFINER;
109
+ $$;
93
110
 
94
111
  -- Add comment
95
- COMMENT ON FUNCTION public.handle_new_user() IS 'Automatically creates user_settings and installs rules from templates (including negative_condition) when a new user signs up';
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 handle_new_user() function to copy negative_condition from templates';
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)';