caterium-app/ops/sql/SUPABASE-DEVELOPER-V22.sql
2026-09-07 15:29:20 +03:00

326 lines
17 KiB
PL/PgSQL

-- Caterium / Sun Catering v17.5.22 developer console
-- Additive only. Gives platform admins a server-enforced developer console API.
create table if not exists public.sun_platform_audit_events (
id bigint generated by default as identity primary key,
actor_user_id uuid null references auth.users(id) on delete set null,
action text not null,
target_workspace_id uuid null references public.sun_workspaces(id) on delete set null,
target_user_id uuid null references auth.users(id) on delete set null,
details jsonb not null default '{}'::jsonb,
created_at timestamptz not null default now()
);
create index if not exists sun_platform_audit_events_created_idx on public.sun_platform_audit_events(created_at desc);
create index if not exists sun_platform_audit_events_workspace_idx on public.sun_platform_audit_events(target_workspace_id,created_at desc);
create index if not exists sun_platform_audit_events_user_idx on public.sun_platform_audit_events(target_user_id,created_at desc);
alter table public.sun_platform_audit_events enable row level security;
revoke all on public.sun_platform_audit_events from anon, authenticated;
create or replace function public.sun_platform_log_event(
p_action text,
p_workspace uuid default null,
p_user uuid default null,
p_details jsonb default '{}'::jsonb
) returns bigint
language plpgsql
security definer
set search_path='public','auth'
as $$
declare v_id bigint;
begin
if not public.sun_is_platform_admin() then raise exception 'Platform administrator required'; end if;
if nullif(trim(coalesce(p_action,'')),'') is null then raise exception 'Action required'; end if;
insert into public.sun_platform_audit_events(actor_user_id,action,target_workspace_id,target_user_id,details)
values(auth.uid(),left(trim(p_action),120),p_workspace,p_user,coalesce(p_details,'{}'::jsonb)) returning id into v_id;
return v_id;
end;
$$;
create or replace function public.sun_platform_dashboard()
returns jsonb
language plpgsql
stable
security definer
set search_path='public','auth'
as $$
declare
v_companies bigint; v_accounts bigint; v_admins bigint; v_active bigint; v_trial bigint; v_locked bigint;
v_errors bigint; v_backups bigint; v_members bigint; v_last_state timestamptz; v_last_backup timestamptz;
begin
if not public.sun_is_platform_admin() then raise exception 'Platform administrator required'; end if;
select count(*) into v_companies from public.sun_workspaces;
select count(*) into v_accounts from auth.users;
select count(*) into v_admins from public.sun_platform_admins;
select count(*) into v_members from public.sun_workspace_members where is_active=true;
select count(*) into v_active from public.sun_workspace_subscriptions where status='active' and coalesce(current_period_end,'infinity'::timestamptz)>now();
select count(*) into v_trial from public.sun_workspace_subscriptions where status='trialing' and coalesce(trial_ends_at,'infinity'::timestamptz)>now();
select count(*) into v_locked from public.sun_workspaces w where public.sun_subscription_access_mode(w.id) in ('read_only','blocked');
select count(*) into v_errors from public.sun_v17_error_events where created_at>now()-interval '24 hours';
select count(*) into v_backups from public.sun_v17_backups where created_at>now()-interval '24 hours';
select max(updated_at) into v_last_state from public.sun_app_state;
select max(created_at) into v_last_backup from public.sun_v17_backups;
return jsonb_build_object(
'companies',v_companies,'accounts',v_accounts,'platform_admins',v_admins,'memberships',v_members,
'active_subscriptions',v_active,'trials',v_trial,'restricted_companies',v_locked,
'errors_24h',v_errors,'backups_24h',v_backups,'last_state_at',v_last_state,'last_backup_at',v_last_backup,
'database_size',pg_size_pretty(pg_database_size(current_database())),
'postgres_version',current_setting('server_version')
);
end;
$$;
create or replace function public.sun_platform_list_activity(p_limit integer default 120)
returns table(
id bigint, action text, actor_user_id uuid, actor_email text,
workspace_id uuid, workspace_name text, target_user_id uuid, target_email text,
details jsonb, created_at timestamptz
)
language plpgsql
stable
security definer
set search_path='public','auth'
as $$
begin
if not public.sun_is_platform_admin() then raise exception 'Platform administrator required'; end if;
return query
select a.id,a.action,a.actor_user_id,au.email::text,a.target_workspace_id,w.name,a.target_user_id,tu.email::text,a.details,a.created_at
from public.sun_platform_audit_events a
left join auth.users au on au.id=a.actor_user_id
left join public.sun_workspaces w on w.id=a.target_workspace_id
left join auth.users tu on tu.id=a.target_user_id
order by a.created_at desc
limit greatest(1,least(coalesce(p_limit,120),500));
end;
$$;
create or replace function public.sun_platform_support_snapshot(p_workspace uuid)
returns table(workspace_id uuid,payload jsonb,revision bigint,updated_at timestamptz,client_id text)
language plpgsql
security definer
set search_path='public'
as $$
begin
if not public.sun_is_platform_admin() then raise exception 'Platform administrator required'; end if;
if not exists(select 1 from public.sun_workspaces where id=p_workspace) then raise exception 'Workspace not found'; end if;
perform public.sun_platform_log_event('support.open',p_workspace,null,jsonb_build_object('mode','read_only'));
return query
select s.workspace_id,s.payload,s.revision,s.updated_at,s.client_id
from public.sun_app_state s where s.workspace_id=p_workspace;
end;
$$;
create or replace function public.sun_platform_workspace_diagnostics(p_workspace uuid)
returns jsonb
language plpgsql
stable
security definer
set search_path='public'
as $$
declare v jsonb;
begin
if not public.sun_is_platform_admin() then raise exception 'Platform administrator required'; end if;
if not exists(select 1 from public.sun_workspaces where id=p_workspace) then raise exception 'Workspace not found'; end if;
select jsonb_build_object(
'workspace_id',w.id,'name',w.name,'created_at',w.created_at,
'revision',coalesce(s.revision,0),'state_updated_at',s.updated_at,'state_client_id',s.client_id,
'members',(select count(*) from public.sun_workspace_members m where m.workspace_id=w.id and m.is_active),
'orders',(select count(*) from public.sun_v17_orders o where o.workspace_id=w.id),
'clients',(select count(*) from public.sun_v17_clients c where c.workspace_id=w.id),
'catalog_items',(select count(*) from public.sun_v17_catalog_items c where c.workspace_id=w.id),
'settings',(select count(*) from public.sun_v17_settings x where x.workspace_id=w.id),
'backups',(select count(*) from public.sun_v17_backups b where b.workspace_id=w.id),
'latest_backup',(select max(created_at) from public.sun_v17_backups b where b.workspace_id=w.id),
'errors_24h',(select count(*) from public.sun_v17_error_events e where e.workspace_id=w.id and e.created_at>now()-interval '24 hours'),
'latest_error',(select max(created_at) from public.sun_v17_error_events e where e.workspace_id=w.id),
'access_mode',public.sun_subscription_access_mode(w.id)
) into v
from public.sun_workspaces w left join public.sun_app_state s on s.workspace_id=w.id
where w.id=p_workspace;
return v;
end;
$$;
create or replace function public.sun_platform_list_workspace_features(p_workspace uuid)
returns table(
feature_key text, plan_enabled boolean, override_enabled boolean,
override_expires_at timestamptz, effective_enabled boolean, note text
)
language plpgsql
stable
security definer
set search_path='public'
as $$
declare v_plan text;
begin
if not public.sun_is_platform_admin() then raise exception 'Platform administrator required'; end if;
select plan_id into v_plan from public.sun_workspace_subscriptions where workspace_id=p_workspace;
return query
with features as (
select distinct pf.feature_key from public.sun_plan_features pf
)
select f.feature_key,
coalesce(pf.enabled,false),
case when o.expires_at is null or o.expires_at>now() then o.enabled else null end,
o.expires_at,
case when o.feature_key is not null and (o.expires_at is null or o.expires_at>now()) then o.enabled else coalesce(pf.enabled,false) end,
o.note
from features f
left join public.sun_plan_features pf on pf.plan_id=v_plan and pf.feature_key=f.feature_key
left join public.sun_workspace_feature_overrides o on o.workspace_id=p_workspace and o.feature_key=f.feature_key
order by f.feature_key;
end;
$$;
create or replace function public.sun_platform_set_plan_feature(p_plan text,p_feature text,p_enabled boolean)
returns void
language plpgsql
security definer
set search_path='public'
as $$
begin
if not public.sun_is_platform_admin() then raise exception 'Platform administrator required'; end if;
if not exists(select 1 from public.sun_plans where id=p_plan) then raise exception 'Unknown plan'; end if;
if nullif(trim(coalesce(p_feature,'')),'') is null then raise exception 'Feature required'; end if;
insert into public.sun_plan_features(plan_id,feature_key,enabled) values(p_plan,p_feature,p_enabled)
on conflict(plan_id,feature_key) do update set enabled=excluded.enabled;
perform public.sun_platform_log_event('plan.feature.set',null,null,jsonb_build_object('plan',p_plan,'feature',p_feature,'enabled',p_enabled));
end;
$$;
create or replace function public.sun_platform_set_plan_max_members(p_plan text,p_max_members integer)
returns void
language plpgsql
security definer
set search_path='public'
as $$
begin
if not public.sun_is_platform_admin() then raise exception 'Platform administrator required'; end if;
if p_max_members is not null and (p_max_members<1 or p_max_members>10000) then raise exception 'Invalid member limit'; end if;
update public.sun_plans set max_members=p_max_members,updated_at=now() where id=p_plan;
if not found then raise exception 'Unknown plan'; end if;
perform public.sun_platform_log_event('plan.members.set',null,null,jsonb_build_object('plan',p_plan,'max_members',p_max_members));
end;
$$;
create or replace function public.sun_platform_seed_workspace_catalog(
p_workspace uuid,
p_boxes jsonb,
p_catalog_version text default '',
p_replace boolean default false
) returns integer
language plpgsql
security definer
set search_path='public'
as $$
declare
v_payload jsonb; v_existing jsonb; v_entry jsonb; v_item jsonb; v_id text; v_count integer:=0;
begin
if not public.sun_is_platform_admin() then raise exception 'Platform administrator required'; end if;
if jsonb_typeof(p_boxes)<>'array' then raise exception 'Catalog must be an array'; end if;
if not exists(select 1 from public.sun_workspaces where id=p_workspace) then raise exception 'Workspace not found'; end if;
select payload into v_payload from public.sun_app_state where workspace_id=p_workspace for update;
if v_payload is null then
v_payload:=jsonb_build_object('format','sun-cloud-v2','version',2,'storage','{}'::jsonb);
insert into public.sun_app_state(workspace_id,payload,revision,updated_by)
values(p_workspace,v_payload,0,auth.uid())
on conflict(workspace_id) do nothing;
end if;
v_existing:=coalesce(v_payload->'storage'->'sunBoxes'->'v','[]'::jsonb);
if not p_replace and jsonb_typeof(v_existing)='array' and jsonb_array_length(v_existing)>0 then
raise exception 'Catalog already populated';
end if;
v_entry:=jsonb_build_object('t','j','v',p_boxes);
v_payload:=jsonb_set(coalesce(v_payload,'{}'::jsonb),'{storage,sunBoxes}',v_entry,true);
if nullif(coalesce(p_catalog_version,''),'') is not null then
v_payload:=jsonb_set(v_payload,'{storage,sunOfficialCatalogVersion}',jsonb_build_object('t','s','v',p_catalog_version),true);
end if;
update public.sun_app_state set payload=v_payload,revision=revision+1,client_id='platform-catalog',updated_by=auth.uid(),updated_at=now() where workspace_id=p_workspace;
if p_replace then delete from public.sun_v17_catalog_items where workspace_id=p_workspace; end if;
for v_item in select value from jsonb_array_elements(p_boxes) loop
v_id:=coalesce(nullif(v_item->>'id',''),gen_random_uuid()::text);
insert into public.sun_v17_catalog_items(workspace_id,item_id,data,version,created_by,updated_by)
values(p_workspace,v_id,v_item,1,auth.uid(),auth.uid())
on conflict(workspace_id,item_id) do update set data=excluded.data,version=sun_v17_catalog_items.version+1,updated_by=auth.uid(),updated_at=now();
v_count:=v_count+1;
end loop;
insert into public.sun_v17_change_events(workspace_id,entity,entity_key,operation,client_id,created_by)
values(p_workspace,'catalog','starter',case when p_replace then 'replace' else 'seed' end,'platform-catalog',auth.uid());
perform public.sun_platform_log_event(case when p_replace then 'catalog.force_apply' else 'catalog.seed' end,p_workspace,null,jsonb_build_object('count',v_count,'version',p_catalog_version));
return v_count;
end;
$$;
revoke all on function public.sun_platform_log_event(text,uuid,uuid,jsonb) from public, anon;
revoke all on function public.sun_platform_dashboard() from public, anon;
revoke all on function public.sun_platform_list_activity(integer) from public, anon;
revoke all on function public.sun_platform_support_snapshot(uuid) from public, anon;
revoke all on function public.sun_platform_workspace_diagnostics(uuid) from public, anon;
revoke all on function public.sun_platform_list_workspace_features(uuid) from public, anon;
revoke all on function public.sun_platform_set_plan_feature(text,text,boolean) from public, anon;
revoke all on function public.sun_platform_set_plan_max_members(text,integer) from public, anon;
revoke all on function public.sun_platform_seed_workspace_catalog(uuid,jsonb,text,boolean) from public, anon;
grant execute on function public.sun_platform_log_event(text,uuid,uuid,jsonb) to authenticated;
grant execute on function public.sun_platform_dashboard() to authenticated;
grant execute on function public.sun_platform_list_activity(integer) to authenticated;
grant execute on function public.sun_platform_support_snapshot(uuid) to authenticated;
grant execute on function public.sun_platform_workspace_diagnostics(uuid) to authenticated;
grant execute on function public.sun_platform_list_workspace_features(uuid) to authenticated;
grant execute on function public.sun_platform_set_plan_feature(text,text,boolean) to authenticated;
grant execute on function public.sun_platform_set_plan_max_members(text,integer) to authenticated;
grant execute on function public.sun_platform_seed_workspace_catalog(uuid,jsonb,text,boolean) to authenticated;
create or replace function public.sun_platform_reset_feature_override(p_workspace uuid,p_feature text)
returns void
language plpgsql
security definer
set search_path='public'
as $$
begin
if not public.sun_is_platform_admin() then raise exception 'Platform administrator required'; end if;
delete from public.sun_workspace_feature_overrides where workspace_id=p_workspace and feature_key=p_feature;
perform public.sun_platform_log_event('feature.override.reset',p_workspace,null,jsonb_build_object('feature',p_feature));
end;
$$;
create or replace function public.sun_platform_create_company_v22(
p_name text,
p_owner_email text default null,
p_plan text default 'full',
p_days integer default 30,
p_boxes jsonb default '[]'::jsonb,
p_catalog_version text default ''
) returns jsonb
language plpgsql
security definer
set search_path='public'
as $$
declare
v_base jsonb; v_ws uuid; v_profile jsonb; v_payload jsonb; v_count integer;
begin
if not public.sun_is_platform_admin() then raise exception 'Platform administrator required'; end if;
select public.caterium_platform_create_company(p_name,p_owner_email,p_plan,p_days,'empty') into v_base;
v_ws:=(v_base->>'workspace_id')::uuid;
v_profile:=jsonb_build_object('name',coalesce(nullif(trim(p_name),''),'Новая компания'),'ownerEmail',coalesce(p_owner_email,''),'createdAt',now());
select payload into v_payload from public.sun_app_state where workspace_id=v_ws for update;
v_payload:=jsonb_set(v_payload,'{storage,sunCompanyProfileV1}',jsonb_build_object('t','j','v',v_profile),true);
v_payload:=jsonb_set(v_payload,'{storage,sunOrders}',jsonb_build_object('t','j','v','[]'::jsonb),true);
v_payload:=jsonb_set(v_payload,'{storage,sunClientsV2}',jsonb_build_object('t','j','v','[]'::jsonb),true);
update public.sun_app_state set payload=v_payload,updated_by=auth.uid(),updated_at=now() where workspace_id=v_ws;
select public.sun_platform_seed_workspace_catalog(v_ws,coalesce(p_boxes,'[]'::jsonb),p_catalog_version,true) into v_count;
perform public.sun_platform_log_event('company.create',v_ws,null,jsonb_build_object('name',p_name,'owner_email',p_owner_email,'plan',p_plan,'days',p_days,'catalog_count',v_count));
return coalesce(v_base,'{}'::jsonb)||jsonb_build_object('catalog_count',v_count);
end;
$$;
revoke all on function public.sun_platform_reset_feature_override(uuid,text) from public, anon;
revoke all on function public.sun_platform_create_company_v22(text,text,text,integer,jsonb,text) from public, anon;
grant execute on function public.sun_platform_reset_feature_override(uuid,text) to authenticated;
grant execute on function public.sun_platform_create_company_v22(text,text,text,integer,jsonb,text) to authenticated;