forked from wangziqi/gongxue-base
236 lines
8.4 KiB
PL/PgSQL
236 lines
8.4 KiB
PL/PgSQL
create or replace function app.uuid_array_from_jsonb(value jsonb)
|
|
returns uuid[]
|
|
language sql
|
|
immutable
|
|
as $$
|
|
select coalesce(array_agg(item::uuid), '{}'::uuid[])
|
|
from jsonb_array_elements_text(
|
|
case when jsonb_typeof(value) = 'array' then value else '[]'::jsonb end
|
|
) as t(item)
|
|
where item ~* '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'
|
|
$$;
|
|
|
|
create or replace function app.text_array_from_jsonb(value jsonb)
|
|
returns text[]
|
|
language sql
|
|
immutable
|
|
as $$
|
|
select coalesce(array_agg(item), '{}'::text[])
|
|
from jsonb_array_elements_text(
|
|
case when jsonb_typeof(value) = 'array' then value else '[]'::jsonb end
|
|
) as t(item)
|
|
where length(item) between 1 and 160
|
|
$$;
|
|
|
|
create or replace function app.public_question_bank_grant_allows(
|
|
grant_allowed_region_ids uuid[],
|
|
grant_allowed_subject_ids uuid[],
|
|
source_region_id uuid,
|
|
source_subject_ids uuid[]
|
|
)
|
|
returns boolean
|
|
language sql
|
|
immutable
|
|
as $$
|
|
select
|
|
(
|
|
coalesce(array_length(grant_allowed_region_ids, 1), 0) = 0
|
|
or source_region_id = any(grant_allowed_region_ids)
|
|
)
|
|
and (
|
|
coalesce(array_length(grant_allowed_subject_ids, 1), 0) = 0
|
|
or coalesce(array_length(source_subject_ids, 1), 0) = 0
|
|
or source_subject_ids <@ grant_allowed_subject_ids
|
|
)
|
|
$$;
|
|
|
|
create or replace function app.public_question_bank_subscription_allows(
|
|
subscription_metadata jsonb,
|
|
plan_feature_flags jsonb,
|
|
source_question_bank_id uuid,
|
|
source_region_id uuid,
|
|
source_subject_ids uuid[]
|
|
)
|
|
returns boolean
|
|
language plpgsql
|
|
immutable
|
|
as $$
|
|
declare
|
|
plan_cfg jsonb := coalesce(plan_feature_flags #> '{publicQuestionBanks}', '{}'::jsonb);
|
|
subscription_cfg jsonb := coalesce(subscription_metadata #> '{publicQuestionBankAccess}', '{}'::jsonb);
|
|
plan_region_ids uuid[] := app.uuid_array_from_jsonb(plan_cfg->'allowedRegionIds');
|
|
subscription_region_ids uuid[] := app.uuid_array_from_jsonb(subscription_cfg->'allowedRegionIds');
|
|
plan_subject_ids uuid[] := app.uuid_array_from_jsonb(plan_cfg->'allowedSubjectIds');
|
|
subscription_subject_ids uuid[] := app.uuid_array_from_jsonb(subscription_cfg->'allowedSubjectIds');
|
|
plan_bank_ids uuid[] := app.uuid_array_from_jsonb(plan_cfg->'allowedQuestionBankIds');
|
|
subscription_bank_ids uuid[] := app.uuid_array_from_jsonb(subscription_cfg->'allowedQuestionBankIds');
|
|
mode text := coalesce(subscription_cfg->>'mode', plan_cfg->>'mode', 'all');
|
|
requires_region_allowlist boolean :=
|
|
case coalesce(subscription_cfg->>'requiresRegionAllowlist', plan_cfg->>'requiresRegionAllowlist')
|
|
when 'true' then true
|
|
when 'false' then false
|
|
else false
|
|
end;
|
|
allow_all_regions boolean := (
|
|
case coalesce(subscription_cfg->>'allowAllRegions', plan_cfg->>'allowAllRegions')
|
|
when 'true' then true
|
|
when 'false' then false
|
|
else false
|
|
end
|
|
) or mode in ('all', 'all_regions', 'national', 'unlimited');
|
|
begin
|
|
if coalesce(subscription_cfg->>'enabled', plan_cfg->>'enabled', 'true') = 'false' then
|
|
return false;
|
|
end if;
|
|
|
|
if coalesce(array_length(plan_bank_ids, 1), 0) > 0 and not source_question_bank_id = any(plan_bank_ids) then
|
|
return false;
|
|
end if;
|
|
if coalesce(array_length(subscription_bank_ids, 1), 0) > 0 and not source_question_bank_id = any(subscription_bank_ids) then
|
|
return false;
|
|
end if;
|
|
|
|
if not allow_all_regions and source_region_id is not null then
|
|
if requires_region_allowlist
|
|
and coalesce(array_length(plan_region_ids, 1), 0) = 0
|
|
and coalesce(array_length(subscription_region_ids, 1), 0) = 0 then
|
|
return false;
|
|
end if;
|
|
if coalesce(array_length(plan_region_ids, 1), 0) > 0 and not source_region_id = any(plan_region_ids) then
|
|
return false;
|
|
end if;
|
|
if coalesce(array_length(subscription_region_ids, 1), 0) > 0 and not source_region_id = any(subscription_region_ids) then
|
|
return false;
|
|
end if;
|
|
end if;
|
|
|
|
if coalesce(array_length(plan_subject_ids, 1), 0) > 0
|
|
and coalesce(array_length(source_subject_ids, 1), 0) > 0
|
|
and not source_subject_ids <@ plan_subject_ids then
|
|
return false;
|
|
end if;
|
|
if coalesce(array_length(subscription_subject_ids, 1), 0) > 0
|
|
and coalesce(array_length(source_subject_ids, 1), 0) > 0
|
|
and not source_subject_ids <@ subscription_subject_ids then
|
|
return false;
|
|
end if;
|
|
|
|
return true;
|
|
end;
|
|
$$;
|
|
|
|
create index if not exists idx_question_bank_grants_allowed_regions
|
|
on public.question_bank_grants using gin(allowed_region_ids);
|
|
|
|
create index if not exists idx_question_bank_grants_allowed_subjects
|
|
on public.question_bank_grants using gin(allowed_subject_ids);
|
|
|
|
update public.platform_saas_plans
|
|
set feature_flags = jsonb_set(
|
|
feature_flags,
|
|
'{publicQuestionBanks}',
|
|
'{"enabled":true,"mode":"limited_regions","requiresRegionAllowlist":true,"maxRegionCount":1}'::jsonb,
|
|
true
|
|
),
|
|
updated_at = now()
|
|
where code = 'starter_yearly';
|
|
|
|
update public.platform_saas_plans
|
|
set feature_flags = jsonb_set(
|
|
feature_flags,
|
|
'{publicQuestionBanks}',
|
|
'{"enabled":true,"mode":"national","allowAllRegions":true}'::jsonb,
|
|
true
|
|
),
|
|
updated_at = now()
|
|
where code = 'pro_yearly';
|
|
|
|
drop policy if exists question_bank_grants_eligible_tenant_read on public.question_bank_grants;
|
|
create policy question_bank_grants_eligible_tenant_read on public.question_bank_grants
|
|
for select
|
|
using (
|
|
status = 'active'
|
|
and (starts_at is null or starts_at <= now())
|
|
and (expires_at is null or expires_at > now())
|
|
and exists (
|
|
select 1
|
|
from public.question_banks qb
|
|
left join lateral (
|
|
select array_agg(distinct q.subject_id) filter (where q.subject_id is not null) as subject_ids
|
|
from public.questions q
|
|
where q.question_bank_id = qb.id
|
|
and q.status = 'published'
|
|
) ss on true
|
|
where qb.id = source_question_bank_id
|
|
and qb.source_scope = 'platform'
|
|
and qb.status = 'active'
|
|
and app.public_question_bank_grant_allows(
|
|
allowed_region_ids,
|
|
allowed_subject_ids,
|
|
qb.region_id,
|
|
coalesce(ss.subject_ids, '{}'::uuid[])
|
|
)
|
|
and (
|
|
(
|
|
grant_scope = 'all_active_tenants'
|
|
and exists (
|
|
select 1
|
|
from public.tenant_subscriptions ts
|
|
left join public.platform_saas_plans p on p.code = ts.plan_code
|
|
where ts.tenant_id = app.current_tenant_id()
|
|
and ts.status in ('trial', 'active')
|
|
and (ts.expires_at is null or ts.expires_at > now())
|
|
and app.public_question_bank_subscription_allows(
|
|
ts.metadata,
|
|
coalesce(p.feature_flags, '{}'::jsonb),
|
|
qb.id,
|
|
qb.region_id,
|
|
coalesce(ss.subject_ids, '{}'::uuid[])
|
|
)
|
|
)
|
|
)
|
|
or (
|
|
grant_scope in ('plans', 'mixed')
|
|
and exists (
|
|
select 1
|
|
from public.tenant_subscriptions ts
|
|
left join public.platform_saas_plans p on p.code = ts.plan_code
|
|
where ts.tenant_id = app.current_tenant_id()
|
|
and ts.status in ('trial', 'active')
|
|
and (ts.expires_at is null or ts.expires_at > now())
|
|
and ts.plan_code = any(allowed_plan_codes)
|
|
and app.public_question_bank_subscription_allows(
|
|
ts.metadata,
|
|
coalesce(p.feature_flags, '{}'::jsonb),
|
|
qb.id,
|
|
qb.region_id,
|
|
coalesce(ss.subject_ids, '{}'::uuid[])
|
|
)
|
|
)
|
|
)
|
|
or (
|
|
grant_scope in ('tenants', 'mixed')
|
|
and app.current_tenant_id() = any(allowed_tenant_ids)
|
|
and (
|
|
metadata->>'requiresActiveSubscription' = 'false'
|
|
or exists (
|
|
select 1
|
|
from public.tenant_subscriptions ts
|
|
left join public.platform_saas_plans p on p.code = ts.plan_code
|
|
where ts.tenant_id = app.current_tenant_id()
|
|
and ts.status in ('trial', 'active')
|
|
and (ts.expires_at is null or ts.expires_at > now())
|
|
and app.public_question_bank_subscription_allows(
|
|
ts.metadata,
|
|
coalesce(p.feature_flags, '{}'::jsonb),
|
|
qb.id,
|
|
qb.region_id,
|
|
coalesce(ss.subject_ids, '{}'::uuid[])
|
|
)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
);
|