@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.
- 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 +116 -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/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,108 +0,0 @@
|
|
|
1
|
-
-- ============================================================================
|
|
2
|
-
-- 015_agent_guard.sql — the role→plan→limit gate for agent_* RPCs.
|
|
3
|
-
--
|
|
4
|
-
-- Every server-plane agent write RPC (agent_<domain>_<verb>) calls this FIRST.
|
|
5
|
-
-- It is the SQL mirror of the shared TS engine (@fayz-ai/core/access
|
|
6
|
-
-- resolveAccess/resolveLimit): role first (owner bypasses role, NOT plan;
|
|
7
|
-
-- `manage` satisfies any action; tenant_role_overrides win over role grants),
|
|
8
|
-
-- then plan feature gate (only explicit false denies), then the plan cap for
|
|
9
|
-
-- p_limit_key (absent / -1 = unlimited). The RPC counts its own domain rows
|
|
10
|
-
-- and passes p_used — the guard never runs dynamic SQL.
|
|
11
|
-
--
|
|
12
|
-
-- Returns NULL when allowed, or the AgentDenial jsonb mirror when denied:
|
|
13
|
-
-- {"allowed":false,"reason":"role"|"plan"|"limit",
|
|
14
|
-
-- "limit":{"key":..,"max":..,"used":..},"upgradeUrl":"/settings/subscription"}
|
|
15
|
-
-- A parity test on the app side runs the same scenarios through the TS engine
|
|
16
|
-
-- and through this function — divergence is a bug.
|
|
17
|
-
--
|
|
18
|
-
-- Permission ids use the `category.action` catalog form; grants live in
|
|
19
|
-
-- role_permissions (+ per-tenant tenant_role_overrides with granted boolean).
|
|
20
|
-
-- ============================================================================
|
|
21
|
-
|
|
22
|
-
CREATE OR REPLACE FUNCTION public.agent_guard(
|
|
23
|
-
p_tenant_id uuid,
|
|
24
|
-
p_actor_user_id uuid,
|
|
25
|
-
p_feature text,
|
|
26
|
-
p_action text,
|
|
27
|
-
p_limit_key text DEFAULT NULL,
|
|
28
|
-
p_used integer DEFAULT NULL,
|
|
29
|
-
p_n integer DEFAULT 1
|
|
30
|
-
) RETURNS jsonb
|
|
31
|
-
LANGUAGE plpgsql STABLE SECURITY DEFINER
|
|
32
|
-
SET search_path = public
|
|
33
|
-
AS $$
|
|
34
|
-
DECLARE
|
|
35
|
-
v_role text;
|
|
36
|
-
v_has boolean;
|
|
37
|
-
v_override boolean;
|
|
38
|
-
v_plan_id text;
|
|
39
|
-
v_ent jsonb;
|
|
40
|
-
v_feature_flag jsonb;
|
|
41
|
-
v_cap numeric;
|
|
42
|
-
BEGIN
|
|
43
|
-
-- ── membership + role ────────────────────────────────────────────────────
|
|
44
|
-
SELECT tm.role INTO v_role
|
|
45
|
-
FROM tenant_members tm
|
|
46
|
-
WHERE tm.tenant_id = p_tenant_id AND tm.user_id = p_actor_user_id;
|
|
47
|
-
IF v_role IS NULL THEN
|
|
48
|
-
RETURN jsonb_build_object('allowed', false, 'reason', 'role',
|
|
49
|
-
'upgradeUrl', '/settings/subscription');
|
|
50
|
-
END IF;
|
|
51
|
-
|
|
52
|
-
IF v_role <> 'owner' THEN
|
|
53
|
-
-- per-tenant override wins in BOTH directions (grant or deny)
|
|
54
|
-
SELECT tro.granted INTO v_override
|
|
55
|
-
FROM tenant_role_overrides tro
|
|
56
|
-
WHERE tro.tenant_id = p_tenant_id AND tro.role = v_role
|
|
57
|
-
AND tro.permission_id IN (p_feature || '.' || p_action, p_feature || '.manage')
|
|
58
|
-
ORDER BY (tro.permission_id = p_feature || '.' || p_action) DESC
|
|
59
|
-
LIMIT 1;
|
|
60
|
-
|
|
61
|
-
IF v_override IS NOT NULL THEN
|
|
62
|
-
v_has := v_override;
|
|
63
|
-
ELSE
|
|
64
|
-
SELECT EXISTS (
|
|
65
|
-
SELECT 1 FROM role_permissions rp
|
|
66
|
-
WHERE rp.role = v_role
|
|
67
|
-
AND rp.permission_id IN (p_feature || '.' || p_action, p_feature || '.manage')
|
|
68
|
-
) INTO v_has;
|
|
69
|
-
END IF;
|
|
70
|
-
|
|
71
|
-
IF NOT v_has THEN
|
|
72
|
-
RETURN jsonb_build_object('allowed', false, 'reason', 'role',
|
|
73
|
-
'upgradeUrl', '/settings/subscription');
|
|
74
|
-
END IF;
|
|
75
|
-
END IF;
|
|
76
|
-
|
|
77
|
-
-- ── plan feature gate (owner does NOT bypass) ────────────────────────────
|
|
78
|
-
SELECT t.plan INTO v_plan_id FROM tenants t WHERE t.id = p_tenant_id;
|
|
79
|
-
SELECT p.entitlements INTO v_ent FROM plans p WHERE p.id = v_plan_id;
|
|
80
|
-
|
|
81
|
-
IF v_ent IS NOT NULL THEN
|
|
82
|
-
v_feature_flag := v_ent->'features'->p_feature;
|
|
83
|
-
-- only an EXPLICIT false denies (additive plans — mirror isEntitledByPlan)
|
|
84
|
-
IF v_feature_flag IS NOT NULL AND v_feature_flag = to_jsonb(false) THEN
|
|
85
|
-
RETURN jsonb_build_object('allowed', false, 'reason', 'plan',
|
|
86
|
-
'upgradeUrl', '/settings/subscription');
|
|
87
|
-
END IF;
|
|
88
|
-
|
|
89
|
-
-- ── plan cap (mirror resolveLimit: absent / -1 = unlimited) ────────────
|
|
90
|
-
IF p_limit_key IS NOT NULL AND p_used IS NOT NULL THEN
|
|
91
|
-
v_cap := (v_ent->'limits'->>p_limit_key)::numeric;
|
|
92
|
-
IF v_cap IS NOT NULL AND v_cap <> -1 AND (p_used + COALESCE(p_n, 1)) > v_cap THEN
|
|
93
|
-
RETURN jsonb_build_object('allowed', false, 'reason', 'limit',
|
|
94
|
-
'limit', jsonb_build_object('key', p_limit_key,
|
|
95
|
-
'max', v_cap,
|
|
96
|
-
'used', p_used),
|
|
97
|
-
'upgradeUrl', '/settings/subscription');
|
|
98
|
-
END IF;
|
|
99
|
-
END IF;
|
|
100
|
-
END IF;
|
|
101
|
-
|
|
102
|
-
RETURN NULL; -- allowed
|
|
103
|
-
END;
|
|
104
|
-
$$;
|
|
105
|
-
|
|
106
|
-
REVOKE ALL ON FUNCTION public.agent_guard(uuid, uuid, text, text, text, integer, integer) FROM public;
|
|
107
|
-
GRANT EXECUTE ON FUNCTION public.agent_guard(uuid, uuid, text, text, text, integer, integer)
|
|
108
|
-
TO authenticated, service_role;
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
-- ============================================================================
|
|
2
|
-
-- 016_agent_guard_actor.sql — the actor cannot be spoofed by a signed-in caller.
|
|
3
|
-
--
|
|
4
|
-
-- agent_* RPCs receive p_actor_user_id explicitly because the Fayz broker
|
|
5
|
-
-- (service_role, no JWT) injects the verified actor server-side. But the same
|
|
6
|
-
-- functions are EXECUTE-granted to `authenticated` so the surface can call
|
|
7
|
-
-- them client-plane before S4 — and there, p_actor comes from the client. A
|
|
8
|
-
-- signed-in user passing SOMEONE ELSE's id would borrow their role.
|
|
9
|
-
--
|
|
10
|
-
-- Rule added at the top of agent_guard (every write RPC's first call): when
|
|
11
|
-
-- the caller carries a JWT identity (auth.uid() not null), p_actor MUST be
|
|
12
|
-
-- that identity. Service-role/broker calls (auth.uid() null) are unaffected.
|
|
13
|
-
-- New file rather than editing 015 — applied migrations are never edited
|
|
14
|
-
-- (ledger rule); CREATE OR REPLACE supersedes the body.
|
|
15
|
-
-- ============================================================================
|
|
16
|
-
|
|
17
|
-
CREATE OR REPLACE FUNCTION public.agent_guard(
|
|
18
|
-
p_tenant_id uuid,
|
|
19
|
-
p_actor_user_id uuid,
|
|
20
|
-
p_feature text,
|
|
21
|
-
p_action text,
|
|
22
|
-
p_limit_key text DEFAULT NULL,
|
|
23
|
-
p_used integer DEFAULT NULL,
|
|
24
|
-
p_n integer DEFAULT 1
|
|
25
|
-
) RETURNS jsonb
|
|
26
|
-
LANGUAGE plpgsql STABLE SECURITY DEFINER
|
|
27
|
-
SET search_path = public
|
|
28
|
-
AS $$
|
|
29
|
-
DECLARE
|
|
30
|
-
v_role text;
|
|
31
|
-
v_has boolean;
|
|
32
|
-
v_override boolean;
|
|
33
|
-
v_plan_id text;
|
|
34
|
-
v_ent jsonb;
|
|
35
|
-
v_feature_flag jsonb;
|
|
36
|
-
v_cap numeric;
|
|
37
|
-
BEGIN
|
|
38
|
-
-- ── actor integrity: a JWT caller can only act as themselves ─────────────
|
|
39
|
-
IF auth.uid() IS NOT NULL AND auth.uid() IS DISTINCT FROM p_actor_user_id THEN
|
|
40
|
-
RETURN jsonb_build_object('allowed', false, 'reason', 'role',
|
|
41
|
-
'upgradeUrl', '/settings/subscription');
|
|
42
|
-
END IF;
|
|
43
|
-
|
|
44
|
-
-- ── membership + role ────────────────────────────────────────────────────
|
|
45
|
-
SELECT tm.role INTO v_role
|
|
46
|
-
FROM tenant_members tm
|
|
47
|
-
WHERE tm.tenant_id = p_tenant_id AND tm.user_id = p_actor_user_id;
|
|
48
|
-
IF v_role IS NULL THEN
|
|
49
|
-
RETURN jsonb_build_object('allowed', false, 'reason', 'role',
|
|
50
|
-
'upgradeUrl', '/settings/subscription');
|
|
51
|
-
END IF;
|
|
52
|
-
|
|
53
|
-
IF v_role <> 'owner' THEN
|
|
54
|
-
SELECT tro.granted INTO v_override
|
|
55
|
-
FROM tenant_role_overrides tro
|
|
56
|
-
WHERE tro.tenant_id = p_tenant_id AND tro.role = v_role
|
|
57
|
-
AND tro.permission_id IN (p_feature || '.' || p_action, p_feature || '.manage')
|
|
58
|
-
ORDER BY (tro.permission_id = p_feature || '.' || p_action) DESC
|
|
59
|
-
LIMIT 1;
|
|
60
|
-
|
|
61
|
-
IF v_override IS NOT NULL THEN
|
|
62
|
-
v_has := v_override;
|
|
63
|
-
ELSE
|
|
64
|
-
SELECT EXISTS (
|
|
65
|
-
SELECT 1 FROM role_permissions rp
|
|
66
|
-
WHERE rp.role = v_role
|
|
67
|
-
AND rp.permission_id IN (p_feature || '.' || p_action, p_feature || '.manage')
|
|
68
|
-
) INTO v_has;
|
|
69
|
-
END IF;
|
|
70
|
-
|
|
71
|
-
IF NOT v_has THEN
|
|
72
|
-
RETURN jsonb_build_object('allowed', false, 'reason', 'role',
|
|
73
|
-
'upgradeUrl', '/settings/subscription');
|
|
74
|
-
END IF;
|
|
75
|
-
END IF;
|
|
76
|
-
|
|
77
|
-
-- ── plan feature gate (owner does NOT bypass) ────────────────────────────
|
|
78
|
-
SELECT t.plan INTO v_plan_id FROM tenants t WHERE t.id = p_tenant_id;
|
|
79
|
-
SELECT p.entitlements INTO v_ent FROM plans p WHERE p.id = v_plan_id;
|
|
80
|
-
|
|
81
|
-
IF v_ent IS NOT NULL THEN
|
|
82
|
-
v_feature_flag := v_ent->'features'->p_feature;
|
|
83
|
-
IF v_feature_flag IS NOT NULL AND v_feature_flag = to_jsonb(false) THEN
|
|
84
|
-
RETURN jsonb_build_object('allowed', false, 'reason', 'plan',
|
|
85
|
-
'upgradeUrl', '/settings/subscription');
|
|
86
|
-
END IF;
|
|
87
|
-
|
|
88
|
-
IF p_limit_key IS NOT NULL AND p_used IS NOT NULL THEN
|
|
89
|
-
v_cap := (v_ent->'limits'->>p_limit_key)::numeric;
|
|
90
|
-
IF v_cap IS NOT NULL AND v_cap <> -1 AND (p_used + COALESCE(p_n, 1)) > v_cap THEN
|
|
91
|
-
RETURN jsonb_build_object('allowed', false, 'reason', 'limit',
|
|
92
|
-
'limit', jsonb_build_object('key', p_limit_key,
|
|
93
|
-
'max', v_cap,
|
|
94
|
-
'used', p_used),
|
|
95
|
-
'upgradeUrl', '/settings/subscription');
|
|
96
|
-
END IF;
|
|
97
|
-
END IF;
|
|
98
|
-
END IF;
|
|
99
|
-
|
|
100
|
-
RETURN NULL;
|
|
101
|
-
END;
|
|
102
|
-
$$;
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
-- ============================================================================
|
|
2
|
-
-- 017_core_addresses.sql — public.addresses becomes part of the SPINE.
|
|
3
|
-
-- ----------------------------------------------------------------------------
|
|
4
|
-
-- The address book was already declared "core" by @fayz-ai/shop's 0012
|
|
5
|
-
-- ("move address/payment concepts OUT of the plugin, into core"), but the SQL
|
|
6
|
-
-- physically shipped inside the shop package. Consequence, live today: any app
|
|
7
|
-
-- that does not install @fayz-ai/shop has no addresses table, while the person
|
|
8
|
-
-- archetype's "Endereços" tab (packages/saas AddressesTab) queries it
|
|
9
|
-
-- unconditionally — beauty-saas, dentist-saas, agency-os and every other
|
|
10
|
-
-- non-shop vertical render "Could not find the table 'public.addresses'".
|
|
11
|
-
--
|
|
12
|
-
-- An address is not e-commerce. It is where a client is delivered to, where a
|
|
13
|
-
-- supplier is collected from, where a staff member lives, where a unit of the
|
|
14
|
-
-- business sits. It belongs beside `people` and `locations`, in the spine.
|
|
15
|
-
--
|
|
16
|
-
-- Replay safety (this file runs on pools that already have the table, via shop
|
|
17
|
-
-- 0012, AND on pools that have never seen it):
|
|
18
|
-
-- • CREATE TABLE IF NOT EXISTS + ADD COLUMN IF NOT EXISTS — additive only
|
|
19
|
-
-- • the owner_type CHECK is dropped and rebuilt WIDER, never narrower, so a
|
|
20
|
-
-- pool holding 'shop_customer' rows keeps them valid
|
|
21
|
-
-- • no DROP of data, no DROP of the shop's own policies
|
|
22
|
-
-- Ordering: the spine runs BEFORE plugin migrations (cli buildMigrationPlan
|
|
23
|
-
-- ① spine → ④ plugin), so on a fresh shop pool this file creates the table and
|
|
24
|
-
-- shop 0012's own CREATE ... IF NOT EXISTS degrades to a no-op.
|
|
25
|
-
--
|
|
26
|
-
-- `owner_type` stays text rather than a FK because the owner is polymorphic:
|
|
27
|
-
-- a core `people` row, a `locations` row, the tenant itself, or — until the
|
|
28
|
-
-- shop's customer table is folded into `people` — a plg_shop_customers row.
|
|
29
|
-
-- ============================================================================
|
|
30
|
-
|
|
31
|
-
-- ----------------------------------------------------------------------------
|
|
32
|
-
-- 1. The table
|
|
33
|
-
-- ----------------------------------------------------------------------------
|
|
34
|
-
CREATE TABLE IF NOT EXISTS public.addresses (
|
|
35
|
-
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
36
|
-
tenant_id uuid NOT NULL,
|
|
37
|
-
owner_type text NOT NULL DEFAULT 'person',
|
|
38
|
-
owner_id uuid,
|
|
39
|
-
kind text NOT NULL DEFAULT 'both',
|
|
40
|
-
label text, -- "Casa", "Trabalho", "Depósito"
|
|
41
|
-
recipient text, -- quem recebe, se não for o próprio dono
|
|
42
|
-
phone text,
|
|
43
|
-
postal_code text NOT NULL, -- CEP
|
|
44
|
-
street text NOT NULL,
|
|
45
|
-
number text,
|
|
46
|
-
complement text,
|
|
47
|
-
district text, -- bairro
|
|
48
|
-
city text NOT NULL,
|
|
49
|
-
state text NOT NULL, -- UF
|
|
50
|
-
country text NOT NULL DEFAULT 'BR',
|
|
51
|
-
is_default boolean NOT NULL DEFAULT false,
|
|
52
|
-
metadata jsonb NOT NULL DEFAULT '{}',
|
|
53
|
-
created_at timestamptz NOT NULL DEFAULT now(),
|
|
54
|
-
updated_at timestamptz NOT NULL DEFAULT now()
|
|
55
|
-
);
|
|
56
|
-
|
|
57
|
-
COMMENT ON TABLE public.addresses IS
|
|
58
|
-
'Core address book. N addresses per owner (person, location, tenant), tenant-scoped. Owned by @fayz-ai/db — plugins read it, none of them own it.';
|
|
59
|
-
|
|
60
|
-
-- Pools provisioned by shop 0012 predate `metadata`/'both' defaults; add what
|
|
61
|
-
-- is missing rather than assuming the 0012 shape.
|
|
62
|
-
ALTER TABLE public.addresses
|
|
63
|
-
ADD COLUMN IF NOT EXISTS owner_type text NOT NULL DEFAULT 'person',
|
|
64
|
-
ADD COLUMN IF NOT EXISTS owner_id uuid,
|
|
65
|
-
ADD COLUMN IF NOT EXISTS kind text NOT NULL DEFAULT 'both',
|
|
66
|
-
ADD COLUMN IF NOT EXISTS label text,
|
|
67
|
-
ADD COLUMN IF NOT EXISTS recipient text,
|
|
68
|
-
ADD COLUMN IF NOT EXISTS phone text,
|
|
69
|
-
ADD COLUMN IF NOT EXISTS number text,
|
|
70
|
-
ADD COLUMN IF NOT EXISTS complement text,
|
|
71
|
-
ADD COLUMN IF NOT EXISTS district text,
|
|
72
|
-
ADD COLUMN IF NOT EXISTS metadata jsonb NOT NULL DEFAULT '{}';
|
|
73
|
-
|
|
74
|
-
-- ----------------------------------------------------------------------------
|
|
75
|
-
-- 2. Constraints — rebuilt WIDER than shop 0012's.
|
|
76
|
-
-- 'staff' and 'partner' are person kinds and already covered by 'person';
|
|
77
|
-
-- what 0012 lacked is nothing — this only re-asserts the same set so a pool
|
|
78
|
-
-- that never ran 0012 gets it, and a pool that did is left unchanged.
|
|
79
|
-
-- ----------------------------------------------------------------------------
|
|
80
|
-
ALTER TABLE public.addresses DROP CONSTRAINT IF EXISTS addresses_owner_type_check;
|
|
81
|
-
ALTER TABLE public.addresses
|
|
82
|
-
ADD CONSTRAINT addresses_owner_type_check
|
|
83
|
-
CHECK (owner_type IN ('person', 'shop_customer', 'location', 'tenant'));
|
|
84
|
-
|
|
85
|
-
ALTER TABLE public.addresses DROP CONSTRAINT IF EXISTS addresses_kind_check;
|
|
86
|
-
ALTER TABLE public.addresses
|
|
87
|
-
ADD CONSTRAINT addresses_kind_check
|
|
88
|
-
CHECK (kind IN ('shipping', 'billing', 'both'));
|
|
89
|
-
|
|
90
|
-
CREATE INDEX IF NOT EXISTS addresses_tenant_idx ON public.addresses (tenant_id);
|
|
91
|
-
CREATE INDEX IF NOT EXISTS addresses_owner_idx ON public.addresses (owner_type, owner_id);
|
|
92
|
-
-- One default per owner per kind, enforced here rather than trusting every
|
|
93
|
-
-- caller to clear the previous one.
|
|
94
|
-
CREATE UNIQUE INDEX IF NOT EXISTS addresses_one_default_idx
|
|
95
|
-
ON public.addresses (owner_type, owner_id, kind) WHERE is_default;
|
|
96
|
-
|
|
97
|
-
DROP TRIGGER IF EXISTS addresses_updated_at ON public.addresses;
|
|
98
|
-
CREATE TRIGGER addresses_updated_at BEFORE UPDATE ON public.addresses
|
|
99
|
-
FOR EACH ROW EXECUTE FUNCTION public.handle_updated_at();
|
|
100
|
-
|
|
101
|
-
-- ----------------------------------------------------------------------------
|
|
102
|
-
-- 3. RLS — tenant members manage their tenant's rows. Anon gets nothing.
|
|
103
|
-
-- The shop's extra `addresses_self_read` policy (a signed-in storefront
|
|
104
|
-
-- customer reading their own rows) is left untouched: it is additive, and
|
|
105
|
-
-- shop 0012 runs after this file.
|
|
106
|
-
-- ----------------------------------------------------------------------------
|
|
107
|
-
ALTER TABLE public.addresses ENABLE ROW LEVEL SECURITY;
|
|
108
|
-
|
|
109
|
-
DROP POLICY IF EXISTS "addresses_member_all" ON public.addresses;
|
|
110
|
-
CREATE POLICY "addresses_member_all" ON public.addresses FOR ALL TO authenticated
|
|
111
|
-
USING (tenant_id IN (SELECT public.user_tenant_ids()))
|
|
112
|
-
WITH CHECK (tenant_id IN (SELECT public.user_tenant_ids()));
|
|
113
|
-
|
|
114
|
-
GRANT ALL ON public.addresses TO authenticated, service_role;
|
|
115
|
-
REVOKE ALL ON public.addresses FROM anon;
|