@fayz-ai/db 0.13.0 → 0.14.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.
Files changed (187) hide show
  1. package/canonical-schema.json +106 -23
  2. package/dist/index.cjs +4 -4
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.ts +1 -1
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +4 -4
  7. package/dist/index.js.map +1 -1
  8. package/dist/schema/spine.d.ts +42 -37
  9. package/dist/schema/spine.d.ts.map +1 -1
  10. package/migrations/000_baseline.sql +29667 -0
  11. package/migrations/001_the_tenant_has_apps.sql +314 -0
  12. package/migrations/002_the_spine_drops_the_vendor_and_the_dead.sql +129 -0
  13. package/migrations/003_the_role_rpcs_finish_and_the_mirrors_go.sql +135 -0
  14. package/migrations/004_the_projections_get_a_successor.sql +312 -0
  15. package/migrations/005_the_archetypes_anchor_to_the_unit.sql +90 -0
  16. package/migrations/006_the_helpers_read_the_source.sql +153 -0
  17. package/migrations/007_the_compat_machinery_leaves_with_the_tables.sql +77 -0
  18. package/migrations/008_the_switchers_see_across_accounts.sql +150 -0
  19. package/migrations/009_the_compat_aliases_nobody_adopted.sql +57 -0
  20. package/migrations/010_the_new_tenant_is_born_with_its_trade.sql +201 -0
  21. package/migrations/011_leadcontrol_joins_the_family.sql +116 -0
  22. package/migrations/012_chefcontrol_wears_its_own_mark.sql +28 -0
  23. package/migrations/013_the_workspace_switch_reaches_the_other_half.sql +156 -0
  24. package/migrations/014_the_chooser_can_read_what_it_offers.sql +91 -0
  25. package/migrations/015_the_unit_bindings_belong_to_one_workspace.sql +86 -0
  26. package/migrations/016_the_rule_stops_asking_every_row.sql +543 -0
  27. package/migrations/017_the_pin_is_checked_against_the_right_workspace.sql +139 -0
  28. package/migrations/018_the_bootstrap_says_what_you_reach.sql +114 -0
  29. package/migrations/019_distributing_one_row_stops_costing_the_table.sql +245 -0
  30. package/migrations/020_the_agenda_asks_where_someone_works.sql +81 -0
  31. package/migrations/022_the_line_says_which_document_it_belongs_to.sql +109 -0
  32. package/package.json +1 -1
  33. package/migrations/0000_legacy_quarantine.sql +0 -86
  34. package/migrations/0000b_straggler_sweep.sql +0 -37
  35. package/migrations/000_core_v1_convert.sql +0 -343
  36. package/migrations/001_core.sql +0 -372
  37. package/migrations/002_rls_user_tenant_ids.sql +0 -64
  38. package/migrations/003_invitations.sql +0 -17
  39. package/migrations/004_archetypes.sql +0 -343
  40. package/migrations/005_locations_archetype.sql +0 -18
  41. package/migrations/006_archetype_rls.sql +0 -74
  42. package/migrations/007_archetype_grants.sql +0 -4
  43. package/migrations/008_grants.sql +0 -5
  44. package/migrations/009_anon_hardening.sql +0 -19
  45. package/migrations/010_migration_ledger.sql +0 -46
  46. package/migrations/011_anon_write_revoke.sql +0 -12
  47. package/migrations/012_rename_migration_ledger.sql +0 -61
  48. package/migrations/013_sequences_rls.sql +0 -22
  49. package/migrations/014_plan_entitlements.sql +0 -28
  50. package/migrations/015_agent_guard.sql +0 -108
  51. package/migrations/016_agent_guard_actor.sql +0 -102
  52. package/migrations/017_core_addresses.sql +0 -115
  53. package/migrations/018_global_search.sql +0 -482
  54. package/migrations/019_tenant_members_person_id.sql +0 -20
  55. package/migrations/020_fayz_projects.sql +0 -63
  56. package/migrations/021_tenant_roles.sql +0 -74
  57. package/migrations/022_invite_acceptance.sql +0 -91
  58. package/migrations/023_team_visibility.sql +0 -75
  59. package/migrations/024_analytics_engine.sql +0 -317
  60. package/migrations/025_created_by.sql +0 -75
  61. package/migrations/026_audit_trail.sql +0 -73
  62. package/migrations/027_domain_events.sql +0 -266
  63. package/migrations/028_tenant_scoped_token.sql +0 -127
  64. package/migrations/029_connections.sql +0 -186
  65. package/migrations/030_effect_idempotency.sql +0 -159
  66. package/migrations/031_sync_run_message.sql +0 -39
  67. package/migrations/032_connection_secrets.sql +0 -227
  68. package/migrations/033_sync_schedule.sql +0 -651
  69. package/migrations/034_custom_fields.sql +0 -55
  70. package/migrations/035_field_registry.sql +0 -148
  71. package/migrations/036_analytics_run_batch.sql +0 -84
  72. package/migrations/037_sync_tick_one_at_a_time.sql +0 -256
  73. package/migrations/038_onboarding_responses.sql +0 -103
  74. package/migrations/039_unit_tree.sql +0 -270
  75. package/migrations/040_resource_grants.sql +0 -474
  76. package/migrations/041_scoped_columns.sql +0 -192
  77. package/migrations/042_unit_scope_policies.sql +0 -145
  78. package/migrations/043_view_invoker.sql +0 -90
  79. package/migrations/044_unit_member_facts.sql +0 -47
  80. package/migrations/045_unit_entry.sql +0 -236
  81. package/migrations/046_membership_visible_to_members.sql +0 -85
  82. package/migrations/047_tasks.sql +0 -266
  83. package/migrations/048_every_login_is_a_person.sql +0 -190
  84. package/migrations/049_bookable_people.sql +0 -126
  85. package/migrations/050_authz_core.sql +0 -230
  86. package/migrations/051_authz_helpers.sql +0 -330
  87. package/migrations/052_authz_fanout.sql +0 -253
  88. package/migrations/053_authz_catalog_templates.sql +0 -402
  89. package/migrations/060_tenancy_seam.sql +0 -132
  90. package/migrations/061_create_tenant.sql +0 -125
  91. package/migrations/062_invitations.sql +0 -329
  92. package/migrations/063_legacy_tenancy_retired.sql +0 -568
  93. package/migrations/064_agent_guard_authz.sql +0 -118
  94. package/migrations/064b_supersede_unit_scope.sql +0 -205
  95. package/migrations/065_scaffold_sweep.sql +0 -139
  96. package/migrations/066_shadow_report.sql +0 -122
  97. package/migrations/070_permission_catalog.sql +0 -205
  98. package/migrations/071_v1_permission_map.sql +0 -266
  99. package/migrations/072_role_rpcs.sql +0 -126
  100. package/migrations/073_legacy_rbac_retire.sql +0 -471
  101. package/migrations/075_company_legal_entities.sql +0 -169
  102. package/migrations/076_tenant_settings.sql +0 -251
  103. package/migrations/080_people_roles.sql +0 -269
  104. package/migrations/081_people_identity.sql +0 -271
  105. package/migrations/082_people_membership_link.sql +0 -142
  106. package/migrations/083_people_data_migration.sql +0 -75
  107. package/migrations/084_people_registrations_tokens.sql +0 -282
  108. package/migrations/085_catalog_products.sql +0 -333
  109. package/migrations/086_catalog_pricing.sql +0 -194
  110. package/migrations/087_catalog_packages.sql +0 -134
  111. package/migrations/090_orders_venda.sql +0 -166
  112. package/migrations/091_orders_scaffold.sql +0 -92
  113. package/migrations/095_config_field_rules.sql +0 -261
  114. package/migrations/096_audit_append_only.sql +0 -256
  115. package/migrations/097_sequences_authorized.sql +0 -111
  116. package/migrations/098_holidays.sql +0 -80
  117. package/migrations/099_access_grants.sql +0 -293
  118. package/migrations/100_migration_schema.sql +0 -217
  119. package/migrations/101_migration_allowlist.sql +0 -202
  120. package/migrations/102_migration_rpcs.sql +0 -933
  121. package/migrations/103_migration_reconcile_ponr.sql +0 -132
  122. package/migrations/105_policies_to_authenticated.sql +0 -71
  123. package/migrations/106_function_search_path.sql +0 -55
  124. package/migrations/107_core_table_prefix.sql +0 -361
  125. package/migrations/108_authz_write_guards.sql +0 -149
  126. package/migrations/109_catalog_migration_promoted_columns.sql +0 -126
  127. package/migrations/110_people_kind_vocabulary.sql +0 -120
  128. package/migrations/111_migration_excluded_is_a_decision.sql +0 -259
  129. package/migrations/111_replacing_a_view_keeps_its_dependents.sql +0 -132
  130. package/migrations/112_money_precision_standard.sql +0 -132
  131. package/migrations/113_addresses_units_companies.sql +0 -114
  132. package/migrations/114_migrations_ledger_immutable.sql +0 -65
  133. package/migrations/115_registration_honours_field_rules.sql +0 -172
  134. package/migrations/116_field_rules_check_needs_a_tenant.sql +0 -72
  135. package/migrations/117_authz_reconciliation_schedule.sql +0 -82
  136. package/migrations/118_migration_catalog_writers.sql +0 -450
  137. package/migrations/119_migration_config_writers.sql +0 -193
  138. package/migrations/120_migration_facet_writer.sql +0 -139
  139. package/migrations/121_migration_financial_writers.sql +0 -294
  140. package/migrations/122_migration_inventory_writers.sql +0 -245
  141. package/migrations/123_migration_order_item_writer.sql +0 -144
  142. package/migrations/124_migration_ledger_writers.sql +0 -170
  143. package/migrations/125_migration_authz_people_writers.sql +0 -205
  144. package/migrations/126_owner_scoping_is_configuration.sql +0 -247
  145. package/migrations/127_scaffold_audit_hook.sql +0 -98
  146. package/migrations/128_shadow_tables_enforced.sql +0 -93
  147. package/migrations/129_order_kind_terminal_states.sql +0 -57
  148. package/migrations/130_every_login_is_a_person.sql +0 -196
  149. package/migrations/131_migration_invoice_split.sql +0 -127
  150. package/migrations/132_audit_prune_scheduled.sql +0 -28
  151. package/migrations/133_unit_is_a_session_property.sql +0 -155
  152. package/migrations/134_field_rules_on_read.sql +0 -341
  153. package/migrations/135_distribution_polarity.sql +0 -158
  154. package/migrations/136_unit_id_means_locality.sql +0 -358
  155. package/migrations/137_vendor_named_spine_objects.sql +0 -126
  156. package/migrations/138_grants_inherit_down_the_tree.sql +0 -178
  157. package/migrations/139_distributable_is_a_scaffold_option.sql +0 -109
  158. package/migrations/140_masking_is_a_scaffold_option.sql +0 -85
  159. package/migrations/141_renamed_objects_keep_a_compat_name.sql +0 -192
  160. package/migrations/142_a_person_has_addresses.sql +0 -212
  161. package/migrations/143_a_professional_has_a_rate.sql +0 -75
  162. package/migrations/144_a_product_can_be_sold_in_sizes.sql +0 -57
  163. package/migrations/145_the_app_says_which_tenant_it_is_in.sql +0 -123
  164. package/migrations/146_products_stock_has_one_writer.sql +0 -67
  165. package/migrations/147_every_archetype_has_a_kind_registry.sql +0 -388
  166. package/migrations/148_the_legacy_archetype_stops_being_writable.sql +0 -154
  167. package/migrations/149_the_event_log_gets_a_consumer.sql +0 -331
  168. package/migrations/150_the_app_is_configured_in_the_database.sql +0 -285
  169. package/migrations/151_stock_balance_is_an_archetype.sql +0 -144
  170. package/migrations/152_a_file_is_an_archetype.sql +0 -222
  171. package/migrations/153_a_plugin_calls_a_service_through_one_door.sql +0 -199
  172. package/migrations/154_the_plan_is_a_ceiling.sql +0 -258
  173. package/migrations/155_a_tenant_can_leave.sql +0 -271
  174. package/migrations/156_the_service_door_has_a_server_side.sql +0 -335
  175. package/migrations/157_nobody_creates_objects_in_public.sql +0 -74
  176. package/migrations/158_the_registry_owns_the_status_vocabulary.sql +0 -153
  177. package/migrations/159_the_plan_catalogue_gets_its_first_rows.sql +0 -132
  178. package/migrations/160_the_vertical_decides_what_starts_on.sql +0 -246
  179. package/migrations/161_a_tenant_leaves_with_its_data.sql +0 -363
  180. package/migrations/162_every_name_has_one_creator_for_real.sql +0 -126
  181. package/migrations/163_a_species_nobody_declared_goes_to_quarantine.sql +0 -184
  182. package/migrations/164_the_sequence_table_joins_the_register.sql +0 -26
  183. package/migrations/165_a_view_is_not_a_way_in.sql +0 -90
  184. package/migrations/166_the_platform_billing_leftovers_go.sql +0 -72
  185. package/migrations/167_a_view_gets_back_what_its_table_grants.sql +0 -112
  186. package/migrations/168_the_grant_census_stops_crying_wolf.sql +0 -33
  187. package/migrations/169_the_census_asks_postgres_instead_of_guessing.sql +0 -45
@@ -1,372 +0,0 @@
1
- -- core: minimal core schema for each SaaS project (industry-pool model)
2
- -- All core entities live directly in the PUBLIC schema — no saas_core schema.
3
- -- Plugin registry, verticals, billing live in Fayz platform DB — not here.
4
-
5
- CREATE EXTENSION IF NOT EXISTS pgcrypto;
6
-
7
- -- Tenants (organizations)
8
- CREATE TABLE IF NOT EXISTS public.tenants (
9
- id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
10
- name text NOT NULL,
11
- slug text UNIQUE NOT NULL,
12
- logo_url text,
13
- plan text NOT NULL DEFAULT 'free',
14
- vertical_id text,
15
- settings jsonb NOT NULL DEFAULT '{}',
16
- created_at timestamptz NOT NULL DEFAULT now(),
17
- updated_at timestamptz NOT NULL DEFAULT now()
18
- );
19
-
20
- -- Profiles (extends auth.users)
21
- CREATE TABLE IF NOT EXISTS public.profiles (
22
- id uuid PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
23
- email text,
24
- full_name text,
25
- avatar_url text,
26
- created_at timestamptz NOT NULL DEFAULT now(),
27
- updated_at timestamptz NOT NULL DEFAULT now()
28
- );
29
-
30
- -- Tenant members (org membership + role)
31
- CREATE TABLE IF NOT EXISTS public.tenant_members (
32
- id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
33
- tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
34
- user_id uuid NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
35
- role text NOT NULL DEFAULT 'owner',
36
- created_at timestamptz NOT NULL DEFAULT now(),
37
- UNIQUE(tenant_id, user_id)
38
- );
39
-
40
- -- Updated_at trigger
41
- CREATE OR REPLACE FUNCTION public.handle_updated_at()
42
- RETURNS trigger LANGUAGE plpgsql AS $$
43
- BEGIN NEW.updated_at = now(); RETURN NEW; END; $$;
44
-
45
- DROP TRIGGER IF EXISTS tenants_updated_at ON public.tenants;
46
- CREATE TRIGGER tenants_updated_at BEFORE UPDATE ON public.tenants
47
- FOR EACH ROW EXECUTE FUNCTION public.handle_updated_at();
48
- DROP TRIGGER IF EXISTS profiles_updated_at ON public.profiles;
49
- CREATE TRIGGER profiles_updated_at BEFORE UPDATE ON public.profiles
50
- FOR EACH ROW EXECUTE FUNCTION public.handle_updated_at();
51
-
52
- -- Auto-create profile on signup
53
- CREATE OR REPLACE FUNCTION public.handle_new_user()
54
- RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER AS $$
55
- BEGIN
56
- INSERT INTO public.profiles (id, email, full_name, avatar_url)
57
- VALUES (NEW.id, NEW.email, NEW.raw_user_meta_data->>'full_name', NEW.raw_user_meta_data->>'avatar_url')
58
- ON CONFLICT (id) DO NOTHING;
59
- RETURN NEW;
60
- END; $$;
61
-
62
- DROP TRIGGER IF EXISTS on_auth_user_created ON auth.users;
63
- CREATE TRIGGER on_auth_user_created AFTER INSERT ON auth.users
64
- FOR EACH ROW EXECUTE FUNCTION public.handle_new_user();
65
-
66
- -- Create tenant + owner atomically (bypasses RLS chicken-and-egg).
67
- -- This public function IS the body — PostgREST exposes public directly, so
68
- -- there is no saas_core wrapper indirection anymore.
69
- CREATE OR REPLACE FUNCTION public.create_tenant_with_owner(
70
- p_name text, p_slug text, p_user_id uuid,
71
- p_vertical_id text DEFAULT NULL, p_plan text DEFAULT 'free', p_settings jsonb DEFAULT '{}'
72
- )
73
- RETURNS jsonb LANGUAGE plpgsql SECURITY DEFINER AS $$
74
- DECLARE v_id uuid; v_tenant jsonb;
75
- BEGIN
76
- INSERT INTO public.tenants (name, slug, plan, vertical_id, settings)
77
- VALUES (p_name, p_slug, p_plan, p_vertical_id, p_settings) RETURNING id INTO v_id;
78
-
79
- INSERT INTO public.tenant_members (tenant_id, user_id, role)
80
- VALUES (v_id, p_user_id, 'owner');
81
-
82
- SELECT to_jsonb(t) INTO v_tenant FROM public.tenants t WHERE t.id = v_id;
83
- RETURN v_tenant;
84
- END; $$;
85
-
86
- -- Admin check (SECURITY DEFINER to avoid RLS recursion)
87
- CREATE OR REPLACE FUNCTION public.is_tenant_admin(p_tenant_id uuid)
88
- RETURNS boolean LANGUAGE sql STABLE SECURITY DEFINER AS $$
89
- SELECT EXISTS (
90
- SELECT 1 FROM public.tenant_members
91
- WHERE tenant_id = p_tenant_id AND user_id = auth.uid() AND role IN ('owner', 'admin')
92
- );
93
- $$;
94
-
95
- -- Plans
96
- CREATE TABLE IF NOT EXISTS public.plans (
97
- id text PRIMARY KEY,
98
- name text NOT NULL,
99
- description text,
100
- vertical_id text,
101
- price_monthly integer DEFAULT 0,
102
- price_yearly integer DEFAULT 0,
103
- features jsonb DEFAULT '[]',
104
- is_popular boolean DEFAULT false,
105
- sort_order integer DEFAULT 0,
106
- created_at timestamptz NOT NULL DEFAULT now()
107
- );
108
-
109
- -- Permissions (RBAC)
110
- CREATE TABLE IF NOT EXISTS public.permissions (
111
- id text PRIMARY KEY,
112
- category text NOT NULL,
113
- description text,
114
- created_at timestamptz NOT NULL DEFAULT now()
115
- );
116
-
117
- -- Role permissions (default grants per role)
118
- CREATE TABLE IF NOT EXISTS public.role_permissions (
119
- id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
120
- role text NOT NULL,
121
- permission_id text NOT NULL REFERENCES public.permissions(id) ON DELETE CASCADE,
122
- actions text[] NOT NULL DEFAULT '{read}',
123
- UNIQUE(role, permission_id)
124
- );
125
-
126
- -- Tenant role overrides (per-tenant permission customization)
127
- CREATE TABLE IF NOT EXISTS public.tenant_role_overrides (
128
- id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
129
- tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
130
- role text NOT NULL,
131
- permission_id text NOT NULL REFERENCES public.permissions(id) ON DELETE CASCADE,
132
- granted boolean NOT NULL DEFAULT true,
133
- UNIQUE(tenant_id, role, permission_id)
134
- );
135
-
136
- -- Invitations
137
- CREATE TABLE IF NOT EXISTS public.invitations (
138
- id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
139
- tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
140
- email text NOT NULL,
141
- role text NOT NULL DEFAULT 'staff',
142
- location_ids uuid[] DEFAULT '{}',
143
- token text UNIQUE NOT NULL DEFAULT replace(gen_random_uuid()::text, '-', ''),
144
- status text NOT NULL DEFAULT 'pending',
145
- invited_by uuid REFERENCES auth.users(id),
146
- accepted_at timestamptz,
147
- expires_at timestamptz NOT NULL DEFAULT (now() + interval '7 days'),
148
- created_at timestamptz NOT NULL DEFAULT now()
149
- );
150
-
151
- -- Payment events (webhook log)
152
- CREATE TABLE IF NOT EXISTS public.payment_events (
153
- id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
154
- tenant_id uuid REFERENCES public.tenants(id) ON DELETE SET NULL,
155
- stripe_event_id text UNIQUE,
156
- event_type text NOT NULL,
157
- payload jsonb DEFAULT '{}',
158
- created_at timestamptz NOT NULL DEFAULT now()
159
- );
160
-
161
- -- Locations (multi-branch)
162
- CREATE TABLE IF NOT EXISTS public.locations (
163
- id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
164
- tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
165
- kind text NOT NULL DEFAULT 'branch',
166
- name text NOT NULL,
167
- email text,
168
- phone text,
169
- address text,
170
- city text,
171
- state text,
172
- country text DEFAULT 'BR',
173
- postal_code text,
174
- is_headquarters boolean DEFAULT false,
175
- is_active boolean DEFAULT true,
176
- tags text[] DEFAULT '{}',
177
- notes text,
178
- metadata jsonb DEFAULT '{}',
179
- created_at timestamptz NOT NULL DEFAULT now(),
180
- updated_at timestamptz NOT NULL DEFAULT now()
181
- );
182
-
183
- DROP TRIGGER IF EXISTS locations_updated_at ON public.locations;
184
- CREATE TRIGGER locations_updated_at BEFORE UPDATE ON public.locations
185
- FOR EACH ROW EXECUTE FUNCTION public.handle_updated_at();
186
-
187
- -- Location members (staff assigned to locations)
188
- CREATE TABLE IF NOT EXISTS public.location_members (
189
- id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
190
- location_id uuid NOT NULL REFERENCES public.locations(id) ON DELETE CASCADE,
191
- user_id uuid NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
192
- role text DEFAULT 'staff',
193
- UNIQUE(location_id, user_id)
194
- );
195
-
196
- -- Audit logs
197
- CREATE TABLE IF NOT EXISTS public.audit_logs (
198
- id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
199
- tenant_id uuid REFERENCES public.tenants(id) ON DELETE SET NULL,
200
- user_id uuid REFERENCES auth.users(id) ON DELETE SET NULL,
201
- action text NOT NULL,
202
- entity_type text,
203
- entity_id text,
204
- metadata jsonb DEFAULT '{}',
205
- created_at timestamptz NOT NULL DEFAULT now()
206
- );
207
-
208
- -- Subscriptions (billing)
209
- CREATE TABLE IF NOT EXISTS public.subscriptions (
210
- id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
211
- tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
212
- plan text NOT NULL DEFAULT 'free',
213
- status text NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'past_due', 'cancelled', 'trialing')),
214
- stripe_customer_id text,
215
- stripe_subscription_id text,
216
- current_period_start timestamptz,
217
- current_period_end timestamptz,
218
- cancel_at timestamptz,
219
- created_at timestamptz NOT NULL DEFAULT now(),
220
- updated_at timestamptz NOT NULL DEFAULT now()
221
- );
222
-
223
- DROP TRIGGER IF EXISTS subscriptions_updated_at ON public.subscriptions;
224
- CREATE TRIGGER subscriptions_updated_at BEFORE UPDATE ON public.subscriptions
225
- FOR EACH ROW EXECUTE FUNCTION public.handle_updated_at();
226
-
227
- -- Invoices
228
- CREATE TABLE IF NOT EXISTS public.invoices (
229
- id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
230
- tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
231
- stripe_invoice_id text,
232
- amount integer NOT NULL DEFAULT 0,
233
- currency text NOT NULL DEFAULT 'brl',
234
- status text NOT NULL DEFAULT 'draft' CHECK (status IN ('draft', 'open', 'paid', 'void', 'uncollectible')),
235
- invoice_url text,
236
- created_at timestamptz NOT NULL DEFAULT now()
237
- );
238
-
239
- -- Notifications
240
- CREATE TABLE IF NOT EXISTS public.notifications (
241
- id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
242
- tenant_id uuid REFERENCES public.tenants(id) ON DELETE CASCADE,
243
- user_id uuid REFERENCES auth.users(id) ON DELETE CASCADE,
244
- title text NOT NULL,
245
- body text,
246
- type text DEFAULT 'info',
247
- read boolean NOT NULL DEFAULT false,
248
- created_at timestamptz NOT NULL DEFAULT now()
249
- );
250
-
251
- -- RLS on all tables
252
- ALTER TABLE public.tenants ENABLE ROW LEVEL SECURITY;
253
- ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY;
254
- ALTER TABLE public.tenant_members ENABLE ROW LEVEL SECURITY;
255
- ALTER TABLE public.plans ENABLE ROW LEVEL SECURITY;
256
- ALTER TABLE public.permissions ENABLE ROW LEVEL SECURITY;
257
- ALTER TABLE public.role_permissions ENABLE ROW LEVEL SECURITY;
258
- ALTER TABLE public.tenant_role_overrides ENABLE ROW LEVEL SECURITY;
259
- ALTER TABLE public.invitations ENABLE ROW LEVEL SECURITY;
260
- ALTER TABLE public.payment_events ENABLE ROW LEVEL SECURITY;
261
- ALTER TABLE public.locations ENABLE ROW LEVEL SECURITY;
262
- ALTER TABLE public.location_members ENABLE ROW LEVEL SECURITY;
263
- ALTER TABLE public.audit_logs ENABLE ROW LEVEL SECURITY;
264
- ALTER TABLE public.subscriptions ENABLE ROW LEVEL SECURITY;
265
- ALTER TABLE public.invoices ENABLE ROW LEVEL SECURITY;
266
- ALTER TABLE public.notifications ENABLE ROW LEVEL SECURITY;
267
-
268
- -- Tenants: members can see, authenticated can create
269
- DROP POLICY IF EXISTS "tenants_select" ON public.tenants;
270
- CREATE POLICY "tenants_select" ON public.tenants FOR SELECT
271
- USING (id IN (SELECT tenant_id FROM public.tenant_members WHERE user_id = auth.uid()));
272
- DROP POLICY IF EXISTS "tenants_insert" ON public.tenants;
273
- CREATE POLICY "tenants_insert" ON public.tenants FOR INSERT TO authenticated WITH CHECK (true);
274
- DROP POLICY IF EXISTS "tenants_update" ON public.tenants;
275
- CREATE POLICY "tenants_update" ON public.tenants FOR UPDATE
276
- USING (public.is_tenant_admin(id));
277
-
278
- -- Profiles: own profile only
279
- DROP POLICY IF EXISTS "profiles_select" ON public.profiles;
280
- CREATE POLICY "profiles_select" ON public.profiles FOR SELECT USING (id = auth.uid());
281
- DROP POLICY IF EXISTS "profiles_insert" ON public.profiles;
282
- CREATE POLICY "profiles_insert" ON public.profiles FOR INSERT WITH CHECK (id = auth.uid());
283
- DROP POLICY IF EXISTS "profiles_update" ON public.profiles;
284
- CREATE POLICY "profiles_update" ON public.profiles FOR UPDATE USING (id = auth.uid());
285
-
286
- -- Tenant members: see own, insert own, admins manage
287
- DROP POLICY IF EXISTS "members_select" ON public.tenant_members;
288
- CREATE POLICY "members_select" ON public.tenant_members FOR SELECT USING (user_id = auth.uid());
289
- DROP POLICY IF EXISTS "members_insert_own" ON public.tenant_members;
290
- CREATE POLICY "members_insert_own" ON public.tenant_members FOR INSERT WITH CHECK (user_id = auth.uid());
291
- DROP POLICY IF EXISTS "members_update" ON public.tenant_members;
292
- CREATE POLICY "members_update" ON public.tenant_members FOR UPDATE USING (public.is_tenant_admin(tenant_id));
293
- DROP POLICY IF EXISTS "members_delete" ON public.tenant_members;
294
- CREATE POLICY "members_delete" ON public.tenant_members FOR DELETE USING (public.is_tenant_admin(tenant_id));
295
-
296
- -- Plans/permissions: readable by all authenticated
297
- DROP POLICY IF EXISTS "plans_select" ON public.plans;
298
- CREATE POLICY "plans_select" ON public.plans FOR SELECT TO authenticated USING (true);
299
- DROP POLICY IF EXISTS "perms_select" ON public.permissions;
300
- CREATE POLICY "perms_select" ON public.permissions FOR SELECT TO authenticated USING (true);
301
- DROP POLICY IF EXISTS "role_perms_select" ON public.role_permissions;
302
- CREATE POLICY "role_perms_select" ON public.role_permissions FOR SELECT TO authenticated USING (true);
303
-
304
- -- Tenant role overrides: members can see their tenant's overrides
305
- DROP POLICY IF EXISTS "overrides_select" ON public.tenant_role_overrides;
306
- CREATE POLICY "overrides_select" ON public.tenant_role_overrides FOR SELECT
307
- USING (tenant_id IN (SELECT tenant_id FROM public.tenant_members WHERE user_id = auth.uid()));
308
- DROP POLICY IF EXISTS "overrides_manage" ON public.tenant_role_overrides;
309
- CREATE POLICY "overrides_manage" ON public.tenant_role_overrides FOR ALL
310
- USING (public.is_tenant_admin(tenant_id));
311
-
312
- -- Invitations: admins manage, anyone can read (to accept)
313
- DROP POLICY IF EXISTS "invites_select" ON public.invitations;
314
- CREATE POLICY "invites_select" ON public.invitations FOR SELECT TO authenticated USING (true);
315
- DROP POLICY IF EXISTS "invites_insert" ON public.invitations;
316
- CREATE POLICY "invites_insert" ON public.invitations FOR INSERT
317
- WITH CHECK (public.is_tenant_admin(tenant_id));
318
- DROP POLICY IF EXISTS "invites_update" ON public.invitations;
319
- CREATE POLICY "invites_update" ON public.invitations FOR UPDATE
320
- USING (public.is_tenant_admin(tenant_id));
321
- DROP POLICY IF EXISTS "invites_delete" ON public.invitations;
322
- CREATE POLICY "invites_delete" ON public.invitations FOR DELETE
323
- USING (public.is_tenant_admin(tenant_id));
324
-
325
- -- Locations: tenant members can see, admins manage
326
- DROP POLICY IF EXISTS "locations_select" ON public.locations;
327
- CREATE POLICY "locations_select" ON public.locations FOR SELECT
328
- USING (tenant_id IN (SELECT tenant_id FROM public.tenant_members WHERE user_id = auth.uid()));
329
- DROP POLICY IF EXISTS "locations_manage" ON public.locations;
330
- CREATE POLICY "locations_manage" ON public.locations FOR ALL
331
- USING (public.is_tenant_admin(tenant_id));
332
-
333
- -- Location members: same as locations
334
- DROP POLICY IF EXISTS "loc_members_select" ON public.location_members;
335
- CREATE POLICY "loc_members_select" ON public.location_members FOR SELECT
336
- USING (location_id IN (SELECT id FROM public.locations WHERE tenant_id IN (
337
- SELECT tenant_id FROM public.tenant_members WHERE user_id = auth.uid()
338
- )));
339
-
340
- -- Subscriptions/invoices: members can see
341
- DROP POLICY IF EXISTS "subs_select" ON public.subscriptions;
342
- CREATE POLICY "subs_select" ON public.subscriptions FOR SELECT
343
- USING (tenant_id IN (SELECT tenant_id FROM public.tenant_members WHERE user_id = auth.uid()));
344
- DROP POLICY IF EXISTS "invoices_select" ON public.invoices;
345
- CREATE POLICY "invoices_select" ON public.invoices FOR SELECT
346
- USING (tenant_id IN (SELECT tenant_id FROM public.tenant_members WHERE user_id = auth.uid()));
347
-
348
- -- Payment events: service_role only (webhook handler)
349
- DROP POLICY IF EXISTS "events_service" ON public.payment_events;
350
- CREATE POLICY "events_service" ON public.payment_events FOR ALL TO service_role USING (true);
351
-
352
- -- Audit logs: tenant members can see their tenant's logs
353
- DROP POLICY IF EXISTS "audit_select" ON public.audit_logs;
354
- CREATE POLICY "audit_select" ON public.audit_logs FOR SELECT
355
- USING (tenant_id IN (SELECT tenant_id FROM public.tenant_members WHERE user_id = auth.uid()));
356
- DROP POLICY IF EXISTS "audit_insert" ON public.audit_logs;
357
- CREATE POLICY "audit_insert" ON public.audit_logs FOR INSERT TO authenticated WITH CHECK (true);
358
-
359
- -- Notifications: own notifications
360
- DROP POLICY IF EXISTS "notif_select" ON public.notifications;
361
- CREATE POLICY "notif_select" ON public.notifications FOR SELECT USING (user_id = auth.uid());
362
- DROP POLICY IF EXISTS "notif_update" ON public.notifications;
363
- CREATE POLICY "notif_update" ON public.notifications FOR UPDATE USING (user_id = auth.uid());
364
-
365
- -- Grant schema access (public is the only API schema now)
366
- GRANT ALL ON ALL TABLES IN SCHEMA public TO authenticated, service_role;
367
- GRANT SELECT ON ALL TABLES IN SCHEMA public TO anon;
368
- GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA public TO anon, authenticated, service_role;
369
-
370
- -- Expose ONLY the public schema to the PostgREST API
371
- ALTER ROLE authenticator SET pgrst.db_schemas = 'public';
372
- NOTIFY pgrst, 'reload config';
@@ -1,64 +0,0 @@
1
- -- core: RLS for project tables (public schema)
2
- -- Ensures every plugin/app table with tenant_id is scoped to the user's tenants.
3
-
4
- -- Helper: get tenant IDs the current user belongs to
5
- CREATE OR REPLACE FUNCTION public.user_tenant_ids()
6
- RETURNS SETOF uuid
7
- LANGUAGE sql STABLE SECURITY DEFINER AS $$
8
- SELECT tenant_id FROM public.tenant_members WHERE user_id = auth.uid();
9
- $$;
10
-
11
- -- Apply generic tenant-isolation RLS to public tables that have a tenant_id
12
- -- column. Runs as a DO block so it auto-discovers tables.
13
- --
14
- -- Scope note: the core tables (tenants/tenant_members/locations/... created in
15
- -- 001, and the archetype tables created later in 004) carry their OWN curated
16
- -- policies and must NOT receive the generic isolation policy (e.g. payment_events
17
- -- would otherwise become readable by tenant members). They are excluded below.
18
- -- What remains covered: plugin (plg_*) and app tables that carry a tenant_id.
19
- -- Child tables (order_items/appointment_items/*_items) have no tenant_id column,
20
- -- so they are skipped here and instead get parent-FK policies in their own
21
- -- migrations.
22
- DO $$
23
- DECLARE
24
- t text;
25
- core_tables text[] := ARRAY[
26
- -- 001 core spine
27
- 'tenants', 'profiles', 'tenant_members', 'plans', 'permissions',
28
- 'role_permissions', 'tenant_role_overrides', 'invitations',
29
- 'payment_events', 'locations', 'location_members', 'audit_logs',
30
- 'subscriptions', 'invoices', 'notifications',
31
- -- 004 archetypes (self-governed via 004/006 policies)
32
- 'people', 'categories', 'products', 'services', 'orders', 'order_items',
33
- 'transactions', 'appointments', 'appointment_items', 'schedules',
34
- -- core, non-API helper tables (both ledger names kept for pre/post-rename compat;
35
- -- `_migrations` is also covered by the NOT LIKE '\_%' filter below)
36
- 'sequences', 'documents', 'fayz_migration_ledger', '_migrations'
37
- ];
38
- BEGIN
39
- FOR t IN
40
- SELECT c.table_name
41
- FROM information_schema.columns c
42
- JOIN information_schema.tables tbl ON tbl.table_name = c.table_name AND tbl.table_schema = c.table_schema
43
- WHERE c.table_schema = 'public'
44
- AND c.column_name = 'tenant_id'
45
- AND tbl.table_type = 'BASE TABLE'
46
- AND c.table_name NOT LIKE '\_%'
47
- AND c.table_name <> ALL(core_tables)
48
- LOOP
49
- -- Enable RLS
50
- EXECUTE format('ALTER TABLE public.%I ENABLE ROW LEVEL SECURITY', t);
51
-
52
- -- Drop existing policies (idempotent)
53
- EXECUTE format('DROP POLICY IF EXISTS tenant_isolation_select ON public.%I', t);
54
- EXECUTE format('DROP POLICY IF EXISTS tenant_isolation_insert ON public.%I', t);
55
- EXECUTE format('DROP POLICY IF EXISTS tenant_isolation_update ON public.%I', t);
56
- EXECUTE format('DROP POLICY IF EXISTS tenant_isolation_delete ON public.%I', t);
57
-
58
- -- Create tenant isolation policies
59
- EXECUTE format('CREATE POLICY tenant_isolation_select ON public.%I FOR SELECT USING (tenant_id::uuid IN (SELECT public.user_tenant_ids()))', t);
60
- EXECUTE format('CREATE POLICY tenant_isolation_insert ON public.%I FOR INSERT WITH CHECK (tenant_id::uuid IN (SELECT public.user_tenant_ids()))', t);
61
- EXECUTE format('CREATE POLICY tenant_isolation_update ON public.%I FOR UPDATE USING (tenant_id::uuid IN (SELECT public.user_tenant_ids()))', t);
62
- EXECUTE format('CREATE POLICY tenant_isolation_delete ON public.%I FOR DELETE USING (tenant_id::uuid IN (SELECT public.user_tenant_ids()))', t);
63
- END LOOP;
64
- END $$;
@@ -1,17 +0,0 @@
1
- -- Add missing columns to invitations table
2
- ALTER TABLE public.invitations
3
- ADD COLUMN IF NOT EXISTS status text NOT NULL DEFAULT 'pending',
4
- ADD COLUMN IF NOT EXISTS location_ids uuid[] DEFAULT '{}';
5
-
6
- -- Add UPDATE and DELETE policies for invite management
7
- DO $$ BEGIN
8
- IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE policyname = 'invites_update' AND tablename = 'invitations') THEN
9
- CREATE POLICY "invites_update" ON public.invitations FOR UPDATE
10
- USING (public.is_tenant_admin(tenant_id));
11
- END IF;
12
-
13
- IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE policyname = 'invites_delete' AND tablename = 'invitations') THEN
14
- CREATE POLICY "invites_delete" ON public.invitations FOR DELETE
15
- USING (public.is_tenant_admin(tenant_id));
16
- END IF;
17
- END $$;