-- 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;