spine-framework 0.3.133 → 0.3.134

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.
@@ -175,12 +175,13 @@ export const checkDomain = createHandler(async (_ctx, _body) => {
175
175
 
176
176
  const { data } = await adminDb
177
177
  .from('people')
178
- .select('id')
178
+ .select('account_id')
179
179
  .ilike('email', `%@${domain}`)
180
180
  .eq('status', 'active')
181
181
  .limit(1)
182
182
 
183
- return { match: (data?.length ?? 0) > 0 }
183
+ const first = data?.[0]
184
+ return { match: !!first, account_id: first?.account_id ?? null }
184
185
  })
185
186
  // CIL[END chunk_id="invites_checkdomain_1783968140"]
186
187
  // CIL[BEGIN chunk_id="invites_validatetoken_1783968140" type="function" identity="validateToken" parent="none"]
@@ -0,0 +1,82 @@
1
+ -- Create trigger function to sync new auth users to people records
2
+ CREATE OR REPLACE FUNCTION public.handle_new_auth_user()
3
+ RETURNS TRIGGER LANGUAGE plpgsql SECURITY DEFINER SET search_path = public AS $$
4
+ DECLARE
5
+ v_account_id uuid;
6
+ v_type_id uuid;
7
+ v_role_id uuid;
8
+ v_is_first boolean;
9
+ v_design_schema jsonb;
10
+ v_validation_schema jsonb;
11
+ v_stub_id uuid;
12
+ v_stub_status text;
13
+ BEGIN
14
+ SELECT id INTO v_account_id FROM public.accounts WHERE slug = 'spine-system' LIMIT 1;
15
+ SELECT id, design_schema, validation_schema
16
+ INTO v_type_id, v_design_schema, v_validation_schema
17
+ FROM public.types WHERE kind = 'person' AND slug = 'person' LIMIT 1;
18
+
19
+ IF v_account_id IS NULL OR v_type_id IS NULL THEN
20
+ RAISE WARNING 'handle_new_auth_user: spine-system account or person type not found — skipping';
21
+ RETURN NEW;
22
+ END IF;
23
+
24
+ SELECT id, status
25
+ INTO v_stub_id, v_stub_status
26
+ FROM public.people
27
+ WHERE email = NEW.email
28
+ AND status IN ('invited', 'pending')
29
+ AND auth_uid IS NULL
30
+ LIMIT 1;
31
+
32
+ IF v_stub_id IS NOT NULL THEN
33
+ UPDATE public.people SET
34
+ auth_uid = NEW.id,
35
+ full_name = COALESCE(NEW.raw_user_meta_data->>'full_name', full_name, split_part(NEW.email, '@', 1)),
36
+ status = CASE WHEN v_stub_status = 'invited' THEN 'active' ELSE 'pending' END,
37
+ updated_at = now()
38
+ WHERE id = v_stub_id;
39
+
40
+ RETURN NEW;
41
+ END IF;
42
+
43
+ SELECT NOT EXISTS (SELECT 1 FROM public.people LIMIT 1) INTO v_is_first;
44
+ IF v_is_first THEN
45
+ SELECT id INTO v_role_id FROM public.roles WHERE slug = 'system_admin' LIMIT 1;
46
+ END IF;
47
+
48
+ INSERT INTO public.people (
49
+ id,
50
+ auth_uid,
51
+ email,
52
+ full_name,
53
+ account_id,
54
+ type_id,
55
+ role_id,
56
+ status,
57
+ is_active,
58
+ design_schema,
59
+ validation_schema
60
+ ) VALUES (
61
+ NEW.id,
62
+ NEW.id,
63
+ NEW.email,
64
+ COALESCE(NEW.raw_user_meta_data->>'full_name', split_part(NEW.email, '@', 1)),
65
+ v_account_id,
66
+ v_type_id,
67
+ v_role_id,
68
+ 'active',
69
+ true,
70
+ v_design_schema,
71
+ COALESCE(v_validation_schema, '{}'::jsonb)
72
+ )
73
+ ON CONFLICT (id) DO NOTHING;
74
+
75
+ RETURN NEW;
76
+ END;
77
+ $$;
78
+
79
+ DROP TRIGGER IF EXISTS on_auth_user_created ON auth.users;
80
+ CREATE TRIGGER on_auth_user_created
81
+ AFTER INSERT ON auth.users
82
+ FOR EACH ROW EXECUTE FUNCTION public.handle_new_auth_user();
@@ -262,8 +262,8 @@ export function RegisterPage() {
262
262
  const result = await callInvitesApi('check-domain', { email })
263
263
  setDomainCheckLoading(false)
264
264
 
265
- if (result.data?.match) {
266
- setDomainMatchAccountId('__matched__')
265
+ if (result.data?.match && result.data?.account_id) {
266
+ setDomainMatchAccountId(result.data.account_id)
267
267
  }
268
268
  }, [email, inviteToken, useType, isFirstUser])
269
269
 
@@ -323,23 +323,22 @@ export function RegisterPage() {
323
323
 
324
324
  const completeRegistrationParams: any = {
325
325
  email: resolvedEmail,
326
- full_name: fullName,
327
- app: effectiveApp,
328
- role: effectiveRole,
329
- redirect: getPostRegistrationRedirect(regConfig, searchParams, false, effectiveRole),
326
+ path: 'new_account',
327
+ role_slug: effectiveRole,
330
328
  }
331
329
 
332
330
  if (useType === 'company') {
333
- completeRegistrationParams.account_name = companyName
331
+ completeRegistrationParams.account_name = companyName || fullName || email.split('@')[0]
332
+ } else {
333
+ completeRegistrationParams.account_name = fullName || email.split('@')[0]
334
334
  }
335
335
 
336
336
  if (effectiveAccountStrategy === 'existing' && targetAccount) {
337
- completeRegistrationParams.account_id = targetAccount
338
- completeRegistrationParams.strategy = 'join_request'
339
- } else if (domainMatchAccountId === '__matched__' && domainMatchChoice === 'join') {
340
- completeRegistrationParams.strategy = 'join_request'
341
- } else {
342
- completeRegistrationParams.strategy = 'new_account'
337
+ completeRegistrationParams.path = 'existing_account'
338
+ completeRegistrationParams.target_account = targetAccount
339
+ } else if (domainMatchAccountId && domainMatchChoice === 'join') {
340
+ completeRegistrationParams.path = 'join_request'
341
+ completeRegistrationParams.target_account_id = domainMatchAccountId
343
342
  }
344
343
 
345
344
  const result = await callInvitesApi('complete-registration', completeRegistrationParams, 'POST')
@@ -367,7 +366,7 @@ export function RegisterPage() {
367
366
  const isInvite = !!inviteToken && !tokenError
368
367
  const showPersonalCompanyToggle = !isInvite && !isFirstUser
369
368
  const showCompanyName = showPersonalCompanyToggle && useType === 'company'
370
- const showDomainMatchChoice = domainMatchAccountId === '__matched__' && !isInvite && !isFirstUser
369
+ const showDomainMatchChoice = !!domainMatchAccountId && !isInvite && !isFirstUser
371
370
 
372
371
  if (tokenLoading || isFirstUser === null || (!isFirstUser && !isInvite && !regConfig)) {
373
372
  return (
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spine-framework",
3
- "version": "0.3.133",
3
+ "version": "0.3.134",
4
4
  "description": "Multi-tenant, modular application platform for modern SaaS systems",
5
5
  "type": "module",
6
6
  "bin": {
@@ -25,6 +25,7 @@
25
25
  ".framework/migrations/007_kb_article_list.sql",
26
26
  ".framework/migrations/008_items_target.sql",
27
27
  ".framework/migrations/009_roles_slug_unique.sql",
28
+ ".framework/migrations/010_auth_user_trigger.sql",
28
29
  ".framework/public/",
29
30
  ".framework/scripts/",
30
31
  ".framework/seeds/",