create table if not exists public.clients ( id uuid primary key default gen_random_uuid(), email text not null, normalized_email text not null, first_name text, last_name text, phone text, last_login_at timestamptz, created_at timestamptz not null default now(), updated_at timestamptz not null default now() ); create unique index if not exists clients_normalized_email_idx on public.clients (normalized_email); create table if not exists public.client_access ( id bigserial primary key, client_id uuid not null references public.clients(id) on delete cascade, tablo_id text not null references public.tablos(id) on delete cascade, granted_by uuid not null references public.profiles(id), granted_at timestamptz not null default now(), revoked_at timestamptz, created_at timestamptz not null default now() ); create unique index if not exists client_access_active_unique_idx on public.client_access (client_id, tablo_id) where revoked_at is null; create index if not exists client_access_client_id_idx on public.client_access (client_id); create index if not exists client_access_tablo_id_idx on public.client_access (tablo_id); create table if not exists public.client_magic_links ( id bigserial primary key, client_id uuid not null references public.clients(id) on delete cascade, email text not null, purpose text not null check (purpose in ('invite', 'login')), token_hash text, jti text, redirect_to text, expires_at timestamptz not null, consumed_at timestamptz, created_by uuid references public.profiles(id), created_at timestamptz not null default now(), constraint client_magic_links_token_reference_check check ( token_hash is not null or jti is not null ) ); create index if not exists client_magic_links_active_idx on public.client_magic_links (client_id, purpose, expires_at) where consumed_at is null; create unique index if not exists client_magic_links_jti_unique_idx on public.client_magic_links (jti) where jti is not null; create unique index if not exists client_magic_links_token_hash_unique_idx on public.client_magic_links (token_hash) where token_hash is not null; alter table public.clients enable row level security; alter table public.client_access enable row level security; alter table public.client_magic_links enable row level security; drop trigger if exists set_clients_updated_at on public.clients; create trigger set_clients_updated_at before update on public.clients for each row execute function public.set_updated_at();