Sessions 5-7a: 955 tests, deployment ready
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
-- VYNDR Web — odds cache + waitlist signups
|
||||
-- Idempotent. Safe to apply multiple times.
|
||||
|
||||
create extension if not exists "pgcrypto";
|
||||
|
||||
-- ────────────────────────────────────────────────────────────
|
||||
-- odds_cache — TTL-backed mirror of upstream Odds API data
|
||||
-- Reduces calls to the rate-limited Odds API (500 req/month free tier).
|
||||
-- All Next.js route handlers must read from this table, never the
|
||||
-- upstream API directly.
|
||||
-- ────────────────────────────────────────────────────────────
|
||||
create table if not exists public.odds_cache (
|
||||
cache_key text primary key,
|
||||
sport text not null,
|
||||
data_type text not null,
|
||||
payload jsonb not null,
|
||||
fetched_at timestamptz not null default now(),
|
||||
expires_at timestamptz not null default (now() + interval '5 minutes')
|
||||
);
|
||||
|
||||
create index if not exists idx_odds_cache_sport on public.odds_cache(sport);
|
||||
create index if not exists idx_odds_cache_expires on public.odds_cache(expires_at);
|
||||
|
||||
alter table public.odds_cache enable row level security;
|
||||
|
||||
-- Public read OK (cache contents are public market data anyway).
|
||||
-- Writes restricted to service role (bypasses RLS automatically).
|
||||
drop policy if exists "anyone can read odds cache" on public.odds_cache;
|
||||
create policy "anyone can read odds cache"
|
||||
on public.odds_cache for select
|
||||
using (true);
|
||||
|
||||
-- Cleanup helper — call from a cron or trigger if rows accumulate.
|
||||
create or replace function public.prune_expired_odds_cache()
|
||||
returns integer as $$
|
||||
declare
|
||||
deleted_count integer;
|
||||
begin
|
||||
delete from public.odds_cache
|
||||
where expires_at < now() - interval '1 hour';
|
||||
get diagnostics deleted_count = row_count;
|
||||
return deleted_count;
|
||||
end;
|
||||
$$ language plpgsql security definer set search_path = public;
|
||||
|
||||
-- ────────────────────────────────────────────────────────────
|
||||
-- waitlist_signups — capture email for upcoming launches
|
||||
-- Referenced by /api/waitlist (marketplace pre-launch lists).
|
||||
-- ────────────────────────────────────────────────────────────
|
||||
create table if not exists public.waitlist_signups (
|
||||
id uuid primary key default gen_random_uuid(),
|
||||
email text not null,
|
||||
list text not null default 'general',
|
||||
source text,
|
||||
created_at timestamptz not null default now(),
|
||||
unique (email, list)
|
||||
);
|
||||
|
||||
create index if not exists idx_waitlist_list on public.waitlist_signups(list);
|
||||
create index if not exists idx_waitlist_created on public.waitlist_signups(created_at desc);
|
||||
|
||||
alter table public.waitlist_signups enable row level security;
|
||||
-- Inserts/reads restricted to service role only. No public policies.
|
||||
|
||||
-- ────────────────────────────────────────────────────────────
|
||||
-- founder_pricing_seat_count — convenience view used by checkout
|
||||
-- to decide if the next paid user still gets the $9.99 lock-in.
|
||||
-- ────────────────────────────────────────────────────────────
|
||||
create or replace view public.founder_pricing_seats as
|
||||
select
|
||||
count(*) filter (where founder_pricing) as taken,
|
||||
100 as cap,
|
||||
greatest(0, 100 - count(*) filter (where founder_pricing)) as remaining
|
||||
from public.user_profiles;
|
||||
Reference in New Issue
Block a user