Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com>
This commit is contained in:
gpt-engineer-app[bot]
2026-04-17 02:27:31 +00:00
co-authored by renee-png
parent 8b21c6efef
commit bf2489fa19
2 changed files with 320 additions and 0 deletions
+175
View File
@@ -665,6 +665,65 @@ export type Database = {
}
Relationships: []
}
conversation_members: {
Row: {
conversation_id: string
created_at: string
id: string
last_read_at: string
user_id: string
}
Insert: {
conversation_id: string
created_at?: string
id?: string
last_read_at?: string
user_id: string
}
Update: {
conversation_id?: string
created_at?: string
id?: string
last_read_at?: string
user_id?: string
}
Relationships: [
{
foreignKeyName: "conversation_members_conversation_id_fkey"
columns: ["conversation_id"]
isOneToOne: false
referencedRelation: "conversations"
referencedColumns: ["id"]
},
]
}
conversations: {
Row: {
created_at: string
created_by: string | null
id: string
name: string | null
type: string
updated_at: string
}
Insert: {
created_at?: string
created_by?: string | null
id?: string
name?: string | null
type: string
updated_at?: string
}
Update: {
created_at?: string
created_by?: string | null
id?: string
name?: string | null
type?: string
updated_at?: string
}
Relationships: []
}
document_folders: {
Row: {
case_id: string
@@ -1123,6 +1182,118 @@ export type Database = {
},
]
}
message_attachments: {
Row: {
created_at: string
id: string
message_id: string
mime_type: string | null
name: string
size_bytes: number | null
storage_path: string
}
Insert: {
created_at?: string
id?: string
message_id: string
mime_type?: string | null
name: string
size_bytes?: number | null
storage_path: string
}
Update: {
created_at?: string
id?: string
message_id?: string
mime_type?: string | null
name?: string
size_bytes?: number | null
storage_path?: string
}
Relationships: [
{
foreignKeyName: "message_attachments_message_id_fkey"
columns: ["message_id"]
isOneToOne: false
referencedRelation: "messages"
referencedColumns: ["id"]
},
]
}
message_mentions: {
Row: {
conversation_id: string
created_at: string
id: string
mentioned_user_id: string
message_id: string
}
Insert: {
conversation_id: string
created_at?: string
id?: string
mentioned_user_id: string
message_id: string
}
Update: {
conversation_id?: string
created_at?: string
id?: string
mentioned_user_id?: string
message_id?: string
}
Relationships: [
{
foreignKeyName: "message_mentions_conversation_id_fkey"
columns: ["conversation_id"]
isOneToOne: false
referencedRelation: "conversations"
referencedColumns: ["id"]
},
{
foreignKeyName: "message_mentions_message_id_fkey"
columns: ["message_id"]
isOneToOne: false
referencedRelation: "messages"
referencedColumns: ["id"]
},
]
}
messages: {
Row: {
body: string
conversation_id: string
created_at: string
edited_at: string | null
id: string
sender_id: string
}
Insert: {
body?: string
conversation_id: string
created_at?: string
edited_at?: string | null
id?: string
sender_id: string
}
Update: {
body?: string
conversation_id?: string
created_at?: string
edited_at?: string | null
id?: string
sender_id?: string
}
Relationships: [
{
foreignKeyName: "messages_conversation_id_fkey"
columns: ["conversation_id"]
isOneToOne: false
referencedRelation: "conversations"
referencedColumns: ["id"]
},
]
}
profiles: {
Row: {
created_at: string
@@ -1283,6 +1454,10 @@ export type Database = {
Returns: boolean
}
is_admin: { Args: { _user_id: string }; Returns: boolean }
is_conversation_member: {
Args: { _conv_id: string; _user_id: string }
Returns: boolean
}
}
Enums: {
app_role: "admin" | "attorney" | "staff"
@@ -0,0 +1,145 @@
-- Conversations
CREATE TABLE public.conversations (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
type text NOT NULL CHECK (type IN ('dm','channel')),
name text,
created_by uuid,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
ALTER TABLE public.conversations ENABLE ROW LEVEL SECURITY;
-- Conversation members
CREATE TABLE public.conversation_members (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
conversation_id uuid NOT NULL REFERENCES public.conversations(id) ON DELETE CASCADE,
user_id uuid NOT NULL,
last_read_at timestamptz NOT NULL DEFAULT now(),
created_at timestamptz NOT NULL DEFAULT now(),
UNIQUE (conversation_id, user_id)
);
ALTER TABLE public.conversation_members ENABLE ROW LEVEL SECURITY;
CREATE INDEX idx_cm_conv ON public.conversation_members(conversation_id);
CREATE INDEX idx_cm_user ON public.conversation_members(user_id);
-- Security definer to avoid recursion
CREATE OR REPLACE FUNCTION public.is_conversation_member(_conv_id uuid, _user_id uuid)
RETURNS boolean LANGUAGE sql STABLE SECURITY DEFINER SET search_path = public AS $$
SELECT EXISTS (SELECT 1 FROM public.conversation_members WHERE conversation_id = _conv_id AND user_id = _user_id)
$$;
-- Messages
CREATE TABLE public.messages (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
conversation_id uuid NOT NULL REFERENCES public.conversations(id) ON DELETE CASCADE,
sender_id uuid NOT NULL,
body text NOT NULL DEFAULT '',
edited_at timestamptz,
created_at timestamptz NOT NULL DEFAULT now()
);
ALTER TABLE public.messages ENABLE ROW LEVEL SECURITY;
CREATE INDEX idx_msg_conv ON public.messages(conversation_id, created_at DESC);
-- Attachments
CREATE TABLE public.message_attachments (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
message_id uuid NOT NULL REFERENCES public.messages(id) ON DELETE CASCADE,
storage_path text NOT NULL,
name text NOT NULL,
mime_type text,
size_bytes bigint,
created_at timestamptz NOT NULL DEFAULT now()
);
ALTER TABLE public.message_attachments ENABLE ROW LEVEL SECURITY;
CREATE INDEX idx_att_msg ON public.message_attachments(message_id);
-- Mentions
CREATE TABLE public.message_mentions (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
message_id uuid NOT NULL REFERENCES public.messages(id) ON DELETE CASCADE,
conversation_id uuid NOT NULL REFERENCES public.conversations(id) ON DELETE CASCADE,
mentioned_user_id uuid NOT NULL,
created_at timestamptz NOT NULL DEFAULT now()
);
ALTER TABLE public.message_mentions ENABLE ROW LEVEL SECURITY;
CREATE INDEX idx_mention_user ON public.message_mentions(mentioned_user_id);
CREATE INDEX idx_mention_msg ON public.message_mentions(message_id);
-- Trigger to update conversations.updated_at on new message
CREATE OR REPLACE FUNCTION public.tg_touch_conversation()
RETURNS trigger LANGUAGE plpgsql SET search_path = public AS $$
BEGIN
UPDATE public.conversations SET updated_at = now() WHERE id = NEW.conversation_id;
RETURN NEW;
END;
$$;
CREATE TRIGGER trg_touch_conv AFTER INSERT ON public.messages
FOR EACH ROW EXECUTE FUNCTION public.tg_touch_conversation();
CREATE TRIGGER trg_conv_updated BEFORE UPDATE ON public.conversations
FOR EACH ROW EXECUTE FUNCTION public.tg_set_updated_at();
-- RLS POLICIES
-- Conversations
CREATE POLICY conv_select ON public.conversations FOR SELECT TO authenticated
USING (public.is_conversation_member(id, auth.uid()));
CREATE POLICY conv_insert ON public.conversations FOR INSERT TO authenticated
WITH CHECK (auth.uid() IS NOT NULL);
CREATE POLICY conv_update ON public.conversations FOR UPDATE TO authenticated
USING (public.is_conversation_member(id, auth.uid()));
CREATE POLICY conv_delete ON public.conversations FOR DELETE TO authenticated
USING (created_by = auth.uid() OR public.is_admin(auth.uid()));
-- Conversation members
CREATE POLICY cm_select ON public.conversation_members FOR SELECT TO authenticated
USING (public.is_conversation_member(conversation_id, auth.uid()));
CREATE POLICY cm_insert ON public.conversation_members FOR INSERT TO authenticated
WITH CHECK (auth.uid() IS NOT NULL);
CREATE POLICY cm_update_self ON public.conversation_members FOR UPDATE TO authenticated
USING (user_id = auth.uid());
CREATE POLICY cm_delete ON public.conversation_members FOR DELETE TO authenticated
USING (user_id = auth.uid() OR public.is_admin(auth.uid()));
-- Messages
CREATE POLICY msg_select ON public.messages FOR SELECT TO authenticated
USING (public.is_conversation_member(conversation_id, auth.uid()));
CREATE POLICY msg_insert ON public.messages FOR INSERT TO authenticated
WITH CHECK (sender_id = auth.uid() AND public.is_conversation_member(conversation_id, auth.uid()));
CREATE POLICY msg_update_own ON public.messages FOR UPDATE TO authenticated
USING (sender_id = auth.uid());
CREATE POLICY msg_delete_own ON public.messages FOR DELETE TO authenticated
USING (sender_id = auth.uid() OR public.is_admin(auth.uid()));
-- Attachments
CREATE POLICY att_select ON public.message_attachments FOR SELECT TO authenticated
USING (EXISTS (SELECT 1 FROM public.messages m WHERE m.id = message_id AND public.is_conversation_member(m.conversation_id, auth.uid())));
CREATE POLICY att_insert ON public.message_attachments FOR INSERT TO authenticated
WITH CHECK (EXISTS (SELECT 1 FROM public.messages m WHERE m.id = message_id AND m.sender_id = auth.uid()));
CREATE POLICY att_delete ON public.message_attachments FOR DELETE TO authenticated
USING (EXISTS (SELECT 1 FROM public.messages m WHERE m.id = message_id AND (m.sender_id = auth.uid() OR public.is_admin(auth.uid()))));
-- Mentions
CREATE POLICY mention_select ON public.message_mentions FOR SELECT TO authenticated
USING (mentioned_user_id = auth.uid() OR public.is_conversation_member(conversation_id, auth.uid()));
CREATE POLICY mention_insert ON public.message_mentions FOR INSERT TO authenticated
WITH CHECK (public.is_conversation_member(conversation_id, auth.uid()));
CREATE POLICY mention_delete ON public.message_mentions FOR DELETE TO authenticated
USING (EXISTS (SELECT 1 FROM public.messages m WHERE m.id = message_id AND m.sender_id = auth.uid()));
-- Realtime
ALTER PUBLICATION supabase_realtime ADD TABLE public.messages;
ALTER PUBLICATION supabase_realtime ADD TABLE public.conversation_members;
ALTER PUBLICATION supabase_realtime ADD TABLE public.message_mentions;
ALTER PUBLICATION supabase_realtime ADD TABLE public.conversations;
-- Storage bucket for attachments
INSERT INTO storage.buckets (id, name, public) VALUES ('message-attachments', 'message-attachments', false)
ON CONFLICT (id) DO NOTHING;
-- Storage policies: any authenticated user can upload/read (we gate via signed URLs and DB-level access)
CREATE POLICY "msg_attach_select" ON storage.objects FOR SELECT TO authenticated
USING (bucket_id = 'message-attachments');
CREATE POLICY "msg_attach_insert" ON storage.objects FOR INSERT TO authenticated
WITH CHECK (bucket_id = 'message-attachments' AND auth.uid()::text = (storage.foldername(name))[1]);
CREATE POLICY "msg_attach_delete" ON storage.objects FOR DELETE TO authenticated
USING (bucket_id = 'message-attachments' AND auth.uid()::text = (storage.foldername(name))[1]);