@fayz-ai/db 0.13.0 → 0.15.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/canonical-schema.json +106 -23
- package/dist/index.cjs +4 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/schema/spine.d.ts +42 -37
- package/dist/schema/spine.d.ts.map +1 -1
- package/migrations/000_baseline.sql +29667 -0
- package/migrations/001_the_tenant_has_apps.sql +314 -0
- package/migrations/002_the_spine_drops_the_vendor_and_the_dead.sql +129 -0
- package/migrations/003_the_role_rpcs_finish_and_the_mirrors_go.sql +135 -0
- package/migrations/004_the_projections_get_a_successor.sql +312 -0
- package/migrations/005_the_archetypes_anchor_to_the_unit.sql +90 -0
- package/migrations/006_the_helpers_read_the_source.sql +153 -0
- package/migrations/007_the_compat_machinery_leaves_with_the_tables.sql +77 -0
- package/migrations/008_the_switchers_see_across_accounts.sql +150 -0
- package/migrations/009_the_compat_aliases_nobody_adopted.sql +57 -0
- package/migrations/010_the_new_tenant_is_born_with_its_trade.sql +201 -0
- package/migrations/011_leadcontrol_joins_the_family.sql +120 -0
- package/migrations/012_chefcontrol_wears_its_own_mark.sql +28 -0
- package/migrations/013_the_workspace_switch_reaches_the_other_half.sql +156 -0
- package/migrations/014_the_chooser_can_read_what_it_offers.sql +91 -0
- package/migrations/015_the_unit_bindings_belong_to_one_workspace.sql +86 -0
- package/migrations/016_the_rule_stops_asking_every_row.sql +543 -0
- package/migrations/017_the_pin_is_checked_against_the_right_workspace.sql +139 -0
- package/migrations/018_the_bootstrap_says_what_you_reach.sql +114 -0
- package/migrations/019_distributing_one_row_stops_costing_the_table.sql +245 -0
- package/migrations/020_the_agenda_asks_where_someone_works.sql +81 -0
- package/migrations/022_the_line_says_which_document_it_belongs_to.sql +109 -0
- package/migrations/029_o_administrador_enxerga_o_que_existe.sql +167 -0
- package/migrations/031_ticketcontrol_joins_the_family.sql +109 -0
- package/migrations/032_a_marca_e_um_conjunto_de_tokens.sql +291 -0
- package/migrations/041_a_entrega_tem_onde_guardar_a_politica.sql +53 -0
- package/migrations/042_o_token_de_marca_fecha_a_porta_anon.sql +17 -0
- package/migrations/043_fiscalcontrol_joins_the_family.sql +75 -0
- package/migrations/044_o_teste_gratis_dura_trinta_dias.sql +15 -0
- package/migrations/046_the_tick_advances_the_flows.sql +77 -0
- package/migrations/047_the_tick_sweeps_the_offers.sql +74 -0
- package/migrations/052_fullcontrol_joins_the_family.sql +213 -0
- package/migrations/053_o_erp_para_de_exigir_o_que_nao_desenha.sql +37 -0
- package/package.json +1 -1
- package/migrations/0000_legacy_quarantine.sql +0 -86
- package/migrations/0000b_straggler_sweep.sql +0 -37
- package/migrations/000_core_v1_convert.sql +0 -343
- package/migrations/001_core.sql +0 -372
- package/migrations/002_rls_user_tenant_ids.sql +0 -64
- package/migrations/003_invitations.sql +0 -17
- package/migrations/004_archetypes.sql +0 -343
- package/migrations/005_locations_archetype.sql +0 -18
- package/migrations/006_archetype_rls.sql +0 -74
- package/migrations/007_archetype_grants.sql +0 -4
- package/migrations/008_grants.sql +0 -5
- package/migrations/009_anon_hardening.sql +0 -19
- package/migrations/010_migration_ledger.sql +0 -46
- package/migrations/011_anon_write_revoke.sql +0 -12
- package/migrations/012_rename_migration_ledger.sql +0 -61
- package/migrations/013_sequences_rls.sql +0 -22
- package/migrations/014_plan_entitlements.sql +0 -28
- package/migrations/015_agent_guard.sql +0 -108
- package/migrations/016_agent_guard_actor.sql +0 -102
- package/migrations/017_core_addresses.sql +0 -115
- package/migrations/018_global_search.sql +0 -482
- package/migrations/019_tenant_members_person_id.sql +0 -20
- package/migrations/020_fayz_projects.sql +0 -63
- package/migrations/021_tenant_roles.sql +0 -74
- package/migrations/022_invite_acceptance.sql +0 -91
- package/migrations/023_team_visibility.sql +0 -75
- package/migrations/024_analytics_engine.sql +0 -317
- package/migrations/025_created_by.sql +0 -75
- package/migrations/026_audit_trail.sql +0 -73
- package/migrations/027_domain_events.sql +0 -266
- package/migrations/028_tenant_scoped_token.sql +0 -127
- package/migrations/029_connections.sql +0 -186
- package/migrations/030_effect_idempotency.sql +0 -159
- package/migrations/031_sync_run_message.sql +0 -39
- package/migrations/032_connection_secrets.sql +0 -227
- package/migrations/033_sync_schedule.sql +0 -651
- package/migrations/034_custom_fields.sql +0 -55
- package/migrations/035_field_registry.sql +0 -148
- package/migrations/036_analytics_run_batch.sql +0 -84
- package/migrations/037_sync_tick_one_at_a_time.sql +0 -256
- package/migrations/038_onboarding_responses.sql +0 -103
- package/migrations/039_unit_tree.sql +0 -270
- package/migrations/040_resource_grants.sql +0 -474
- package/migrations/041_scoped_columns.sql +0 -192
- package/migrations/042_unit_scope_policies.sql +0 -145
- package/migrations/043_view_invoker.sql +0 -90
- package/migrations/044_unit_member_facts.sql +0 -47
- package/migrations/045_unit_entry.sql +0 -236
- package/migrations/046_membership_visible_to_members.sql +0 -85
- package/migrations/047_tasks.sql +0 -266
- package/migrations/048_every_login_is_a_person.sql +0 -190
- package/migrations/049_bookable_people.sql +0 -126
- package/migrations/050_authz_core.sql +0 -230
- package/migrations/051_authz_helpers.sql +0 -330
- package/migrations/052_authz_fanout.sql +0 -253
- package/migrations/053_authz_catalog_templates.sql +0 -402
- package/migrations/060_tenancy_seam.sql +0 -132
- package/migrations/061_create_tenant.sql +0 -125
- package/migrations/062_invitations.sql +0 -329
- package/migrations/063_legacy_tenancy_retired.sql +0 -568
- package/migrations/064_agent_guard_authz.sql +0 -118
- package/migrations/064b_supersede_unit_scope.sql +0 -205
- package/migrations/065_scaffold_sweep.sql +0 -139
- package/migrations/066_shadow_report.sql +0 -122
- package/migrations/070_permission_catalog.sql +0 -205
- package/migrations/071_v1_permission_map.sql +0 -266
- package/migrations/072_role_rpcs.sql +0 -126
- package/migrations/073_legacy_rbac_retire.sql +0 -471
- package/migrations/075_company_legal_entities.sql +0 -169
- package/migrations/076_tenant_settings.sql +0 -251
- package/migrations/080_people_roles.sql +0 -269
- package/migrations/081_people_identity.sql +0 -271
- package/migrations/082_people_membership_link.sql +0 -142
- package/migrations/083_people_data_migration.sql +0 -75
- package/migrations/084_people_registrations_tokens.sql +0 -282
- package/migrations/085_catalog_products.sql +0 -333
- package/migrations/086_catalog_pricing.sql +0 -194
- package/migrations/087_catalog_packages.sql +0 -134
- package/migrations/090_orders_venda.sql +0 -166
- package/migrations/091_orders_scaffold.sql +0 -92
- package/migrations/095_config_field_rules.sql +0 -261
- package/migrations/096_audit_append_only.sql +0 -256
- package/migrations/097_sequences_authorized.sql +0 -111
- package/migrations/098_holidays.sql +0 -80
- package/migrations/099_access_grants.sql +0 -293
- package/migrations/100_migration_schema.sql +0 -217
- package/migrations/101_migration_allowlist.sql +0 -202
- package/migrations/102_migration_rpcs.sql +0 -933
- package/migrations/103_migration_reconcile_ponr.sql +0 -132
- package/migrations/105_policies_to_authenticated.sql +0 -71
- package/migrations/106_function_search_path.sql +0 -55
- package/migrations/107_core_table_prefix.sql +0 -361
- package/migrations/108_authz_write_guards.sql +0 -149
- package/migrations/109_catalog_migration_promoted_columns.sql +0 -126
- package/migrations/110_people_kind_vocabulary.sql +0 -120
- package/migrations/111_migration_excluded_is_a_decision.sql +0 -259
- package/migrations/111_replacing_a_view_keeps_its_dependents.sql +0 -132
- package/migrations/112_money_precision_standard.sql +0 -132
- package/migrations/113_addresses_units_companies.sql +0 -114
- package/migrations/114_migrations_ledger_immutable.sql +0 -65
- package/migrations/115_registration_honours_field_rules.sql +0 -172
- package/migrations/116_field_rules_check_needs_a_tenant.sql +0 -72
- package/migrations/117_authz_reconciliation_schedule.sql +0 -82
- package/migrations/118_migration_catalog_writers.sql +0 -450
- package/migrations/119_migration_config_writers.sql +0 -193
- package/migrations/120_migration_facet_writer.sql +0 -139
- package/migrations/121_migration_financial_writers.sql +0 -294
- package/migrations/122_migration_inventory_writers.sql +0 -245
- package/migrations/123_migration_order_item_writer.sql +0 -144
- package/migrations/124_migration_ledger_writers.sql +0 -170
- package/migrations/125_migration_authz_people_writers.sql +0 -205
- package/migrations/126_owner_scoping_is_configuration.sql +0 -247
- package/migrations/127_scaffold_audit_hook.sql +0 -98
- package/migrations/128_shadow_tables_enforced.sql +0 -93
- package/migrations/129_order_kind_terminal_states.sql +0 -57
- package/migrations/130_every_login_is_a_person.sql +0 -196
- package/migrations/131_migration_invoice_split.sql +0 -127
- package/migrations/132_audit_prune_scheduled.sql +0 -28
- package/migrations/133_unit_is_a_session_property.sql +0 -155
- package/migrations/134_field_rules_on_read.sql +0 -341
- package/migrations/135_distribution_polarity.sql +0 -158
- package/migrations/136_unit_id_means_locality.sql +0 -358
- package/migrations/137_vendor_named_spine_objects.sql +0 -126
- package/migrations/138_grants_inherit_down_the_tree.sql +0 -178
- package/migrations/139_distributable_is_a_scaffold_option.sql +0 -109
- package/migrations/140_masking_is_a_scaffold_option.sql +0 -85
- package/migrations/141_renamed_objects_keep_a_compat_name.sql +0 -192
- package/migrations/142_a_person_has_addresses.sql +0 -212
- package/migrations/143_a_professional_has_a_rate.sql +0 -75
- package/migrations/144_a_product_can_be_sold_in_sizes.sql +0 -57
- package/migrations/145_the_app_says_which_tenant_it_is_in.sql +0 -123
- package/migrations/146_products_stock_has_one_writer.sql +0 -67
- package/migrations/147_every_archetype_has_a_kind_registry.sql +0 -388
- package/migrations/148_the_legacy_archetype_stops_being_writable.sql +0 -154
- package/migrations/149_the_event_log_gets_a_consumer.sql +0 -331
- package/migrations/150_the_app_is_configured_in_the_database.sql +0 -285
- package/migrations/151_stock_balance_is_an_archetype.sql +0 -144
- package/migrations/152_a_file_is_an_archetype.sql +0 -222
- package/migrations/153_a_plugin_calls_a_service_through_one_door.sql +0 -199
- package/migrations/154_the_plan_is_a_ceiling.sql +0 -258
- package/migrations/155_a_tenant_can_leave.sql +0 -271
- package/migrations/156_the_service_door_has_a_server_side.sql +0 -335
- package/migrations/157_nobody_creates_objects_in_public.sql +0 -74
- package/migrations/158_the_registry_owns_the_status_vocabulary.sql +0 -153
- package/migrations/159_the_plan_catalogue_gets_its_first_rows.sql +0 -132
- package/migrations/160_the_vertical_decides_what_starts_on.sql +0 -246
- package/migrations/161_a_tenant_leaves_with_its_data.sql +0 -363
- package/migrations/162_every_name_has_one_creator_for_real.sql +0 -126
- package/migrations/163_a_species_nobody_declared_goes_to_quarantine.sql +0 -184
- package/migrations/164_the_sequence_table_joins_the_register.sql +0 -26
- package/migrations/165_a_view_is_not_a_way_in.sql +0 -90
- package/migrations/166_the_platform_billing_leftovers_go.sql +0 -72
- package/migrations/167_a_view_gets_back_what_its_table_grants.sql +0 -112
- package/migrations/168_the_grant_census_stops_crying_wolf.sql +0 -33
- package/migrations/169_the_census_asks_postgres_instead_of_guessing.sql +0 -45
|
@@ -1,343 +0,0 @@
|
|
|
1
|
-
-- ============================================================================
|
|
2
|
-
-- 000_core_v1_convert.sql — in-place converter: saas_core -> public
|
|
3
|
-
--
|
|
4
|
-
-- For EXISTING pools that already hold live data in the saas_core schema.
|
|
5
|
-
-- Moves every core table into public, renames the three entities that changed
|
|
6
|
-
-- (persons->people, bookings->appointments, booking_items->appointment_items),
|
|
7
|
-
-- re-authors the core functions/triggers/policies in public terms, then drops
|
|
8
|
-
-- the saas_core schema and re-points PostgREST at public only.
|
|
9
|
-
--
|
|
10
|
-
-- Idempotent + partial-re-run safe: guarded by the existence of the saas_core
|
|
11
|
-
-- schema, and every table move/rename is individually guarded with to_regclass.
|
|
12
|
-
--
|
|
13
|
-
-- Relies on Postgres semantics:
|
|
14
|
-
-- * ALTER TABLE ... SET SCHEMA preserves data / PKs / FKs / indexes /
|
|
15
|
-
-- policies / grants (only the containing schema changes).
|
|
16
|
-
-- * ALTER TABLE ... RENAME cascades FK / index / policy references.
|
|
17
|
-
-- * Function BODIES and trigger function references do NOT auto-follow a
|
|
18
|
-
-- schema move -> they are re-emitted below before saas_core is dropped.
|
|
19
|
-
--
|
|
20
|
-
-- ORDERING NOTE: 0000_legacy_quarantine.sql MUST be applied BEFORE this file.
|
|
21
|
-
-- On any pool that already holds a legacy public.<target> table (e.g. a bespoke
|
|
22
|
-
-- public.appointments, or a real public.subscriptions), that collision would
|
|
23
|
-
-- block the corresponding move/rename here (each is guarded on target absence),
|
|
24
|
-
-- leaving the live saas_core.<source> stranded for the CASCADE below to destroy.
|
|
25
|
-
-- The quarantine step moves such collisions into legacy_pre_pools first.
|
|
26
|
-
-- ============================================================================
|
|
27
|
-
|
|
28
|
-
DO $convert$
|
|
29
|
-
DECLARE
|
|
30
|
-
t text;
|
|
31
|
-
r record;
|
|
32
|
-
v_roles text;
|
|
33
|
-
v_using text;
|
|
34
|
-
v_check text;
|
|
35
|
-
v_sql text;
|
|
36
|
-
core_tables text[] := ARRAY[
|
|
37
|
-
'tenants', 'profiles', 'tenant_members', 'plans', 'permissions',
|
|
38
|
-
'role_permissions', 'tenant_role_overrides', 'invitations',
|
|
39
|
-
'payment_events', 'locations', 'location_members', 'audit_logs',
|
|
40
|
-
'subscriptions', 'invoices', 'notifications',
|
|
41
|
-
'persons', 'categories', 'products', 'services', 'orders', 'order_items',
|
|
42
|
-
'transactions', 'bookings', 'booking_items', 'schedules',
|
|
43
|
-
-- may or may not exist depending on installed plugins (stay core, unprefixed)
|
|
44
|
-
'sequences', 'documents'
|
|
45
|
-
];
|
|
46
|
-
-- post-move / post-rename public names carrying an updated_at trigger
|
|
47
|
-
ts_tables text[] := ARRAY[
|
|
48
|
-
'tenants', 'profiles', 'locations', 'subscriptions', 'people', 'categories',
|
|
49
|
-
'products', 'services', 'orders', 'transactions', 'appointments', 'schedules'
|
|
50
|
-
];
|
|
51
|
-
-- post-move / post-rename public names governed by generic tenant isolation
|
|
52
|
-
archetype_tables text[] := ARRAY[
|
|
53
|
-
'people', 'categories', 'products', 'services',
|
|
54
|
-
'orders', 'transactions', 'appointments', 'schedules'
|
|
55
|
-
];
|
|
56
|
-
-- every core table that must be re-policied in public terms
|
|
57
|
-
policy_tables text[];
|
|
58
|
-
BEGIN
|
|
59
|
-
-- Only act on pools that still have the legacy schema.
|
|
60
|
-
IF NOT EXISTS (SELECT 1 FROM information_schema.schemata WHERE schema_name = 'saas_core') THEN
|
|
61
|
-
RETURN;
|
|
62
|
-
END IF;
|
|
63
|
-
|
|
64
|
-
-- --------------------------------------------------------------------------
|
|
65
|
-
-- 1. Move every core table saas_core.* -> public.* (guarded, idempotent).
|
|
66
|
-
-- --------------------------------------------------------------------------
|
|
67
|
-
FOREACH t IN ARRAY core_tables LOOP
|
|
68
|
-
IF to_regclass('saas_core.' || quote_ident(t)) IS NOT NULL
|
|
69
|
-
AND to_regclass('public.' || quote_ident(t)) IS NULL THEN
|
|
70
|
-
EXECUTE format('ALTER TABLE saas_core.%I SET SCHEMA public', t);
|
|
71
|
-
END IF;
|
|
72
|
-
END LOOP;
|
|
73
|
-
|
|
74
|
-
-- --------------------------------------------------------------------------
|
|
75
|
-
-- 2. Rename the three entities that changed (only if source present, target free).
|
|
76
|
-
-- --------------------------------------------------------------------------
|
|
77
|
-
IF to_regclass('public.persons') IS NOT NULL AND to_regclass('public.people') IS NULL THEN
|
|
78
|
-
ALTER TABLE public.persons RENAME TO people;
|
|
79
|
-
END IF;
|
|
80
|
-
IF to_regclass('public.bookings') IS NOT NULL AND to_regclass('public.appointments') IS NULL THEN
|
|
81
|
-
ALTER TABLE public.bookings RENAME TO appointments;
|
|
82
|
-
END IF;
|
|
83
|
-
IF to_regclass('public.booking_items') IS NOT NULL AND to_regclass('public.appointment_items') IS NULL THEN
|
|
84
|
-
ALTER TABLE public.booking_items RENAME TO appointment_items;
|
|
85
|
-
END IF;
|
|
86
|
-
|
|
87
|
-
-- --------------------------------------------------------------------------
|
|
88
|
-
-- 3. Re-emit the core functions in public (bodies do not follow a move).
|
|
89
|
-
-- MUST run after the moves/renames: LANGUAGE sql bodies are validated at
|
|
90
|
-
-- CREATE time, so public.tenant_members etc. have to exist already.
|
|
91
|
-
-- --------------------------------------------------------------------------
|
|
92
|
-
EXECUTE $q$
|
|
93
|
-
CREATE OR REPLACE FUNCTION public.handle_updated_at()
|
|
94
|
-
RETURNS trigger LANGUAGE plpgsql AS $fn$
|
|
95
|
-
BEGIN NEW.updated_at = now(); RETURN NEW; END; $fn$;
|
|
96
|
-
$q$;
|
|
97
|
-
|
|
98
|
-
EXECUTE $q$
|
|
99
|
-
CREATE OR REPLACE FUNCTION public.handle_new_user()
|
|
100
|
-
RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER AS $fn$
|
|
101
|
-
BEGIN
|
|
102
|
-
INSERT INTO public.profiles (id, email, full_name, avatar_url)
|
|
103
|
-
VALUES (NEW.id, NEW.email, NEW.raw_user_meta_data->>'full_name', NEW.raw_user_meta_data->>'avatar_url')
|
|
104
|
-
ON CONFLICT (id) DO NOTHING;
|
|
105
|
-
RETURN NEW;
|
|
106
|
-
END; $fn$;
|
|
107
|
-
$q$;
|
|
108
|
-
|
|
109
|
-
EXECUTE $q$
|
|
110
|
-
CREATE OR REPLACE FUNCTION public.create_tenant_with_owner(
|
|
111
|
-
p_name text, p_slug text, p_user_id uuid,
|
|
112
|
-
p_vertical_id text DEFAULT NULL, p_plan text DEFAULT 'free', p_settings jsonb DEFAULT '{}'
|
|
113
|
-
)
|
|
114
|
-
RETURNS jsonb LANGUAGE plpgsql SECURITY DEFINER AS $fn$
|
|
115
|
-
DECLARE v_id uuid; v_tenant jsonb;
|
|
116
|
-
BEGIN
|
|
117
|
-
INSERT INTO public.tenants (name, slug, plan, vertical_id, settings)
|
|
118
|
-
VALUES (p_name, p_slug, p_plan, p_vertical_id, p_settings) RETURNING id INTO v_id;
|
|
119
|
-
INSERT INTO public.tenant_members (tenant_id, user_id, role)
|
|
120
|
-
VALUES (v_id, p_user_id, 'owner');
|
|
121
|
-
SELECT to_jsonb(t) INTO v_tenant FROM public.tenants t WHERE t.id = v_id;
|
|
122
|
-
RETURN v_tenant;
|
|
123
|
-
END; $fn$;
|
|
124
|
-
$q$;
|
|
125
|
-
|
|
126
|
-
EXECUTE $q$
|
|
127
|
-
CREATE OR REPLACE FUNCTION public.is_tenant_admin(p_tenant_id uuid)
|
|
128
|
-
RETURNS boolean LANGUAGE sql STABLE SECURITY DEFINER AS $fn$
|
|
129
|
-
SELECT EXISTS (
|
|
130
|
-
SELECT 1 FROM public.tenant_members
|
|
131
|
-
WHERE tenant_id = p_tenant_id AND user_id = auth.uid() AND role IN ('owner', 'admin')
|
|
132
|
-
);
|
|
133
|
-
$fn$;
|
|
134
|
-
$q$;
|
|
135
|
-
|
|
136
|
-
EXECUTE $q$
|
|
137
|
-
CREATE OR REPLACE FUNCTION public.user_tenant_ids()
|
|
138
|
-
RETURNS SETOF uuid LANGUAGE sql STABLE SECURITY DEFINER AS $fn$
|
|
139
|
-
SELECT tenant_id FROM public.tenant_members WHERE user_id = auth.uid();
|
|
140
|
-
$fn$;
|
|
141
|
-
$q$;
|
|
142
|
-
|
|
143
|
-
-- Re-point the auth.users trigger at the public function.
|
|
144
|
-
DROP TRIGGER IF EXISTS on_auth_user_created ON auth.users;
|
|
145
|
-
CREATE TRIGGER on_auth_user_created AFTER INSERT ON auth.users
|
|
146
|
-
FOR EACH ROW EXECUTE FUNCTION public.handle_new_user();
|
|
147
|
-
|
|
148
|
-
-- --------------------------------------------------------------------------
|
|
149
|
-
-- 4. Re-create updated_at triggers to call public.handle_updated_at.
|
|
150
|
-
-- --------------------------------------------------------------------------
|
|
151
|
-
FOREACH t IN ARRAY ts_tables LOOP
|
|
152
|
-
IF to_regclass('public.' || quote_ident(t)) IS NOT NULL THEN
|
|
153
|
-
EXECUTE format('DROP TRIGGER IF EXISTS %I ON public.%I', t || '_updated_at', t);
|
|
154
|
-
EXECUTE format(
|
|
155
|
-
'CREATE TRIGGER %I BEFORE UPDATE ON public.%I FOR EACH ROW EXECUTE FUNCTION public.handle_updated_at()',
|
|
156
|
-
t || '_updated_at', t
|
|
157
|
-
);
|
|
158
|
-
END IF;
|
|
159
|
-
END LOOP;
|
|
160
|
-
-- Legacy trigger names carried over by the RENAME.
|
|
161
|
-
IF to_regclass('public.people') IS NOT NULL THEN
|
|
162
|
-
DROP TRIGGER IF EXISTS persons_updated_at ON public.people;
|
|
163
|
-
END IF;
|
|
164
|
-
IF to_regclass('public.appointments') IS NOT NULL THEN
|
|
165
|
-
DROP TRIGGER IF EXISTS bookings_updated_at ON public.appointments;
|
|
166
|
-
END IF;
|
|
167
|
-
|
|
168
|
-
-- --------------------------------------------------------------------------
|
|
169
|
-
-- 5. Re-emit RLS policies in public terms.
|
|
170
|
-
-- Drop EVERY existing policy on the moved tables (their bodies still
|
|
171
|
-
-- reference saas_core.*) and recreate the canonical public policy set —
|
|
172
|
-
-- identical end-state to a fresh baseline (001 + 006).
|
|
173
|
-
-- --------------------------------------------------------------------------
|
|
174
|
-
policy_tables := core_tables
|
|
175
|
-
|| ARRAY['people', 'appointments', 'appointment_items']::text[];
|
|
176
|
-
|
|
177
|
-
FOR r IN
|
|
178
|
-
SELECT policyname, tablename FROM pg_policies
|
|
179
|
-
WHERE schemaname = 'public' AND tablename = ANY(policy_tables)
|
|
180
|
-
LOOP
|
|
181
|
-
EXECUTE format('DROP POLICY IF EXISTS %I ON public.%I', r.policyname, r.tablename);
|
|
182
|
-
END LOOP;
|
|
183
|
-
|
|
184
|
-
-- tenants
|
|
185
|
-
CREATE POLICY "tenants_select" ON public.tenants FOR SELECT
|
|
186
|
-
USING (id IN (SELECT tenant_id FROM public.tenant_members WHERE user_id = auth.uid()));
|
|
187
|
-
CREATE POLICY "tenants_insert" ON public.tenants FOR INSERT TO authenticated WITH CHECK (true);
|
|
188
|
-
CREATE POLICY "tenants_update" ON public.tenants FOR UPDATE USING (public.is_tenant_admin(id));
|
|
189
|
-
|
|
190
|
-
-- profiles
|
|
191
|
-
CREATE POLICY "profiles_select" ON public.profiles FOR SELECT USING (id = auth.uid());
|
|
192
|
-
CREATE POLICY "profiles_insert" ON public.profiles FOR INSERT WITH CHECK (id = auth.uid());
|
|
193
|
-
CREATE POLICY "profiles_update" ON public.profiles FOR UPDATE USING (id = auth.uid());
|
|
194
|
-
|
|
195
|
-
-- tenant_members
|
|
196
|
-
CREATE POLICY "members_select" ON public.tenant_members FOR SELECT USING (user_id = auth.uid());
|
|
197
|
-
CREATE POLICY "members_insert_own" ON public.tenant_members FOR INSERT WITH CHECK (user_id = auth.uid());
|
|
198
|
-
CREATE POLICY "members_update" ON public.tenant_members FOR UPDATE USING (public.is_tenant_admin(tenant_id));
|
|
199
|
-
CREATE POLICY "members_delete" ON public.tenant_members FOR DELETE USING (public.is_tenant_admin(tenant_id));
|
|
200
|
-
|
|
201
|
-
-- plans / permissions / role_permissions
|
|
202
|
-
CREATE POLICY "plans_select" ON public.plans FOR SELECT TO authenticated USING (true);
|
|
203
|
-
CREATE POLICY "perms_select" ON public.permissions FOR SELECT TO authenticated USING (true);
|
|
204
|
-
CREATE POLICY "role_perms_select" ON public.role_permissions FOR SELECT TO authenticated USING (true);
|
|
205
|
-
|
|
206
|
-
-- tenant_role_overrides
|
|
207
|
-
CREATE POLICY "overrides_select" ON public.tenant_role_overrides FOR SELECT
|
|
208
|
-
USING (tenant_id IN (SELECT tenant_id FROM public.tenant_members WHERE user_id = auth.uid()));
|
|
209
|
-
CREATE POLICY "overrides_manage" ON public.tenant_role_overrides FOR ALL
|
|
210
|
-
USING (public.is_tenant_admin(tenant_id));
|
|
211
|
-
|
|
212
|
-
-- invitations
|
|
213
|
-
CREATE POLICY "invites_select" ON public.invitations FOR SELECT TO authenticated USING (true);
|
|
214
|
-
CREATE POLICY "invites_insert" ON public.invitations FOR INSERT WITH CHECK (public.is_tenant_admin(tenant_id));
|
|
215
|
-
CREATE POLICY "invites_update" ON public.invitations FOR UPDATE USING (public.is_tenant_admin(tenant_id));
|
|
216
|
-
CREATE POLICY "invites_delete" ON public.invitations FOR DELETE USING (public.is_tenant_admin(tenant_id));
|
|
217
|
-
|
|
218
|
-
-- payment_events
|
|
219
|
-
CREATE POLICY "events_service" ON public.payment_events FOR ALL TO service_role USING (true);
|
|
220
|
-
|
|
221
|
-
-- locations
|
|
222
|
-
CREATE POLICY "locations_select" ON public.locations FOR SELECT
|
|
223
|
-
USING (tenant_id IN (SELECT tenant_id FROM public.tenant_members WHERE user_id = auth.uid()));
|
|
224
|
-
CREATE POLICY "locations_manage" ON public.locations FOR ALL USING (public.is_tenant_admin(tenant_id));
|
|
225
|
-
|
|
226
|
-
-- location_members
|
|
227
|
-
CREATE POLICY "loc_members_select" ON public.location_members FOR SELECT
|
|
228
|
-
USING (location_id IN (SELECT id FROM public.locations WHERE tenant_id IN (
|
|
229
|
-
SELECT tenant_id FROM public.tenant_members WHERE user_id = auth.uid()
|
|
230
|
-
)));
|
|
231
|
-
|
|
232
|
-
-- subscriptions / invoices
|
|
233
|
-
CREATE POLICY "subs_select" ON public.subscriptions FOR SELECT
|
|
234
|
-
USING (tenant_id IN (SELECT tenant_id FROM public.tenant_members WHERE user_id = auth.uid()));
|
|
235
|
-
CREATE POLICY "invoices_select" ON public.invoices FOR SELECT
|
|
236
|
-
USING (tenant_id IN (SELECT tenant_id FROM public.tenant_members WHERE user_id = auth.uid()));
|
|
237
|
-
|
|
238
|
-
-- audit_logs
|
|
239
|
-
CREATE POLICY "audit_select" ON public.audit_logs FOR SELECT
|
|
240
|
-
USING (tenant_id IN (SELECT tenant_id FROM public.tenant_members WHERE user_id = auth.uid()));
|
|
241
|
-
CREATE POLICY "audit_insert" ON public.audit_logs FOR INSERT TO authenticated WITH CHECK (true);
|
|
242
|
-
|
|
243
|
-
-- notifications
|
|
244
|
-
CREATE POLICY "notif_select" ON public.notifications FOR SELECT USING (user_id = auth.uid());
|
|
245
|
-
CREATE POLICY "notif_update" ON public.notifications FOR UPDATE USING (user_id = auth.uid());
|
|
246
|
-
|
|
247
|
-
-- archetype tables (generic tenant isolation via SECURITY DEFINER helper)
|
|
248
|
-
FOREACH t IN ARRAY archetype_tables LOOP
|
|
249
|
-
EXECUTE format('ALTER TABLE public.%I ENABLE ROW LEVEL SECURITY', t);
|
|
250
|
-
EXECUTE format('CREATE POLICY "%s_select" ON public.%I FOR SELECT TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()))', t, t);
|
|
251
|
-
EXECUTE format('CREATE POLICY "%s_insert" ON public.%I FOR INSERT TO authenticated WITH CHECK (tenant_id IN (SELECT public.user_tenant_ids()))', t, t);
|
|
252
|
-
EXECUTE format('CREATE POLICY "%s_update" ON public.%I FOR UPDATE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()))', t, t);
|
|
253
|
-
EXECUTE format('CREATE POLICY "%s_delete" ON public.%I FOR DELETE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()))', t, t);
|
|
254
|
-
END LOOP;
|
|
255
|
-
|
|
256
|
-
-- order_items (parent-FK policies)
|
|
257
|
-
CREATE POLICY "order_items_select" ON public.order_items FOR SELECT TO authenticated
|
|
258
|
-
USING (order_id IN (SELECT id FROM public.orders WHERE tenant_id IN (SELECT public.user_tenant_ids())));
|
|
259
|
-
CREATE POLICY "order_items_insert" ON public.order_items FOR INSERT TO authenticated
|
|
260
|
-
WITH CHECK (order_id IN (SELECT id FROM public.orders WHERE tenant_id IN (SELECT public.user_tenant_ids())));
|
|
261
|
-
CREATE POLICY "order_items_update" ON public.order_items FOR UPDATE TO authenticated
|
|
262
|
-
USING (order_id IN (SELECT id FROM public.orders WHERE tenant_id IN (SELECT public.user_tenant_ids())));
|
|
263
|
-
CREATE POLICY "order_items_delete" ON public.order_items FOR DELETE TO authenticated
|
|
264
|
-
USING (order_id IN (SELECT id FROM public.orders WHERE tenant_id IN (SELECT public.user_tenant_ids())));
|
|
265
|
-
|
|
266
|
-
-- appointment_items (parent-FK policies; booking_id column preserved by rename)
|
|
267
|
-
CREATE POLICY "appointment_items_select" ON public.appointment_items FOR SELECT TO authenticated
|
|
268
|
-
USING (booking_id IN (SELECT id FROM public.appointments WHERE tenant_id IN (SELECT public.user_tenant_ids())));
|
|
269
|
-
CREATE POLICY "appointment_items_insert" ON public.appointment_items FOR INSERT TO authenticated
|
|
270
|
-
WITH CHECK (booking_id IN (SELECT id FROM public.appointments WHERE tenant_id IN (SELECT public.user_tenant_ids())));
|
|
271
|
-
CREATE POLICY "appointment_items_update" ON public.appointment_items FOR UPDATE TO authenticated
|
|
272
|
-
USING (booking_id IN (SELECT id FROM public.appointments WHERE tenant_id IN (SELECT public.user_tenant_ids())));
|
|
273
|
-
CREATE POLICY "appointment_items_delete" ON public.appointment_items FOR DELETE TO authenticated
|
|
274
|
-
USING (booking_id IN (SELECT id FROM public.appointments WHERE tenant_id IN (SELECT public.user_tenant_ids())));
|
|
275
|
-
|
|
276
|
-
-- --------------------------------------------------------------------------
|
|
277
|
-
-- 5b. Re-point APP-table policies that still call saas_core.* helpers.
|
|
278
|
-
-- App-created tables (unknown to this converter — e.g. conversations,
|
|
279
|
-
-- conversation_messages) can carry RLS policies whose USING / WITH CHECK
|
|
280
|
-
-- bodies reference saas_core.user_tenant_ids() (FKs are OID-based and
|
|
281
|
-
-- survive the schema move, but policy bodies are text). DROP SCHEMA
|
|
282
|
-
-- saas_core CASCADE would cascade-drop such a policy, leaving its table
|
|
283
|
-
-- RLS-enabled with ZERO policies (a full lock-out). Rewrite each in
|
|
284
|
-
-- public terms first — GENERIC, driven by pg_policies text, never a list.
|
|
285
|
-
-- Idempotent: after the rewrite no body contains 'saas_core.', so a
|
|
286
|
-
-- re-run selects nothing.
|
|
287
|
-
-- --------------------------------------------------------------------------
|
|
288
|
-
FOR r IN
|
|
289
|
-
SELECT schemaname, tablename, policyname, permissive, roles, cmd, qual, with_check
|
|
290
|
-
FROM pg_policies
|
|
291
|
-
WHERE schemaname = 'public'
|
|
292
|
-
AND (
|
|
293
|
-
coalesce(qual, '') LIKE '%saas_core.%'
|
|
294
|
-
OR coalesce(with_check, '') LIKE '%saas_core.%'
|
|
295
|
-
)
|
|
296
|
-
LOOP
|
|
297
|
-
v_using := replace(coalesce(r.qual, ''), 'saas_core.', 'public.');
|
|
298
|
-
v_check := replace(coalesce(r.with_check, ''), 'saas_core.', 'public.');
|
|
299
|
-
|
|
300
|
-
-- Render the TO role list. PUBLIC is a keyword, never quote it.
|
|
301
|
-
SELECT string_agg(CASE WHEN role = 'public' THEN 'public' ELSE quote_ident(role) END, ', ')
|
|
302
|
-
INTO v_roles
|
|
303
|
-
FROM unnest(r.roles) AS role;
|
|
304
|
-
|
|
305
|
-
v_sql := format(
|
|
306
|
-
'CREATE POLICY %I ON public.%I AS %s FOR %s TO %s',
|
|
307
|
-
r.policyname, r.tablename, r.permissive, r.cmd, coalesce(v_roles, 'public')
|
|
308
|
-
);
|
|
309
|
-
IF r.qual IS NOT NULL THEN
|
|
310
|
-
v_sql := v_sql || format(' USING (%s)', v_using);
|
|
311
|
-
END IF;
|
|
312
|
-
IF r.with_check IS NOT NULL THEN
|
|
313
|
-
v_sql := v_sql || format(' WITH CHECK (%s)', v_check);
|
|
314
|
-
END IF;
|
|
315
|
-
|
|
316
|
-
EXECUTE format('DROP POLICY IF EXISTS %I ON public.%I', r.policyname, r.tablename);
|
|
317
|
-
EXECUTE v_sql;
|
|
318
|
-
RAISE NOTICE 're-pointed policy %.% to public.* helpers', r.tablename, r.policyname;
|
|
319
|
-
END LOOP;
|
|
320
|
-
|
|
321
|
-
-- --------------------------------------------------------------------------
|
|
322
|
-
-- 6. Drop the now-empty legacy schema (any residue is old wrappers/functions).
|
|
323
|
-
-- SAFETY GATE: refuse to CASCADE if any BASE TABLE is still in saas_core —
|
|
324
|
-
-- that means a move/rename above silently skipped (a name collision the
|
|
325
|
-
-- quarantine step missed), and dropping now would destroy live data.
|
|
326
|
-
-- --------------------------------------------------------------------------
|
|
327
|
-
IF EXISTS (
|
|
328
|
-
SELECT 1 FROM information_schema.tables
|
|
329
|
-
WHERE table_schema = 'saas_core' AND table_type = 'BASE TABLE'
|
|
330
|
-
) THEN
|
|
331
|
-
RAISE EXCEPTION 'saas_core still has tables after move — aborting before CASCADE (check name collisions / quarantine)';
|
|
332
|
-
END IF;
|
|
333
|
-
|
|
334
|
-
DROP SCHEMA IF EXISTS saas_core CASCADE;
|
|
335
|
-
|
|
336
|
-
-- --------------------------------------------------------------------------
|
|
337
|
-
-- 7. Point PostgREST at public only.
|
|
338
|
-
-- --------------------------------------------------------------------------
|
|
339
|
-
EXECUTE 'ALTER ROLE authenticator SET pgrst.db_schemas = ''public''';
|
|
340
|
-
END;
|
|
341
|
-
$convert$;
|
|
342
|
-
|
|
343
|
-
NOTIFY pgrst, 'reload config';
|