@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,343 +0,0 @@
1
- -- ============================================================================
2
- -- core archetype tables (public schema)
3
- -- Shared base entities that project-specific tables extend via FK
4
- -- ============================================================================
5
-
6
- -- ---------------------------------------------------------------------------
7
- -- 1. people — any human entity (staff, customer, supplier, professional)
8
- -- ---------------------------------------------------------------------------
9
- CREATE TABLE IF NOT EXISTS public.people (
10
- id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
11
- tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
12
- kind text NOT NULL,
13
- name text NOT NULL,
14
- email text,
15
- phone text,
16
- document_number text,
17
- avatar_url text,
18
- date_of_birth date,
19
- address text,
20
- city text,
21
- state text,
22
- country text DEFAULT 'BR',
23
- postal_code text,
24
- tags text[] DEFAULT '{}',
25
- is_active boolean DEFAULT true,
26
- notes text,
27
- metadata jsonb DEFAULT '{}',
28
- created_at timestamptz NOT NULL DEFAULT now(),
29
- updated_at timestamptz NOT NULL DEFAULT now()
30
- );
31
-
32
- CREATE INDEX IF NOT EXISTS people_tenant_kind ON public.people(tenant_id, kind);
33
- DROP TRIGGER IF EXISTS people_updated_at ON public.people;
34
- CREATE TRIGGER people_updated_at BEFORE UPDATE ON public.people
35
- FOR EACH ROW EXECUTE FUNCTION public.handle_updated_at();
36
-
37
- -- ---------------------------------------------------------------------------
38
- -- 2. categories — generic taxonomy (species, menu categories, regions)
39
- -- ---------------------------------------------------------------------------
40
- CREATE TABLE IF NOT EXISTS public.categories (
41
- id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
42
- tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
43
- kind text NOT NULL,
44
- name text NOT NULL,
45
- slug text,
46
- parent_id uuid REFERENCES public.categories(id) ON DELETE SET NULL,
47
- icon text,
48
- color text,
49
- sort_order integer DEFAULT 0,
50
- is_active boolean DEFAULT true,
51
- metadata jsonb DEFAULT '{}',
52
- created_at timestamptz NOT NULL DEFAULT now(),
53
- updated_at timestamptz NOT NULL DEFAULT now()
54
- );
55
-
56
- CREATE INDEX IF NOT EXISTS categories_tenant_kind ON public.categories(tenant_id, kind);
57
- DROP TRIGGER IF EXISTS categories_updated_at ON public.categories;
58
- CREATE TRIGGER categories_updated_at BEFORE UPDATE ON public.categories
59
- FOR EACH ROW EXECUTE FUNCTION public.handle_updated_at();
60
-
61
- -- ---------------------------------------------------------------------------
62
- -- 3. products — physical/trackable items (menu items, equipment, ingredients)
63
- -- ---------------------------------------------------------------------------
64
- CREATE TABLE IF NOT EXISTS public.products (
65
- id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
66
- tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
67
- category_id uuid REFERENCES public.categories(id) ON DELETE SET NULL,
68
- name text NOT NULL,
69
- description text,
70
- sku text,
71
- price numeric,
72
- cost numeric,
73
- currency text DEFAULT 'BRL',
74
- unit text,
75
- image_url text,
76
- stock numeric,
77
- min_stock numeric,
78
- status text DEFAULT 'active',
79
- is_active boolean DEFAULT true,
80
- tags text[] DEFAULT '{}',
81
- metadata jsonb DEFAULT '{}',
82
- created_at timestamptz NOT NULL DEFAULT now(),
83
- updated_at timestamptz NOT NULL DEFAULT now()
84
- );
85
-
86
- CREATE INDEX IF NOT EXISTS products_tenant ON public.products(tenant_id);
87
- DROP TRIGGER IF EXISTS products_updated_at ON public.products;
88
- CREATE TRIGGER products_updated_at BEFORE UPDATE ON public.products
89
- FOR EACH ROW EXECUTE FUNCTION public.handle_updated_at();
90
-
91
- -- ---------------------------------------------------------------------------
92
- -- 4. services — intangible offerings (exam types, consultations)
93
- -- ---------------------------------------------------------------------------
94
- CREATE TABLE IF NOT EXISTS public.services (
95
- id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
96
- tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
97
- category_id uuid REFERENCES public.categories(id) ON DELETE SET NULL,
98
- name text NOT NULL,
99
- description text,
100
- price numeric,
101
- cost numeric,
102
- currency text DEFAULT 'BRL',
103
- duration_minutes integer,
104
- image_url text,
105
- status text DEFAULT 'active',
106
- is_active boolean DEFAULT true,
107
- tags text[] DEFAULT '{}',
108
- metadata jsonb DEFAULT '{}',
109
- created_at timestamptz NOT NULL DEFAULT now(),
110
- updated_at timestamptz NOT NULL DEFAULT now()
111
- );
112
-
113
- CREATE INDEX IF NOT EXISTS services_tenant ON public.services(tenant_id);
114
- DROP TRIGGER IF EXISTS services_updated_at ON public.services;
115
- CREATE TRIGGER services_updated_at BEFORE UPDATE ON public.services
116
- FOR EACH ROW EXECUTE FUNCTION public.handle_updated_at();
117
-
118
- -- ---------------------------------------------------------------------------
119
- -- 5. orders — business orders/jobs
120
- -- ---------------------------------------------------------------------------
121
- CREATE TABLE IF NOT EXISTS public.orders (
122
- id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
123
- tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
124
- kind text NOT NULL,
125
- reference_number text,
126
- status text DEFAULT 'draft',
127
- party_id uuid REFERENCES public.people(id) ON DELETE SET NULL,
128
- assignee_id uuid REFERENCES public.people(id) ON DELETE SET NULL,
129
- location_id uuid REFERENCES public.locations(id) ON DELETE SET NULL,
130
- subtotal numeric DEFAULT 0,
131
- discount numeric DEFAULT 0,
132
- tax numeric DEFAULT 0,
133
- total numeric DEFAULT 0,
134
- currency text DEFAULT 'BRL',
135
- due_at timestamptz,
136
- completed_at timestamptz,
137
- notes text,
138
- tags text[] DEFAULT '{}',
139
- metadata jsonb DEFAULT '{}',
140
- created_at timestamptz NOT NULL DEFAULT now(),
141
- updated_at timestamptz NOT NULL DEFAULT now()
142
- );
143
-
144
- CREATE INDEX IF NOT EXISTS orders_tenant_kind ON public.orders(tenant_id, kind);
145
- CREATE INDEX IF NOT EXISTS orders_status ON public.orders(tenant_id, status);
146
- DROP TRIGGER IF EXISTS orders_updated_at ON public.orders;
147
- CREATE TRIGGER orders_updated_at BEFORE UPDATE ON public.orders
148
- FOR EACH ROW EXECUTE FUNCTION public.handle_updated_at();
149
-
150
- -- ---------------------------------------------------------------------------
151
- -- 6. order_items — line items for orders
152
- -- ---------------------------------------------------------------------------
153
- CREATE TABLE IF NOT EXISTS public.order_items (
154
- id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
155
- order_id uuid NOT NULL REFERENCES public.orders(id) ON DELETE CASCADE,
156
- product_id uuid REFERENCES public.products(id) ON DELETE SET NULL,
157
- service_id uuid REFERENCES public.services(id) ON DELETE SET NULL,
158
- name text NOT NULL,
159
- description text,
160
- quantity numeric DEFAULT 1,
161
- unit_price numeric DEFAULT 0,
162
- discount numeric DEFAULT 0,
163
- total numeric DEFAULT 0,
164
- sort_order integer DEFAULT 0,
165
- metadata jsonb DEFAULT '{}',
166
- created_at timestamptz NOT NULL DEFAULT now()
167
- );
168
-
169
- CREATE INDEX IF NOT EXISTS order_items_order ON public.order_items(order_id);
170
-
171
- -- ---------------------------------------------------------------------------
172
- -- 7. transactions — financial movements (payments, refunds, expenses)
173
- -- ---------------------------------------------------------------------------
174
- CREATE TABLE IF NOT EXISTS public.transactions (
175
- id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
176
- tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
177
- kind text NOT NULL,
178
- order_id uuid REFERENCES public.orders(id) ON DELETE SET NULL,
179
- party_id uuid REFERENCES public.people(id) ON DELETE SET NULL,
180
- amount numeric NOT NULL,
181
- currency text DEFAULT 'BRL',
182
- payment_method text,
183
- reference text,
184
- status text DEFAULT 'completed',
185
- transacted_at timestamptz DEFAULT now(),
186
- notes text,
187
- metadata jsonb DEFAULT '{}',
188
- created_at timestamptz NOT NULL DEFAULT now(),
189
- updated_at timestamptz NOT NULL DEFAULT now()
190
- );
191
-
192
- CREATE INDEX IF NOT EXISTS transactions_tenant_kind ON public.transactions(tenant_id, kind);
193
- DROP TRIGGER IF EXISTS transactions_updated_at ON public.transactions;
194
- CREATE TRIGGER transactions_updated_at BEFORE UPDATE ON public.transactions
195
- FOR EACH ROW EXECUTE FUNCTION public.handle_updated_at();
196
-
197
- -- ---------------------------------------------------------------------------
198
- -- 8. appointments — reservations/appointments
199
- -- ---------------------------------------------------------------------------
200
- CREATE TABLE IF NOT EXISTS public.appointments (
201
- id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
202
- tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
203
- kind text NOT NULL,
204
- party_id uuid REFERENCES public.people(id) ON DELETE SET NULL,
205
- assignee_id uuid REFERENCES public.people(id) ON DELETE SET NULL,
206
- location_id uuid REFERENCES public.locations(id) ON DELETE SET NULL,
207
- order_id uuid REFERENCES public.orders(id) ON DELETE SET NULL,
208
- starts_at timestamptz NOT NULL,
209
- ends_at timestamptz,
210
- status text DEFAULT 'pending',
211
- notes text,
212
- metadata jsonb DEFAULT '{}',
213
- created_at timestamptz NOT NULL DEFAULT now(),
214
- updated_at timestamptz NOT NULL DEFAULT now()
215
- );
216
-
217
- CREATE INDEX IF NOT EXISTS appointments_tenant_kind ON public.appointments(tenant_id, kind);
218
- CREATE INDEX IF NOT EXISTS appointments_starts ON public.appointments(tenant_id, starts_at);
219
- DROP TRIGGER IF EXISTS appointments_updated_at ON public.appointments;
220
- CREATE TRIGGER appointments_updated_at BEFORE UPDATE ON public.appointments
221
- FOR EACH ROW EXECUTE FUNCTION public.handle_updated_at();
222
-
223
- -- ---------------------------------------------------------------------------
224
- -- 9. appointment_items — services within an appointment
225
- -- (booking_id column name preserved for parity with converted pools —
226
- -- the in-place converter renames the table, not the FK column)
227
- -- ---------------------------------------------------------------------------
228
- CREATE TABLE IF NOT EXISTS public.appointment_items (
229
- id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
230
- booking_id uuid NOT NULL REFERENCES public.appointments(id) ON DELETE CASCADE,
231
- service_id uuid REFERENCES public.services(id) ON DELETE SET NULL,
232
- assignee_id uuid REFERENCES public.people(id) ON DELETE SET NULL,
233
- name text NOT NULL,
234
- duration_minutes integer,
235
- price numeric DEFAULT 0,
236
- sort_order integer DEFAULT 0,
237
- notes text,
238
- metadata jsonb DEFAULT '{}',
239
- created_at timestamptz NOT NULL DEFAULT now()
240
- );
241
-
242
- CREATE INDEX IF NOT EXISTS appointment_items_booking ON public.appointment_items(booking_id);
243
-
244
- -- ---------------------------------------------------------------------------
245
- -- 10. schedules — recurring availability/shifts
246
- -- ---------------------------------------------------------------------------
247
- CREATE TABLE IF NOT EXISTS public.schedules (
248
- id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
249
- tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
250
- kind text NOT NULL,
251
- assignee_id uuid REFERENCES public.people(id) ON DELETE CASCADE,
252
- location_id uuid REFERENCES public.locations(id) ON DELETE SET NULL,
253
- day_of_week smallint,
254
- specific_date date,
255
- starts_at time NOT NULL,
256
- ends_at time NOT NULL,
257
- is_active boolean DEFAULT true,
258
- metadata jsonb DEFAULT '{}',
259
- created_at timestamptz NOT NULL DEFAULT now(),
260
- updated_at timestamptz NOT NULL DEFAULT now()
261
- );
262
-
263
- CREATE INDEX IF NOT EXISTS schedules_tenant_kind ON public.schedules(tenant_id, kind);
264
- CREATE INDEX IF NOT EXISTS schedules_assignee ON public.schedules(assignee_id, day_of_week);
265
- DROP TRIGGER IF EXISTS schedules_updated_at ON public.schedules;
266
- CREATE TRIGGER schedules_updated_at BEFORE UPDATE ON public.schedules
267
- FOR EACH ROW EXECUTE FUNCTION public.handle_updated_at();
268
-
269
- -- ============================================================================
270
- -- RLS policies — tenant isolation for all archetype tables
271
- -- ============================================================================
272
-
273
- DO $$
274
- DECLARE
275
- tbl text;
276
- BEGIN
277
- FOREACH tbl IN ARRAY ARRAY[
278
- 'people', 'categories', 'products', 'services',
279
- 'orders', 'transactions', 'appointments', 'schedules'
280
- ]
281
- LOOP
282
- EXECUTE format('ALTER TABLE public.%I ENABLE ROW LEVEL SECURITY', tbl);
283
- -- Drop first so a replay on an already-provisioned pool no-ops cleanly.
284
- EXECUTE format('DROP POLICY IF EXISTS "%s_select" ON public.%I', tbl, tbl);
285
- EXECUTE format('DROP POLICY IF EXISTS "%s_insert" ON public.%I', tbl, tbl);
286
- EXECUTE format('DROP POLICY IF EXISTS "%s_update" ON public.%I', tbl, tbl);
287
- EXECUTE format('DROP POLICY IF EXISTS "%s_delete" ON public.%I', tbl, tbl);
288
- EXECUTE format(
289
- 'CREATE POLICY "%s_select" ON public.%I FOR SELECT TO authenticated USING (tenant_id IN (SELECT tenant_id FROM public.tenant_members WHERE user_id = auth.uid()))',
290
- tbl, tbl
291
- );
292
- EXECUTE format(
293
- 'CREATE POLICY "%s_insert" ON public.%I FOR INSERT TO authenticated WITH CHECK (tenant_id IN (SELECT tenant_id FROM public.tenant_members WHERE user_id = auth.uid()))',
294
- tbl, tbl
295
- );
296
- EXECUTE format(
297
- 'CREATE POLICY "%s_update" ON public.%I FOR UPDATE TO authenticated USING (tenant_id IN (SELECT tenant_id FROM public.tenant_members WHERE user_id = auth.uid()))',
298
- tbl, tbl
299
- );
300
- EXECUTE format(
301
- 'CREATE POLICY "%s_delete" ON public.%I FOR DELETE TO authenticated USING (tenant_id IN (SELECT tenant_id FROM public.tenant_members WHERE user_id = auth.uid()))',
302
- tbl, tbl
303
- );
304
- END LOOP;
305
-
306
- -- Child tables (order_items, appointment_items) — RLS via parent FK
307
- FOREACH tbl IN ARRAY ARRAY['order_items', 'appointment_items']
308
- LOOP
309
- EXECUTE format('ALTER TABLE public.%I ENABLE ROW LEVEL SECURITY', tbl);
310
- END LOOP;
311
- END $$;
312
-
313
- -- order_items: access via parent order's tenant
314
- DROP POLICY IF EXISTS "order_items_select" ON public.order_items;
315
- CREATE POLICY "order_items_select" ON public.order_items FOR SELECT TO authenticated
316
- USING (order_id IN (SELECT id FROM public.orders WHERE tenant_id IN (SELECT tenant_id FROM public.tenant_members WHERE user_id = auth.uid())));
317
- DROP POLICY IF EXISTS "order_items_insert" ON public.order_items;
318
- CREATE POLICY "order_items_insert" ON public.order_items FOR INSERT TO authenticated
319
- WITH CHECK (order_id IN (SELECT id FROM public.orders WHERE tenant_id IN (SELECT tenant_id FROM public.tenant_members WHERE user_id = auth.uid())));
320
- DROP POLICY IF EXISTS "order_items_update" ON public.order_items;
321
- CREATE POLICY "order_items_update" ON public.order_items FOR UPDATE TO authenticated
322
- USING (order_id IN (SELECT id FROM public.orders WHERE tenant_id IN (SELECT tenant_id FROM public.tenant_members WHERE user_id = auth.uid())));
323
- DROP POLICY IF EXISTS "order_items_delete" ON public.order_items;
324
- CREATE POLICY "order_items_delete" ON public.order_items FOR DELETE TO authenticated
325
- USING (order_id IN (SELECT id FROM public.orders WHERE tenant_id IN (SELECT tenant_id FROM public.tenant_members WHERE user_id = auth.uid())));
326
-
327
- -- appointment_items: access via parent appointment's tenant
328
- DROP POLICY IF EXISTS "appointment_items_select" ON public.appointment_items;
329
- CREATE POLICY "appointment_items_select" ON public.appointment_items FOR SELECT TO authenticated
330
- USING (booking_id IN (SELECT id FROM public.appointments WHERE tenant_id IN (SELECT tenant_id FROM public.tenant_members WHERE user_id = auth.uid())));
331
- DROP POLICY IF EXISTS "appointment_items_insert" ON public.appointment_items;
332
- CREATE POLICY "appointment_items_insert" ON public.appointment_items FOR INSERT TO authenticated
333
- WITH CHECK (booking_id IN (SELECT id FROM public.appointments WHERE tenant_id IN (SELECT tenant_id FROM public.tenant_members WHERE user_id = auth.uid())));
334
- DROP POLICY IF EXISTS "appointment_items_update" ON public.appointment_items;
335
- CREATE POLICY "appointment_items_update" ON public.appointment_items FOR UPDATE TO authenticated
336
- USING (booking_id IN (SELECT id FROM public.appointments WHERE tenant_id IN (SELECT tenant_id FROM public.tenant_members WHERE user_id = auth.uid())));
337
- DROP POLICY IF EXISTS "appointment_items_delete" ON public.appointment_items;
338
- CREATE POLICY "appointment_items_delete" ON public.appointment_items FOR DELETE TO authenticated
339
- USING (booking_id IN (SELECT id FROM public.appointments WHERE tenant_id IN (SELECT tenant_id FROM public.tenant_members WHERE user_id = auth.uid())));
340
-
341
- -- Grant access to archetype tables
342
- GRANT ALL ON ALL TABLES IN SCHEMA public TO authenticated, service_role;
343
- GRANT SELECT ON ALL TABLES IN SCHEMA public TO anon;
@@ -1,18 +0,0 @@
1
- -- Bring public.locations in line with other archetype tables
2
- ALTER TABLE public.locations
3
- ADD COLUMN IF NOT EXISTS kind text NOT NULL DEFAULT 'branch',
4
- ADD COLUMN IF NOT EXISTS email text,
5
- ADD COLUMN IF NOT EXISTS postal_code text,
6
- ADD COLUMN IF NOT EXISTS tags text[] DEFAULT '{}',
7
- ADD COLUMN IF NOT EXISTS notes text,
8
- ADD COLUMN IF NOT EXISTS metadata jsonb DEFAULT '{}';
9
-
10
- -- Migrate settings → metadata for existing rows (if settings column still exists)
11
- DO $$ BEGIN
12
- IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'locations' AND column_name = 'settings') THEN
13
- UPDATE public.locations SET metadata = settings WHERE metadata = '{}' AND settings != '{}';
14
- ALTER TABLE public.locations DROP COLUMN settings;
15
- END IF;
16
- END $$;
17
-
18
- CREATE INDEX IF NOT EXISTS locations_tenant_kind ON public.locations(tenant_id, kind);
@@ -1,74 +0,0 @@
1
- -- Fix archetype RLS policies to use SECURITY DEFINER function
2
- -- The raw subquery on tenant_members fails due to recursive RLS
3
-
4
- -- Ensure the SECURITY DEFINER helper exists (bypasses RLS)
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
- -- Drop and recreate all archetype table policies using the SECURITY DEFINER function
12
- DO $$
13
- DECLARE
14
- tbl text;
15
- BEGIN
16
- FOREACH tbl IN ARRAY ARRAY[
17
- 'people', 'categories', 'products', 'services',
18
- 'orders', 'transactions', 'appointments', 'schedules'
19
- ]
20
- LOOP
21
- -- Drop old policies
22
- EXECUTE format('DROP POLICY IF EXISTS "%s_select" ON public.%I', tbl, tbl);
23
- EXECUTE format('DROP POLICY IF EXISTS "%s_insert" ON public.%I', tbl, tbl);
24
- EXECUTE format('DROP POLICY IF EXISTS "%s_update" ON public.%I', tbl, tbl);
25
- EXECUTE format('DROP POLICY IF EXISTS "%s_delete" ON public.%I', tbl, tbl);
26
-
27
- -- Create new policies using SECURITY DEFINER function
28
- EXECUTE format(
29
- 'CREATE POLICY "%s_select" ON public.%I FOR SELECT TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()))',
30
- tbl, tbl
31
- );
32
- EXECUTE format(
33
- 'CREATE POLICY "%s_insert" ON public.%I FOR INSERT TO authenticated WITH CHECK (tenant_id IN (SELECT public.user_tenant_ids()))',
34
- tbl, tbl
35
- );
36
- EXECUTE format(
37
- 'CREATE POLICY "%s_update" ON public.%I FOR UPDATE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()))',
38
- tbl, tbl
39
- );
40
- EXECUTE format(
41
- 'CREATE POLICY "%s_delete" ON public.%I FOR DELETE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()))',
42
- tbl, tbl
43
- );
44
- END LOOP;
45
- END $$;
46
-
47
- -- Fix child table policies too
48
- DROP POLICY IF EXISTS "order_items_select" ON public.order_items;
49
- DROP POLICY IF EXISTS "order_items_insert" ON public.order_items;
50
- DROP POLICY IF EXISTS "order_items_update" ON public.order_items;
51
- DROP POLICY IF EXISTS "order_items_delete" ON public.order_items;
52
-
53
- CREATE POLICY "order_items_select" ON public.order_items FOR SELECT TO authenticated
54
- USING (order_id IN (SELECT id FROM public.orders WHERE tenant_id IN (SELECT public.user_tenant_ids())));
55
- CREATE POLICY "order_items_insert" ON public.order_items FOR INSERT TO authenticated
56
- WITH CHECK (order_id IN (SELECT id FROM public.orders WHERE tenant_id IN (SELECT public.user_tenant_ids())));
57
- CREATE POLICY "order_items_update" ON public.order_items FOR UPDATE TO authenticated
58
- USING (order_id IN (SELECT id FROM public.orders WHERE tenant_id IN (SELECT public.user_tenant_ids())));
59
- CREATE POLICY "order_items_delete" ON public.order_items FOR DELETE TO authenticated
60
- USING (order_id IN (SELECT id FROM public.orders WHERE tenant_id IN (SELECT public.user_tenant_ids())));
61
-
62
- DROP POLICY IF EXISTS "appointment_items_select" ON public.appointment_items;
63
- DROP POLICY IF EXISTS "appointment_items_insert" ON public.appointment_items;
64
- DROP POLICY IF EXISTS "appointment_items_update" ON public.appointment_items;
65
- DROP POLICY IF EXISTS "appointment_items_delete" ON public.appointment_items;
66
-
67
- CREATE POLICY "appointment_items_select" ON public.appointment_items FOR SELECT TO authenticated
68
- USING (booking_id IN (SELECT id FROM public.appointments WHERE tenant_id IN (SELECT public.user_tenant_ids())));
69
- CREATE POLICY "appointment_items_insert" ON public.appointment_items FOR INSERT TO authenticated
70
- WITH CHECK (booking_id IN (SELECT id FROM public.appointments WHERE tenant_id IN (SELECT public.user_tenant_ids())));
71
- CREATE POLICY "appointment_items_update" ON public.appointment_items FOR UPDATE TO authenticated
72
- USING (booking_id IN (SELECT id FROM public.appointments WHERE tenant_id IN (SELECT public.user_tenant_ids())));
73
- CREATE POLICY "appointment_items_delete" ON public.appointment_items FOR DELETE TO authenticated
74
- USING (booking_id IN (SELECT id FROM public.appointments WHERE tenant_id IN (SELECT public.user_tenant_ids())));
@@ -1,4 +0,0 @@
1
- -- Grant access to archetype tables created in migration 004
2
- -- The original GRANT ALL ON ALL TABLES only covered tables that existed at that time
3
- GRANT ALL ON ALL TABLES IN SCHEMA public TO authenticated, service_role;
4
- GRANT SELECT ON ALL TABLES IN SCHEMA public TO anon;
@@ -1,5 +0,0 @@
1
- -- Ensure all roles have access to the public schema
2
- GRANT USAGE ON SCHEMA public TO anon, authenticated, service_role;
3
- GRANT ALL ON ALL TABLES IN SCHEMA public TO authenticated, service_role;
4
- GRANT SELECT ON ALL TABLES IN SCHEMA public TO anon;
5
- GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA public TO anon, authenticated, service_role;
@@ -1,19 +0,0 @@
1
- -- ============================================================================
2
- -- 009: anon hardening — close 008's blanket grants for the anon role.
3
- --
4
- -- 008_grants.sql grants SELECT on all tables and EXECUTE on all functions to
5
- -- anon. RLS (policies are TO authenticated) already returns zero rows for
6
- -- table reads, but EXECUTE on SECURITY DEFINER functions like
7
- -- create_tenant_with_owner is a real abuse surface on projects that serve
8
- -- public (anon-key) pages. Revoke everything; anon-facing surfaces must grant
9
- -- themselves explicitly (e.g. plugin-agenda's public booking RPCs/views).
10
- --
11
- -- Apply AFTER 008 and BEFORE plugin migrations that grant anon access.
12
- -- ============================================================================
13
-
14
- REVOKE EXECUTE ON ALL FUNCTIONS IN SCHEMA public FROM anon;
15
-
16
- REVOKE SELECT ON ALL TABLES IN SCHEMA public FROM anon;
17
-
18
- -- Future objects: don't default-grant anon anything.
19
- ALTER DEFAULT PRIVILEGES IN SCHEMA public REVOKE EXECUTE ON FUNCTIONS FROM anon;
@@ -1,46 +0,0 @@
1
- -- ============================================================================
2
- -- 010_migration_ledger.sql — per-pool applied-migration ledger.
3
- --
4
- -- The Runner v2 executor records every applied SQL file here (plugin_id +
5
- -- file_name + checksum) so re-applies are detected and checksum drift on an
6
- -- already-applied file is a HARD STOP.
7
- --
8
- -- service_role only: no anon/authenticated grants, RLS enabled with NO
9
- -- policies, so PostgREST (anon/authenticated) is deny-all. Only the service
10
- -- role (which bypasses RLS) — i.e. the runner — can read/write it.
11
- --
12
- -- Naming: the ledger is an internal, non-API table, so it uses the `_`-prefixed
13
- -- `public._migrations` name (excluded from the project_rls auto-policy pass by
14
- -- the `NOT LIKE '\_%'` filter). Legacy pools provisioned as `fayz_migration_ledger`
15
- -- are migrated by 012_rename_migration_ledger.sql (and by the runner's
16
- -- ensureLedger guard); fresh installs are born as `_migrations` here.
17
- -- ============================================================================
18
-
19
- CREATE TABLE IF NOT EXISTS public._migrations (
20
- id bigserial PRIMARY KEY,
21
- plugin_id text NOT NULL,
22
- file_name text NOT NULL,
23
- checksum text NOT NULL,
24
- plugin_version text,
25
- applied_at timestamptz NOT NULL DEFAULT now(),
26
- applied_by text,
27
- UNIQUE(plugin_id, file_name)
28
- );
29
-
30
- ALTER TABLE public._migrations ENABLE ROW LEVEL SECURITY;
31
-
32
- -- Deny-all through PostgREST: revoke any inherited grants, grant to nobody but
33
- -- service_role (which bypasses RLS anyway). No policies = no anon/authenticated access.
34
- REVOKE ALL ON public._migrations FROM anon, authenticated;
35
- GRANT ALL ON public._migrations TO service_role;
36
- -- The sequence, WHATEVER IT IS CALLED. A legacy pool whose table was renamed
37
- -- ahead of 012 still owns fayz_migration_ledger_id_seq, and naming the new one
38
- -- here would stop the chain twelve files before the migration that renames it.
39
- -- pg_get_serial_sequence asks the column instead of guessing.
40
- DO $$
41
- DECLARE v_seq text := pg_get_serial_sequence('public._migrations', 'id');
42
- BEGIN
43
- IF v_seq IS NOT NULL THEN
44
- EXECUTE format('GRANT USAGE, SELECT ON SEQUENCE %s TO service_role', v_seq);
45
- END IF;
46
- END $$;
@@ -1,12 +0,0 @@
1
- -- 009 revoked anon SELECT/EXECUTE but Supabase default privileges also hand
2
- -- anon INSERT/UPDATE/DELETE/... on every table created by postgres. RLS blocks
3
- -- those writes today; make the grant layer agree and stop future tables from
4
- -- being born writable-by-anon.
5
-
6
- REVOKE INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER
7
- ON ALL TABLES IN SCHEMA public FROM anon;
8
-
9
- ALTER DEFAULT PRIVILEGES IN SCHEMA public
10
- REVOKE INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER ON TABLES FROM anon;
11
-
12
- ALTER DEFAULT PRIVILEGES IN SCHEMA public REVOKE SELECT ON TABLES FROM anon;
@@ -1,61 +0,0 @@
1
- -- ============================================================================
2
- -- 012_rename_migration_ledger.sql — rename the migration ledger onto the
3
- -- internal `_`-prefixed baseline name:
4
- --
5
- -- public.fayz_migration_ledger → public._migrations
6
- --
7
- -- Legacy-pool remediation ONLY: 010_migration_ledger.sql now creates `_migrations`
8
- -- directly, so fresh installs never reach a rename branch. The runner's
9
- -- ensureLedger (cli/src/lib/ledger.ts) performs the same guarded rename at the
10
- -- START of every apply, so on a real pool the ledger is already `_migrations`
11
- -- by the time this migration runs — this file is the declarative backstop.
12
- --
13
- -- Fully idempotent + guarded: each RENAME fires only when the old object exists
14
- -- and the new one does not. A table RENAME preserves the rows (the applied-file
15
- -- history MUST survive), plus RLS state, grants and constraints; re-assert the
16
- -- deny-all grants afterwards to keep the baseline explicit.
17
- -- ============================================================================
18
-
19
- DO $$
20
- BEGIN
21
- -- Table (carries the applied-migration history — never drop + recreate).
22
- IF to_regclass('public.fayz_migration_ledger') IS NOT NULL
23
- AND to_regclass('public._migrations') IS NULL THEN
24
- ALTER TABLE public.fayz_migration_ledger RENAME TO _migrations;
25
- END IF;
26
-
27
- -- bigserial owns fayz_migration_ledger_id_seq — a table rename does not move it.
28
- IF to_regclass('public.fayz_migration_ledger_id_seq') IS NOT NULL
29
- AND to_regclass('public._migrations_id_seq') IS NULL THEN
30
- ALTER SEQUENCE public.fayz_migration_ledger_id_seq RENAME TO _migrations_id_seq;
31
- END IF;
32
-
33
- -- Named constraints are auto-named after the old table; rename for parity with
34
- -- a fresh install. Guard on the new table existing so this is safe pre-rename.
35
- IF to_regclass('public._migrations') IS NOT NULL THEN
36
- IF EXISTS (SELECT 1 FROM pg_constraint
37
- WHERE conname = 'fayz_migration_ledger_pkey'
38
- AND conrelid = 'public._migrations'::regclass) THEN
39
- ALTER TABLE public._migrations RENAME CONSTRAINT fayz_migration_ledger_pkey TO _migrations_pkey;
40
- END IF;
41
- IF EXISTS (SELECT 1 FROM pg_constraint
42
- WHERE conname = 'fayz_migration_ledger_plugin_id_file_name_key'
43
- AND conrelid = 'public._migrations'::regclass) THEN
44
- ALTER TABLE public._migrations
45
- RENAME CONSTRAINT fayz_migration_ledger_plugin_id_file_name_key
46
- TO _migrations_plugin_id_file_name_key;
47
- END IF;
48
- END IF;
49
- END $$;
50
-
51
- -- Re-assert the deny-all posture (idempotent; RLS/grants survive a rename but the
52
- -- baseline keeps this explicit so a manually-created ledger converges too).
53
- DO $$
54
- BEGIN
55
- IF to_regclass('public._migrations') IS NOT NULL THEN
56
- EXECUTE 'ALTER TABLE public._migrations ENABLE ROW LEVEL SECURITY';
57
- EXECUTE 'REVOKE ALL ON public._migrations FROM anon, authenticated';
58
- EXECUTE 'GRANT ALL ON public._migrations TO service_role';
59
- EXECUTE 'GRANT USAGE, SELECT ON SEQUENCE public._migrations_id_seq TO service_role';
60
- END IF;
61
- END $$;
@@ -1,22 +0,0 @@
1
- -- ============================================================================
2
- -- 013_sequences_rls.sql — close the RLS drift on public.sequences.
3
- --
4
- -- public.sequences (the REC-/PAG- numbering counter, created by the financial
5
- -- plugin's 004_order_to_cash.sql) shipped WITHOUT row-level security on a set of
6
- -- pools. Baseline = RLS ON, deny-all (no policies), exactly like the salon pool:
7
- -- the table is written ONLY through the SECURITY DEFINER function
8
- -- public.next_sequence(), which runs as the table owner and so bypasses RLS.
9
- -- With RLS on and no policy, the PostgREST anon/authenticated surface is deny-all,
10
- -- so the counter can never be read or tampered with directly by a tenant.
11
- --
12
- -- Legacy-pool remediation: guarded so it is a no-op on pools that never enabled
13
- -- Payments (sequences absent) and idempotent on pools already at RLS ON. Fresh
14
- -- installs are born with RLS via the financial plugin's own 004_order_to_cash.sql.
15
- --
16
- -- NOTE: next_sequence() MUST be SECURITY DEFINER for the deny-all baseline to
17
- -- keep working — it is called directly via PostgREST RPC by authenticated users
18
- -- (plugins/plugin-financial/src/data/supabase.ts → createInvoice). That fix ships
19
- -- in the financial plugin migration alongside this one.
20
- -- ============================================================================
21
-
22
- ALTER TABLE IF EXISTS public.sequences ENABLE ROW LEVEL SECURITY;
@@ -1,28 +0,0 @@
1
- -- ============================================================================
2
- -- 014_plan_entitlements.sql — plans carry machine-readable entitlements.
3
- --
4
- -- public.plans shipped with `features jsonb` as DISPLAY BULLETS only — nothing
5
- -- in the pool encodes what a plan actually gates. Server-side enforcement (the
6
- -- agent RPCs' role→plan→limit guard, and eventually RLS defense-in-depth)
7
- -- needs the SAME `{features: {id: bool}, limits: {key: number}}` structure the
8
- -- client resolves from `config.billing` (@fayz-ai/core PlanEntitlements; -1 =
9
- -- unlimited, absent feature = allowed).
10
- --
11
- -- Writer: the app's plan catalog is code (billing.ts) → synced into this table
12
- -- (transitionally by the app seed, then by the Fayz platform on
13
- -- `fayz manifest sync` — the manifest carries billing.plans verbatim).
14
- --
15
- -- `hidden` retires the "qa-free-test plans visible on SubscriptionPage" debt:
16
- -- QA/internal plans stay selectable by id but are never rendered.
17
- --
18
- -- Guarded + idempotent: no-op when the pool predates public.plans (legacy
19
- -- saas_core pools run their app-local mirror of this ALTER instead).
20
- -- ============================================================================
21
-
22
- DO $$
23
- BEGIN
24
- IF to_regclass('public.plans') IS NOT NULL THEN
25
- ALTER TABLE public.plans ADD COLUMN IF NOT EXISTS entitlements jsonb NOT NULL DEFAULT '{}'::jsonb;
26
- ALTER TABLE public.plans ADD COLUMN IF NOT EXISTS hidden boolean NOT NULL DEFAULT false;
27
- END IF;
28
- END $$;