Add MineGeo gameplay systems and interface
This commit is contained in:
119
supabase/migrations/20261009123642_admin_permissions.sql
Normal file
119
supabase/migrations/20261009123642_admin_permissions.sql
Normal file
@@ -0,0 +1,119 @@
|
||||
-- Admin accounts manage settings and enforcement. Moderators review map submissions.
|
||||
alter table public.profiles drop constraint profiles_role_check;
|
||||
alter table public.profiles add constraint profiles_role_check
|
||||
check (role in ('contributor','moderator','admin'));
|
||||
|
||||
create or replace function public.publish_dataset(p_dataset uuid,p_reviewer uuid,p_decision text,p_reason text default null) returns void
|
||||
language plpgsql security invoker set search_path='' as $$
|
||||
declare d public.datasets; m jsonb; rev uuid; first jsonb; count integer;
|
||||
begin
|
||||
if not exists(select 1 from public.profiles where id=p_reviewer and role in ('moderator','admin')) then raise exception 'Moderator permission required'; end if;
|
||||
if p_decision='unpublish' and not exists(select 1 from public.profiles where id=p_reviewer and role='admin') then raise exception 'Admin permission required'; end if;
|
||||
select * into d from public.datasets where id=p_dataset and parent_dataset_id is null for update;
|
||||
if not found then raise exception 'Map not found'; end if;
|
||||
if p_decision='approve' then
|
||||
if not(d.state='review' or (d.state='published' and coalesce(d.update_state,'')='review')) then raise exception 'Map is not ready for publication'; end if;
|
||||
select manifest into m from public.dataset_inputs where dataset_id=p_dataset and validated;
|
||||
count:=jsonb_array_length(m->'viewpoints'); first:=m->'viewpoints'->0;
|
||||
if m is null or count<5 or count>24 then raise exception 'Map requires 5–24 validated panoramas'; end if;
|
||||
insert into public.revisions(dataset_id,manifest,asset_root) values(p_dataset,m,p_dataset::text) returning id into rev;
|
||||
update public.datasets set state='published',revision_id=rev,update_state=null,name=m->>'name',description=m->>'description',panorama_count=count,
|
||||
map_path=coalesce(m->>'mapPath',d.map_path),
|
||||
cover_path=coalesce(first->>'assetRoot',id::text)||'/'||coalesce(first->>'assetId',first->>'id')||'/preview.webp',rejection_reason=null,updated_at=now() where id=p_dataset;
|
||||
elsif p_decision='reject' and (d.state='review' or (d.state='published' and d.update_state='review')) then
|
||||
if coalesce(length(trim(p_reason)),0)=0 then raise exception 'Provide a rejection reason'; end if;
|
||||
update public.datasets set state=case when state='published' then state else 'rejected' end,update_state=case when state='published' then 'rejected' end,rejection_reason=p_reason,updated_at=now() where id=p_dataset;
|
||||
elsif p_decision='unpublish' and d.state='published' then
|
||||
update public.datasets set state='unpublished',update_state=null,updated_at=now() where id=p_dataset;
|
||||
else raise exception 'Invalid review transition'; end if;
|
||||
insert into public.review_decisions(dataset_id,reviewer_id,decision,reason) values(p_dataset,p_reviewer,p_decision,p_reason);
|
||||
end; $$;
|
||||
|
||||
create or replace function public.review_content_report(p_report uuid,p_reviewer uuid,p_decision text,p_reason text) returns void
|
||||
language plpgsql security invoker set search_path='' as $$
|
||||
declare r public.reports; d public.datasets; map_id uuid; strikes integer;
|
||||
begin
|
||||
if not exists(select 1 from public.profiles where id=p_reviewer and role = 'admin') then raise exception 'Admin permission required'; end if;
|
||||
select dataset_id into map_id from public.reports where id=p_report;
|
||||
select * into d from public.datasets where id=map_id for update;
|
||||
select * into r from public.reports where id=p_report for update;
|
||||
if not found or r.state<>'open' or r.report_cycle<>d.report_cycle then raise exception 'Report is no longer awaiting review'; end if;
|
||||
if p_decision is null or p_decision not in ('confirm','dismiss') or coalesce(length(trim(p_reason)),0) not between 10 and 2000 then raise exception 'Provide a review decision and feedback in 10–2000 characters'; end if;
|
||||
if p_decision='confirm' then
|
||||
if d.banned_at is not null then raise exception 'This map is already banned; dismiss the remaining report or review its appeal'; end if;
|
||||
if p_reviewer=r.owner_id or p_reviewer=d.owner_id then raise exception 'An independent moderator must confirm this report'; end if;
|
||||
-- Distinct reporters provide each strike, including legacy reports.
|
||||
if exists(select 1 from public.reports where dataset_id=d.id and owner_id=r.owner_id and report_cycle=d.report_cycle and state='confirmed') then raise exception 'This reporter already contributed a strike'; end if;
|
||||
strikes:=d.report_strikes+1;
|
||||
update public.datasets set report_strikes=strikes,
|
||||
state=case when strikes>=3 then 'unpublished' else state end,
|
||||
update_state=case when strikes>=3 then null else update_state end,
|
||||
banned_at=case when strikes>=3 then now() end,
|
||||
ban_reason=case when strikes>=3 then trim(p_reason) end,updated_at=now() where id=d.id;
|
||||
end if;
|
||||
update public.reports set state=case when p_decision='confirm' then 'confirmed' else 'dismissed' end,
|
||||
reviewer_id=p_reviewer,reviewed_at=now(),review_reason=trim(p_reason) where id=r.id;
|
||||
end; $$;
|
||||
|
||||
create or replace function public.review_map_appeal(p_appeal uuid,p_reviewer uuid,p_decision text,p_reason text) returns void
|
||||
language plpgsql security invoker set search_path='' as $$
|
||||
declare a public.map_appeals; d public.datasets; map_id uuid;
|
||||
begin
|
||||
if not exists(select 1 from public.profiles where id=p_reviewer and role = 'admin') then raise exception 'Admin permission required'; end if;
|
||||
select dataset_id into map_id from public.map_appeals where id=p_appeal;
|
||||
select * into d from public.datasets where id=map_id for update;
|
||||
select * into a from public.map_appeals where id=p_appeal for update;
|
||||
if not found or a.state<>'open' or d.banned_at is null or a.report_cycle<>d.report_cycle then raise exception 'Appeal is no longer awaiting review'; end if;
|
||||
if p_reviewer=d.owner_id then raise exception 'An independent moderator must review your appeal'; end if;
|
||||
if p_decision is null or p_decision not in ('approve','reject') or coalesce(length(trim(p_reason)),0) not between 10 and 2000 then raise exception 'Provide an appeal decision and feedback in 10–2000 characters'; end if;
|
||||
if p_decision='approve' then
|
||||
if d.revision_id is null then raise exception 'Map has no approved version to restore'; end if;
|
||||
update public.datasets set report_strikes=0,report_cycle=report_cycle+1,banned_at=null,ban_reason=null,state='published',updated_at=now() where id=d.id;
|
||||
update public.reports set state='dismissed',reviewer_id=p_reviewer,reviewed_at=now(),review_reason='Closed when the map ban appeal was approved.' where dataset_id=d.id and report_cycle=d.report_cycle and state='open';
|
||||
end if;
|
||||
update public.map_appeals set state=case when p_decision='approve' then 'approved' else 'rejected' end,reviewer_id=p_reviewer,reviewed_at=now(),review_reason=trim(p_reason) where id=a.id;
|
||||
end; $$;
|
||||
|
||||
create or replace function public.set_beta_badge(p_reviewer uuid, p_enabled boolean)
|
||||
returns boolean language plpgsql security invoker set search_path = '' as $$
|
||||
begin
|
||||
if not exists (
|
||||
select 1 from public.profiles where id = p_reviewer and role = 'admin'
|
||||
) then
|
||||
raise exception 'Admin permission required';
|
||||
end if;
|
||||
if p_enabled is null then
|
||||
raise exception 'Beta badge visibility is required';
|
||||
end if;
|
||||
update public.site_settings
|
||||
set beta_badge_enabled = p_enabled, updated_at = now(), updated_by = p_reviewer
|
||||
where id = true;
|
||||
if not found then raise exception 'Site settings not found'; end if;
|
||||
return p_enabled;
|
||||
end;
|
||||
$$;
|
||||
|
||||
create or replace function public.set_user_prefix(p_reviewer uuid,p_user uuid,p_prefix text,p_icon text) returns void
|
||||
language plpgsql security invoker set search_path='' as $$
|
||||
begin
|
||||
if not exists(select 1 from public.profiles where id=p_reviewer and role = 'admin') then raise exception 'Admin permission required'; end if;
|
||||
if p_icon not in ('star','crown','heart','flame','gem','trophy','sparkles') or p_icon is null then raise exception 'Invalid prefix icon'; end if;
|
||||
if p_prefix is not null and (length(btrim(p_prefix)) not between 1 and 24 or p_prefix ~ '[[:cntrl:]]') then raise exception 'Invalid custom prefix'; end if;
|
||||
update public.profiles set custom_prefix=case when p_prefix is null then null else btrim(p_prefix) end,custom_prefix_icon=p_icon where id=p_user;
|
||||
if not found then raise exception 'Player not found'; end if;
|
||||
end; $$;
|
||||
|
||||
revoke all on function public.publish_dataset(uuid,uuid,text,text) from public,anon,authenticated;
|
||||
grant execute on function public.publish_dataset(uuid,uuid,text,text) to service_role;
|
||||
|
||||
revoke all on function public.review_content_report(uuid,uuid,text,text) from public,anon,authenticated;
|
||||
grant execute on function public.review_content_report(uuid,uuid,text,text) to service_role;
|
||||
|
||||
revoke all on function public.review_map_appeal(uuid,uuid,text,text) from public,anon,authenticated;
|
||||
grant execute on function public.review_map_appeal(uuid,uuid,text,text) to service_role;
|
||||
|
||||
revoke all on function public.set_beta_badge(uuid,boolean) from public,anon,authenticated;
|
||||
grant execute on function public.set_beta_badge(uuid,boolean) to service_role;
|
||||
|
||||
revoke all on function public.set_user_prefix(uuid,uuid,text,text) from public,anon,authenticated;
|
||||
grant execute on function public.set_user_prefix(uuid,uuid,text,text) to service_role;
|
||||
Reference in New Issue
Block a user