feat(db): add ai_chat_log so the ask endpoint is actually rate limited

`ask` has never been deployed, and the CI step in this branch will publish it.
Its limiter counts rows in ai_chat_log to cap requests at 8/min and 120/day per
IP, but no migration ever created that table, and the limiter fails open when
the count cannot be read.

Shipping `ask` without this would put an unauthenticated endpoint with
Access-Control-Allow-Origin: * in front of paid model APIs with no limit at all.

Applied to the live database as well.
This commit is contained in:
2026-07-29 22:36:06 -07:00
parent 35723996e1
commit 41bfd70322
@@ -0,0 +1,28 @@
-- Rate-limit ledger for the public `ask` edge function (news.pex.mom assistant).
--
-- `ask` is unauthenticated and sends `Access-Control-Allow-Origin: *`, so this
-- table is its only throttle: 8 requests per IP per minute, 120 per day. The
-- limiter counts rows here and fails open when the count cannot be read, which
-- means shipping `ask` without this table would leave a public endpoint calling
-- paid model APIs with no limit at all.
create table if not exists public.ai_chat_log (
id bigserial primary key,
ip text not null,
created_at timestamptz not null default now()
);
-- The limiter always filters on both columns together (ip = ? and created_at >= ?).
create index if not exists ai_chat_log_ip_created_at_idx
on public.ai_chat_log (ip, created_at desc);
-- Only the service role touches this table; the function reaches it through
-- PostgREST with the service key, which bypasses RLS. Enabling RLS without any
-- policy therefore keeps the behaviour intact while denying anon and
-- authenticated clients, who have no reason to read a table of IP addresses.
alter table public.ai_chat_log enable row level security;
revoke all on public.ai_chat_log from anon, authenticated;
comment on table public.ai_chat_log is
'Rate-limit ledger for the public ask endpoint. Holds IP addresses; prune rows older than the 24h window the limiter uses.';