FancyWordle/supabase/schema.sql

288 lines
6.5 KiB
PL/PgSQL

create extension if not exists pgcrypto;
create table if not exists public.wordle_words (
position integer primary key,
word text not null unique check (word ~ '^[a-z]{5}$')
);
insert into public.wordle_words (position, word) values
(1, 'cigar'),
(2, 'rebut'),
(3, 'sissy'),
(4, 'humph'),
(5, 'awake'),
(6, 'blush'),
(7, 'focal'),
(8, 'evade'),
(9, 'naval'),
(10, 'serve'),
(11, 'heath'),
(12, 'dwarf'),
(13, 'model'),
(14, 'karma'),
(15, 'stink'),
(16, 'grade'),
(17, 'quiet'),
(18, 'bench'),
(19, 'abate'),
(20, 'feign'),
(21, 'major'),
(22, 'death'),
(23, 'fresh'),
(24, 'crust'),
(25, 'stool'),
(26, 'colon'),
(27, 'abase'),
(28, 'marry'),
(29, 'react'),
(30, 'batty'),
(31, 'pride'),
(32, 'floss'),
(33, 'helix'),
(34, 'croak'),
(35, 'staff'),
(36, 'paper'),
(37, 'unfed'),
(38, 'whelp'),
(39, 'trawl'),
(40, 'outdo'),
(41, 'adobe'),
(42, 'crazy'),
(43, 'sower'),
(44, 'repay'),
(45, 'digit'),
(46, 'crate'),
(47, 'cluck'),
(48, 'spike'),
(49, 'mimic'),
(50, 'pound'),
(51, 'maxim'),
(52, 'linen'),
(53, 'unmet'),
(54, 'flesh'),
(55, 'booby'),
(56, 'forth'),
(57, 'first'),
(58, 'stand'),
(59, 'belly'),
(60, 'ivory'),
(61, 'seedy'),
(62, 'print'),
(63, 'yearn'),
(64, 'drain'),
(65, 'bribe'),
(66, 'stout'),
(67, 'panel'),
(68, 'crass'),
(69, 'flume'),
(70, 'offal'),
(71, 'agree'),
(72, 'error'),
(73, 'swirl'),
(74, 'argue'),
(75, 'bleed'),
(76, 'delta'),
(77, 'flick'),
(78, 'totem'),
(79, 'wooer'),
(80, 'front'),
(81, 'shrub'),
(82, 'parry'),
(83, 'biome'),
(84, 'lapel'),
(85, 'start'),
(86, 'greet'),
(87, 'goner'),
(88, 'golem'),
(89, 'lusty'),
(90, 'loopy'),
(91, 'round'),
(92, 'audit'),
(93, 'lying'),
(94, 'gamma'),
(95, 'labor'),
(96, 'islet'),
(97, 'civic'),
(98, 'forge'),
(99, 'corny'),
(100, 'moult'),
(101, 'basic'),
(102, 'salad'),
(103, 'agate'),
(104, 'spicy'),
(105, 'spray'),
(106, 'essay'),
(107, 'fjord'),
(108, 'spend'),
(109, 'kebab'),
(110, 'guild'),
(111, 'aback'),
(112, 'motor'),
(113, 'alone'),
(114, 'hatch'),
(115, 'hyper'),
(116, 'thumb'),
(117, 'dowry'),
(118, 'ought'),
(119, 'belch'),
(120, 'dutch')
on conflict (position) do nothing;
alter table public.wordle_words enable row level security;
create table if not exists public.wordle_rounds (
id uuid primary key default gen_random_uuid(),
user_id uuid not null references auth.users(id) on delete cascade,
word text not null check (word ~ '^[a-z]{5}$'),
hour_start timestamptz,
started_at timestamptz not null default now(),
next_playable_at timestamptz not null default (now() + interval '1 hour'),
completed_at timestamptz,
won boolean,
guess_count integer check (guess_count between 1 and 6),
created_at timestamptz not null default now()
);
alter table public.wordle_rounds
add column if not exists hour_start timestamptz;
update public.wordle_rounds
set hour_start = date_trunc('hour', started_at)
where hour_start is null;
alter table public.wordle_rounds
alter column hour_start set not null;
create index if not exists wordle_rounds_user_started_idx
on public.wordle_rounds (user_id, started_at desc);
create unique index if not exists wordle_rounds_user_hour_idx
on public.wordle_rounds (user_id, hour_start);
alter table public.wordle_rounds enable row level security;
drop policy if exists "Users can read their own rounds" on public.wordle_rounds;
create policy "Users can read their own rounds"
on public.wordle_rounds
for select
using (auth.uid() = user_id);
drop function if exists public.start_hourly_round(text);
create or replace function public.start_hourly_round()
returns table (
round_id uuid,
word text,
hour_start timestamptz,
started_at timestamptz,
next_playable_at timestamptz,
completed_at timestamptz,
won boolean,
guess_count integer,
server_now timestamptz,
is_existing boolean
)
language plpgsql
security definer
set search_path = public
as $$
declare
existing_round public.wordle_rounds%rowtype;
new_round public.wordle_rounds%rowtype;
current_hour timestamptz := date_trunc('hour', now());
word_count integer;
word_offset integer;
hourly_word text;
begin
if auth.uid() is null then
raise exception 'Authentication required';
end if;
select count(*) into word_count from public.wordle_words;
if word_count = 0 then
raise exception 'No hourly words are configured';
end if;
word_offset := (floor(extract(epoch from current_hour) / 3600)::bigint % word_count)::integer;
select public.wordle_words.word
into hourly_word
from public.wordle_words
order by position
limit 1 offset word_offset;
select *
into existing_round
from public.wordle_rounds
where user_id = auth.uid()
and public.wordle_rounds.hour_start = current_hour
order by started_at desc
limit 1;
if found then
return query select
existing_round.id,
existing_round.word,
existing_round.hour_start,
existing_round.started_at,
existing_round.next_playable_at,
existing_round.completed_at,
existing_round.won,
existing_round.guess_count,
now(),
true;
return;
end if;
insert into public.wordle_rounds (user_id, word, hour_start, next_playable_at)
values (auth.uid(), hourly_word, current_hour, current_hour + interval '1 hour')
returning * into new_round;
return query select
new_round.id,
new_round.word,
new_round.hour_start,
new_round.started_at,
new_round.next_playable_at,
new_round.completed_at,
new_round.won,
new_round.guess_count,
now(),
false;
end;
$$;
create or replace function public.complete_hourly_round(
round_id uuid,
did_win boolean,
guess_total integer
)
returns void
language plpgsql
security definer
set search_path = public
as $$
begin
if auth.uid() is null then
raise exception 'Authentication required';
end if;
if guess_total < 1 or guess_total > 6 then
raise exception 'Guess total must be between 1 and 6';
end if;
update public.wordle_rounds
set completed_at = coalesce(completed_at, now()),
won = did_win,
guess_count = guess_total
where id = round_id
and user_id = auth.uid();
end;
$$;
revoke all on public.wordle_rounds from anon, authenticated;
revoke all on public.wordle_words from anon, authenticated;
grant select on public.wordle_rounds to authenticated;
grant execute on function public.start_hourly_round() to authenticated;
grant execute on function public.complete_hourly_round(uuid, boolean, integer) to authenticated;