326 lines
17 KiB
PL/PgSQL
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;
|