From ec9c9622aa5e29bea4157219ac8d6b2dfd06b6e3 Mon Sep 17 00:00:00 2001 From: Arthur Belleville Date: Wed, 15 Apr 2026 08:58:10 +0200 Subject: [PATCH] feat(db): add is_client column and client_invites table --- packages/shared-types/src/database.types.ts | 51 +++++++++++++++++++ .../20260415120000_add_client_invites.sql | 40 +++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 supabase/migrations/20260415120000_add_client_invites.sql diff --git a/packages/shared-types/src/database.types.ts b/packages/shared-types/src/database.types.ts index c48c314..3ab03a5 100644 --- a/packages/shared-types/src/database.types.ts +++ b/packages/shared-types/src/database.types.ts @@ -78,6 +78,54 @@ export type Database = { }, ]; }; + client_invites: { + Row: { + created_at: string; + expires_at: string; + id: number; + invited_by: string; + invited_email: string; + invite_token: string; + is_pending: boolean; + tablo_id: string; + }; + Insert: { + created_at?: string; + expires_at?: string; + id?: number; + invited_by: string; + invited_email: string; + invite_token: string; + is_pending?: boolean; + tablo_id: string; + }; + Update: { + created_at?: string; + expires_at?: string; + id?: number; + invited_by?: string; + invited_email?: string; + invite_token?: string; + is_pending?: boolean; + tablo_id?: string; + }; + Relationships: [ + { + foreignKeyName: "client_invites_tablo_id_fkey"; + columns: ["tablo_id"]; + isOneToOne: false; + referencedRelation: "tablos"; + referencedColumns: ["id"]; + }, + { + foreignKeyName: "client_invites_invited_by_fkey"; + columns: ["invited_by"]; + isOneToOne: false; + referencedRelation: "profiles"; + referencedColumns: ["id"]; + }, + ]; + }; devis: { Row: { client_email: string; @@ -385,6 +433,7 @@ export type Database = { email: string | null; first_name: string | null; id: string; + is_client: boolean; is_temporary: boolean; last_name: string | null; last_signed_in: string | null; @@ -398,6 +447,7 @@ export type Database = { email?: string | null; first_name?: string | null; id: string; + is_client?: boolean; is_temporary?: boolean; last_name?: string | null; last_signed_in?: string | null; @@ -411,6 +461,7 @@ export type Database = { email?: string | null; first_name?: string | null; id?: string; + is_client?: boolean; is_temporary?: boolean; last_name?: string | null; last_signed_in?: string | null; diff --git a/supabase/migrations/20260415120000_add_client_invites.sql b/supabase/migrations/20260415120000_add_client_invites.sql new file mode 100644 index 0000000..7ee07fd --- /dev/null +++ b/supabase/migrations/20260415120000_add_client_invites.sql @@ -0,0 +1,40 @@ +-- Add is_client column to profiles +ALTER TABLE public.profiles + ADD COLUMN is_client boolean NOT NULL DEFAULT false; + +-- Create client_invites table +CREATE TABLE public.client_invites ( + id serial PRIMARY KEY, + tablo_id text NOT NULL REFERENCES public.tablos(id) ON DELETE CASCADE, + invited_email varchar(255) NOT NULL, + invited_by uuid NOT NULL REFERENCES public.profiles(id), + invite_token text NOT NULL, + expires_at timestamptz NOT NULL DEFAULT (now() + interval '30 days'), + is_pending boolean NOT NULL DEFAULT true, + created_at timestamptz NOT NULL DEFAULT now() +); + +-- Index for token lookups +CREATE UNIQUE INDEX idx_client_invites_token ON public.client_invites(invite_token); + +-- Index for listing invites by tablo +CREATE INDEX idx_client_invites_tablo ON public.client_invites(tablo_id, is_pending); + +-- RLS +ALTER TABLE public.client_invites ENABLE ROW LEVEL SECURITY; + +-- Admins can manage invites they created +CREATE POLICY "Admins can manage their client invites" + ON public.client_invites + FOR ALL + USING (invited_by = auth.uid()); + +-- Client users can read invites sent to their email +CREATE POLICY "Clients can read their own invites" + ON public.client_invites + FOR SELECT + USING ( + invited_email = ( + SELECT email FROM auth.users WHERE id = auth.uid() + ) + );