Some checks failed
Deploy MineGeo to Dokploy / deploy (push) Failing after 3m40s
136 lines
9.7 KiB
PL/PgSQL
136 lines
9.7 KiB
PL/PgSQL
-- Keep old snapshots unchanged; missing new switches retain their client defaults.
|
|
alter table public.difficulty_presets
|
|
add column map_pan boolean not null default true,
|
|
add column timer_visible boolean not null default true;
|
|
alter table public.difficulty_presets drop constraint difficulty_presets_seconds_check;
|
|
alter table public.difficulty_presets add constraint difficulty_presets_seconds_check
|
|
check (seconds between 5 and 600 and seconds % 5 = 0);
|
|
|
|
alter table public.game_sessions drop constraint game_sessions_difficulty_check;
|
|
alter table public.game_sessions add constraint game_sessions_difficulty_check check (
|
|
difficulty in ('easy','normal','hard','void')
|
|
or difficulty ~ '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'
|
|
);
|
|
alter table public.multiplayer_rooms drop constraint multiplayer_rooms_difficulty_check;
|
|
alter table public.multiplayer_rooms add constraint multiplayer_rooms_difficulty_check check (
|
|
difficulty in ('easy','normal','hard','void')
|
|
or difficulty ~ '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'
|
|
);
|
|
|
|
create or replace function public.resolve_difficulty_preset(p_id text)
|
|
returns jsonb language plpgsql stable security invoker set search_path = '' as $$
|
|
declare preset jsonb;
|
|
begin
|
|
if p_id = 'void' then
|
|
return '{"id":"void","name":"Void","seconds":10,"zoom":false,"pan":false,"compass":false,"coordinates":false,"mapZoom":false,"mapPan":false,"timerVisible":true,"scoring":"strict"}'::jsonb;
|
|
end if;
|
|
if p_id in ('easy','normal','hard') then
|
|
return jsonb_build_object('id',p_id,'name',initcap(p_id),
|
|
'seconds',case p_id when 'easy' then 180 when 'hard' then 30 else 90 end,
|
|
'zoom',p_id <> 'hard','pan',true,'compass',true,'coordinates',true,
|
|
'mapZoom',true,'mapPan',true,'timerVisible',true,'scoring','normal');
|
|
end if;
|
|
select jsonb_build_object('id',p.id::text,'name',p.name,'seconds',p.seconds,'zoom',p.zoom,
|
|
'pan',p.pan,'compass',p.compass,'coordinates',p.coordinates,'mapZoom',p.map_zoom,
|
|
'mapPan',p.map_pan,'timerVisible',p.timer_visible,'scoring',p.scoring)
|
|
into preset from public.difficulty_presets p where p.id::text=lower(p_id);
|
|
if not found then raise exception 'Choose a saved difficulty'; end if;
|
|
return preset;
|
|
end; $$;
|
|
|
|
-- A Void tournament must keep the same ten-second limit for every mini-round.
|
|
alter table public.tournaments drop constraint tournaments_round_time_seconds_check;
|
|
alter table public.tournaments add constraint tournaments_round_time_seconds_check check (
|
|
(difficulty='void' and timer_mode='perMini' and round_time_seconds=10)
|
|
or (difficulty<>'void' and round_time_seconds between 15 and 3000 and round_time_seconds%15=0)
|
|
);
|
|
|
|
create or replace function public.create_tournament(p_owner uuid,p_payload jsonb) returns uuid
|
|
language plpgsql security invoker set search_path='' as $$
|
|
declare tid uuid; rev public.revisions; settings jsonb; start_time timestamptz; zone text;
|
|
seconds integer; cap integer; rounds integer; title text; mode text;
|
|
begin
|
|
if not exists(select 1 from public.profiles where id=p_owner and account_status='active') then raise exception 'Active account required'; end if;
|
|
select r.* into rev from public.datasets d join public.revisions r on r.id=d.revision_id
|
|
where d.id=(p_payload->>'datasetId')::uuid and d.state='published';
|
|
if not found then raise exception 'Choose a published map'; end if;
|
|
if jsonb_array_length(public.game_viewpoint_pool(rev.manifest,rev.asset_root))<5 then raise exception 'Map needs at least five distinct panorama locations'; end if;
|
|
start_time:=(p_payload->>'startsAt')::timestamptz;
|
|
if start_time is null or start_time<=clock_timestamp() or start_time>clock_timestamp()+interval '90 days' then raise exception 'Choose a future start within 90 days'; end if;
|
|
zone:=p_payload->>'serverTimezone';
|
|
if zone is null or not exists(select 1 from pg_catalog.pg_timezone_names where name=zone) then raise exception 'Invalid server timezone'; end if;
|
|
title:=btrim(p_payload->>'name'); cap:=(p_payload->>'playerLimit')::integer;
|
|
rounds:=(p_payload->>'roundCount')::integer; seconds:=(p_payload->>'roundTimeSeconds')::integer;
|
|
if cap is null or cap not between 2 and 32 then raise exception 'Choose between 2 and 32 players'; end if;
|
|
if rounds is null or rounds not between 1 and 10 then raise exception 'Choose between 1 and 10 tournament rounds'; end if;
|
|
mode:=coalesce(p_payload->>'timerMode','shared');
|
|
if mode not in('shared','perMini') then raise exception 'Invalid tournament timer mode'; end if;
|
|
if p_payload->>'difficulty'='void' then
|
|
if mode<>'perMini' or seconds is distinct from 10 then raise exception 'Void requires a separate 10-second timer'; end if;
|
|
elsif seconds is null or seconds not between 15 and (case when mode='shared' then 3000 else 600 end) or seconds%15<>0 then raise exception 'Choose a valid tournament time in steps of 15 seconds'; end if;
|
|
settings:=public.resolve_difficulty_preset(p_payload->>'difficulty')||jsonb_build_object('seconds',least(seconds,600),'roundCount',5);
|
|
insert into public.tournaments(name,owner_id,revision_id,starts_at,server_timezone,player_limit,round_count,round_time_seconds,timer_mode,difficulty,difficulty_settings,announce_discord)
|
|
values(title,p_owner,rev.id,start_time,zone,cap,rounds,seconds,mode,p_payload->>'difficulty',settings,coalesce((p_payload->>'announceDiscord')::boolean,false)) returning id into tid;
|
|
insert into public.tournament_members(tournament_id,account_id) values(tid,p_owner);
|
|
return tid;
|
|
end; $$;
|
|
|
|
create or replace function public.read_prometheus_metrics(p_token_hash text) returns jsonb
|
|
language plpgsql security invoker set search_path = '' set statement_timeout = '15s' as $$
|
|
declare settings public.prometheus_settings; collected_at timestamptz := clock_timestamp(); result jsonb;
|
|
begin
|
|
-- The token grants only access to aggregate metrics. Demoting/deleting its
|
|
-- issuing Admin also invalidates it. Lock prevents concurrent scrape stampedes.
|
|
select s.* into settings from public.prometheus_settings s
|
|
where s.id and s.token_hash=p_token_hash
|
|
and exists(select 1 from public.profiles p where p.id=s.updated_by and p.role='admin')
|
|
for update;
|
|
if not found then return null; end if;
|
|
if settings.snapshot_at > collected_at - interval '30 seconds' then
|
|
update public.prometheus_settings set last_scraped_at=collected_at where id;
|
|
return settings.snapshot;
|
|
end if;
|
|
with samples(metric, labels, value) as (
|
|
select 'minegeo_accounts', '{}'::jsonb, count(*)::double precision from public.profiles
|
|
union all select 'minegeo_published_panoramas', '{}'::jsonb, coalesce(sum(panorama_count),0)::double precision from public.datasets where state='published'
|
|
union all select 'minegeo_games_started_24h', '{}'::jsonb, count(*)::double precision from public.game_sessions where created_at >= collected_at - interval '24 hours'
|
|
union all select 'minegeo_rooms_created_24h', '{}'::jsonb, count(*)::double precision from public.multiplayer_rooms where created_at >= collected_at - interval '24 hours'
|
|
union all select 'minegeo_snapshot_timestamp_seconds', '{}'::jsonb, extract(epoch from collected_at)::double precision
|
|
union all
|
|
select 'minegeo_maps', jsonb_build_object('state',v.state), coalesce(c.n,0)::double precision
|
|
from (values('uploading'),('processing'),('draft'),('review'),('published'),('rejected'),('unpublished'),('failed')) v(state)
|
|
left join (select state,count(*) n from public.datasets group by state) c using(state)
|
|
union all
|
|
select 'minegeo_games', jsonb_build_object('difficulty',d.difficulty,'status',v.status), coalesce(c.n,0)::double precision
|
|
from (values('easy'),('normal'),('hard'),('void')) d(difficulty)
|
|
cross join (values('incomplete'),('complete')) v(status)
|
|
left join (select difficulty,case when current_round=5 then 'complete' else 'incomplete' end status,count(*) n
|
|
from public.game_sessions group by 1,2) c using(difficulty,status)
|
|
union all
|
|
select 'minegeo_rooms', jsonb_build_object('state',v.state), coalesce(c.n,0)::double precision
|
|
from (values('lobby'),('loading'),('playing'),('results'),('finished'),('closed')) v(state)
|
|
left join (select status state,count(*) n from public.multiplayer_rooms group by status) c using(state)
|
|
union all
|
|
select 'minegeo_upload_jobs', jsonb_build_object('state',v.state), coalesce(c.n,0)::double precision
|
|
from (values('queued'),('running'),('complete'),('failed'),('cancelled')) v(state)
|
|
left join (select state,count(*) n from public.upload_jobs group by state) c using(state)
|
|
union all
|
|
select 'minegeo_bug_reports', jsonb_build_object('state',v.state), coalesce(c.n,0)::double precision
|
|
from (values('draft'),('pending'),('open'),('done')) v(state)
|
|
left join (select state,count(*) n from public.bug_reports group by state) c using(state)
|
|
union all
|
|
select 'minegeo_content_reports', jsonb_build_object('state',v.state), coalesce(c.n,0)::double precision
|
|
from (values('open'),('resolved'),('confirmed'),('dismissed')) v(state)
|
|
left join (select state,count(*) n from public.reports group by state) c using(state)
|
|
union all
|
|
select 'minegeo_discord_announcements', jsonb_build_object('state',v.state), coalesce(c.n,0)::double precision
|
|
from (values('pending'),('sending'),('sent'),('skipped'),('failed')) v(state)
|
|
left join (select state,count(*) n from public.discord_announcements group by state) c using(state)
|
|
) select jsonb_agg(jsonb_build_object('metric',metric,'labels',labels,'value',value) order by metric,labels::text) into result from samples;
|
|
update public.prometheus_settings set last_scraped_at=collected_at, snapshot_at=collected_at, snapshot=result where id;
|
|
return result;
|
|
end; $$;
|
|
|
|
-- Discard the cached metrics shape when adding the Void dimension.
|
|
update public.prometheus_settings set snapshot=null,snapshot_at=null;
|