first commit
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
CREATE TABLE IF NOT EXISTS "public"."conversations" (
|
||||
"id" "uuid" DEFAULT "gen_random_uuid"() NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT "now"(),
|
||||
"updated_at" timestamp with time zone DEFAULT "now"(),
|
||||
"user_id" "uuid" NOT NULL,
|
||||
"title" "text" NOT NULL,
|
||||
"type" "public"."conversation-type" DEFAULT 'parametric'::"public"."conversation-type" NOT NULL,
|
||||
"privacy" "public"."privacy_type" DEFAULT 'private'::"public"."privacy_type" NOT NULL,
|
||||
"current_message_leaf_id" "uuid",
|
||||
"settings" "jsonb" DEFAULT '{}'::"jsonb" NOT NULL
|
||||
);
|
||||
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS conversations_pkey ON "public"."conversations" USING btree (id);
|
||||
|
||||
ALTER TABLE "public"."conversations" ADD CONSTRAINT "conversations_pkey" PRIMARY KEY USING INDEX "conversations_pkey";
|
||||
|
||||
ALTER TABLE "public"."conversations" ADD CONSTRAINT "conversations_user_id_fkey" FOREIGN KEY (user_id) REFERENCES auth.users(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
|
||||
|
||||
ALTER TABLE "public"."conversations" VALIDATE CONSTRAINT "conversations_user_id_fkey";
|
||||
|
||||
|
||||
CREATE INDEX IF NOT EXISTS conversations_created_at_idx ON "public"."conversations" USING btree (created_at);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS conversations_updated_at_idx ON "public"."conversations" USING btree (updated_at);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS conversations_user_id_idx ON "public"."conversations" USING btree (user_id);
|
||||
|
||||
|
||||
CREATE POLICY "Anyone can view a public conversation" ON "public"."conversations" FOR SELECT TO "authenticated", "anon" USING (("privacy" = 'public'::"public"."privacy_type"));
|
||||
|
||||
CREATE POLICY "Users can manage their own conversations" ON "public"."conversations" USING ( (SELECT "auth"."uid"()) = "user_id" );
|
||||
|
||||
ALTER TABLE "public"."conversations" ENABLE ROW LEVEL SECURITY;
|
||||
@@ -0,0 +1,34 @@
|
||||
CREATE TABLE IF NOT EXISTS "public"."images" (
|
||||
"id" "uuid" DEFAULT "gen_random_uuid"() NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT "now"() NOT NULL,
|
||||
"status" "public"."generation-status" DEFAULT 'pending'::"public"."generation-status" NOT NULL,
|
||||
"user_id" "uuid" NOT NULL,
|
||||
"conversation_id" "uuid" NOT NULL,
|
||||
"image_generation_call_id" "text",
|
||||
"prompt" "jsonb" DEFAULT '{}'::"jsonb" NOT NULL
|
||||
);
|
||||
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS images_pkey ON "public"."images" USING btree (id);
|
||||
|
||||
ALTER TABLE "public"."images" ADD CONSTRAINT "images_pkey" PRIMARY KEY USING INDEX "images_pkey";
|
||||
|
||||
ALTER TABLE "public"."images" ADD CONSTRAINT "images_conversation_id_fkey" FOREIGN KEY (conversation_id) REFERENCES conversations(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
|
||||
|
||||
ALTER TABLE "public"."images" VALIDATE CONSTRAINT "images_conversation_id_fkey";
|
||||
|
||||
ALTER TABLE "public"."images" ADD CONSTRAINT "images_user_id_fkey" FOREIGN KEY (user_id) REFERENCES auth.users(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
|
||||
|
||||
ALTER TABLE "public"."images" VALIDATE CONSTRAINT "images_user_id_fkey";
|
||||
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_images_image_generation_call_id ON "public"."images" USING "btree" ("image_generation_call_id");
|
||||
|
||||
|
||||
CREATE POLICY "Public conversations images" ON "public"."images" FOR SELECT TO "authenticated", "anon" USING ((EXISTS ( SELECT 1
|
||||
FROM "public"."conversations"
|
||||
WHERE (("conversations"."id" = "images"."conversation_id") AND ("conversations"."privacy" = 'public'::"public"."privacy_type")))));
|
||||
|
||||
CREATE POLICY "User can manage their data" ON "public"."images" TO "authenticated" USING ((( SELECT "auth"."uid"()) = "user_id")) WITH CHECK ((( SELECT "auth"."uid"()) = "user_id"));
|
||||
|
||||
ALTER TABLE "public"."images" ENABLE ROW LEVEL SECURITY;
|
||||
@@ -0,0 +1,32 @@
|
||||
CREATE TABLE IF NOT EXISTS "public"."meshes" (
|
||||
"id" "uuid" DEFAULT "gen_random_uuid"() NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT "now"() NOT NULL,
|
||||
"status" "public"."generation-status" DEFAULT 'pending'::"public"."generation-status" NOT NULL,
|
||||
"user_id" "uuid" NOT NULL,
|
||||
"images" "uuid"[],
|
||||
"conversation_id" "uuid" NOT NULL,
|
||||
"prompt" "jsonb" DEFAULT '{}'::"jsonb" NOT NULL,
|
||||
"file_type" "public"."mesh_file_type" DEFAULT 'glb'::"public"."mesh_file_type" NOT NULL
|
||||
);
|
||||
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS meshes_pkey ON "public"."meshes" USING btree (id);
|
||||
|
||||
ALTER TABLE "public"."meshes" ADD CONSTRAINT "meshes_pkey" PRIMARY KEY USING INDEX "meshes_pkey";
|
||||
|
||||
ALTER TABLE "public"."meshes" ADD CONSTRAINT "meshes_conversation_id_fkey" FOREIGN KEY (conversation_id) REFERENCES conversations(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
|
||||
|
||||
ALTER TABLE "public"."meshes" VALIDATE CONSTRAINT "meshes_conversation_id_fkey";
|
||||
|
||||
ALTER TABLE "public"."meshes" ADD CONSTRAINT "meshes_user_id_fkey" FOREIGN KEY (user_id) REFERENCES auth.users(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
|
||||
|
||||
ALTER TABLE "public"."meshes" VALIDATE CONSTRAINT "meshes_user_id_fkey";
|
||||
|
||||
|
||||
CREATE POLICY "Everyone can view meshes associated with public conversations" ON "public"."meshes" FOR SELECT TO "authenticated", "anon" USING ((EXISTS ( SELECT 1
|
||||
FROM "public"."conversations"
|
||||
WHERE (("conversations"."id" = "meshes"."conversation_id") AND ("conversations"."privacy" = 'public'::"public"."privacy_type")))));
|
||||
|
||||
CREATE POLICY "Users can manage their meshes" ON "public"."meshes" USING ( (SELECT "auth"."uid"()) = "user_id" );
|
||||
|
||||
ALTER TABLE "public"."meshes" ENABLE ROW LEVEL SECURITY;
|
||||
@@ -0,0 +1,36 @@
|
||||
CREATE TABLE IF NOT EXISTS "public"."messages" (
|
||||
"id" "uuid" DEFAULT "gen_random_uuid"() NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT "now"() NOT NULL,
|
||||
"conversation_id" "uuid" NOT NULL,
|
||||
"role" "text" NOT NULL,
|
||||
"parts" "jsonb" DEFAULT '[]'::"jsonb" NOT NULL,
|
||||
"metadata" "jsonb" DEFAULT '{}'::"jsonb" NOT NULL,
|
||||
"content" "jsonb",
|
||||
"rating" smallint DEFAULT '0'::smallint NOT NULL,
|
||||
"parent_message_id" "uuid",
|
||||
CONSTRAINT "messages_role_check" CHECK (("role" = ANY (ARRAY['user'::"text", 'assistant'::"text"]))),
|
||||
CONSTRAINT "messages_payload_present" CHECK ((jsonb_array_length("parts") > 0) OR ("content" IS NOT NULL))
|
||||
);
|
||||
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS messages_pkey ON "public"."messages" USING btree (id);
|
||||
|
||||
ALTER TABLE "public"."messages" ADD CONSTRAINT "messages_pkey" PRIMARY KEY USING INDEX "messages_pkey";
|
||||
|
||||
ALTER TABLE "public"."messages" ADD CONSTRAINT "messages_conversation_id_fkey" FOREIGN KEY (conversation_id) REFERENCES conversations(id) ON DELETE CASCADE not valid;
|
||||
|
||||
ALTER TABLE "public"."messages" VALIDATE CONSTRAINT "messages_conversation_id_fkey";
|
||||
|
||||
|
||||
CREATE INDEX IF NOT EXISTS messages_conversation_id_idx ON "public"."messages" USING btree (conversation_id);
|
||||
|
||||
|
||||
CREATE POLICY "Public conversations messages" ON "public"."messages" FOR SELECT TO "authenticated", "anon" USING ((EXISTS ( SELECT 1
|
||||
FROM "public"."conversations"
|
||||
WHERE (("conversations"."id" = "messages"."conversation_id") AND ("conversations"."privacy" = 'public'::"public"."privacy_type")))));
|
||||
|
||||
CREATE POLICY "Users can manage messages in their conversations" ON "public"."messages" USING (((SELECT "auth"."uid"()) IN ( SELECT "conversations"."user_id"
|
||||
FROM "public"."conversations"
|
||||
WHERE ("conversations"."id" = "messages"."conversation_id"))));
|
||||
|
||||
ALTER TABLE "public"."messages" ENABLE ROW LEVEL SECURITY;
|
||||
@@ -0,0 +1,31 @@
|
||||
CREATE TABLE IF NOT EXISTS "public"."previews" (
|
||||
"id" "uuid" DEFAULT "gen_random_uuid"() NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT "now"() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT "now"() NOT NULL,
|
||||
"status" "public"."generation-status" DEFAULT 'pending'::"public"."generation-status" NOT NULL,
|
||||
"user_id" "uuid" NOT NULL,
|
||||
"conversation_id" "uuid" NOT NULL,
|
||||
"mesh_id" "uuid" NOT NULL
|
||||
);
|
||||
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS previews_pkey ON "public"."previews" USING btree (id);
|
||||
|
||||
ALTER TABLE "public"."previews" ADD CONSTRAINT "previews_pkey" PRIMARY KEY USING INDEX "previews_pkey";
|
||||
|
||||
ALTER TABLE "public"."previews" ADD CONSTRAINT "previews_user_id_fkey" FOREIGN KEY (user_id) REFERENCES auth.users(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
|
||||
|
||||
ALTER TABLE "public"."previews" VALIDATE CONSTRAINT "previews_user_id_fkey";
|
||||
|
||||
ALTER TABLE "public"."previews" ADD CONSTRAINT "previews_conversation_id_fkey" FOREIGN KEY (conversation_id) REFERENCES conversations(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
|
||||
|
||||
ALTER TABLE "public"."previews" VALIDATE CONSTRAINT "previews_conversation_id_fkey";
|
||||
|
||||
ALTER TABLE "public"."previews" ADD CONSTRAINT "previews_mesh_id_fkey" FOREIGN KEY (mesh_id) REFERENCES meshes(id) ON DELETE CASCADE not valid;
|
||||
|
||||
ALTER TABLE "public"."previews" VALIDATE CONSTRAINT "previews_mesh_id_fkey";
|
||||
|
||||
|
||||
ALTER TABLE "public"."previews" ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
CREATE POLICY "Users can manage their own previews" ON "public"."previews" USING ( (SELECT "auth"."uid"()) = "user_id" );
|
||||
@@ -0,0 +1,21 @@
|
||||
CREATE TABLE IF NOT EXISTS "public"."profiles" (
|
||||
"id" "uuid" DEFAULT "gen_random_uuid"() NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT "now"() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT "now"() NOT NULL,
|
||||
"user_id" "uuid" NOT NULL,
|
||||
"full_name" "text" NOT NULL,
|
||||
"notifications_enabled" boolean DEFAULT false NOT NULL,
|
||||
"avatar_path" "text" DEFAULT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS profiles_pkey ON "public"."profiles" USING btree (id);
|
||||
|
||||
ALTER TABLE "public"."profiles" ADD CONSTRAINT "profiles_pkey" PRIMARY KEY USING INDEX "profiles_pkey";
|
||||
|
||||
ALTER TABLE "public"."profiles" ADD CONSTRAINT "profiles_user_id_fkey" FOREIGN KEY (user_id) REFERENCES auth.users(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
|
||||
|
||||
ALTER TABLE "public"."profiles" VALIDATE CONSTRAINT "profiles_user_id_fkey";
|
||||
|
||||
CREATE POLICY "Users can manage their own profile" ON "public"."profiles" USING ( (SELECT "auth"."uid"()) = "user_id" );
|
||||
|
||||
ALTER TABLE "public"."profiles" ENABLE ROW LEVEL SECURITY;
|
||||
@@ -0,0 +1,18 @@
|
||||
CREATE TABLE IF NOT EXISTS "public"."prompts" (
|
||||
"id" bigint generated by default as identity not null,
|
||||
"created_at" timestamp with time zone not null default now(),
|
||||
"user_id" "uuid" NOT NULL,
|
||||
"type" "public"."prompt_type" DEFAULT 'chat'::"public"."prompt_type" NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS prompts_pkey ON "public"."prompts" USING btree (id);
|
||||
|
||||
ALTER TABLE "public"."prompts" ADD CONSTRAINT "prompts_pkey" PRIMARY KEY USING INDEX "prompts_pkey";
|
||||
|
||||
ALTER TABLE "public"."prompts" ADD CONSTRAINT "prompts_user_id_fkey" FOREIGN KEY (user_id) REFERENCES auth.users(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
|
||||
|
||||
ALTER TABLE "public"."prompts" VALIDATE CONSTRAINT "prompts_user_id_fkey";
|
||||
|
||||
CREATE POLICY "Users can view their own prompts" ON "public"."prompts" FOR SELECT USING ((SELECT "auth"."uid"()) = "user_id");
|
||||
|
||||
ALTER TABLE "public"."prompts" ENABLE ROW LEVEL SECURITY;
|
||||
@@ -0,0 +1,48 @@
|
||||
-- Images and Meshes
|
||||
CREATE POLICY "Give users access to own folder images_select" ON storage.objects FOR SELECT TO public USING (bucket_id = 'images' AND (select auth.uid()::text) = (storage.foldername(name))[1]);
|
||||
|
||||
CREATE POLICY "Give users access to own folder images_insert" ON storage.objects FOR INSERT TO public WITH CHECK (bucket_id = 'images' AND (select auth.uid()::text) = (storage.foldername(name))[1]);
|
||||
|
||||
CREATE POLICY "Give users access to own folder images_update" ON storage.objects FOR UPDATE TO public USING (bucket_id = 'images' AND (select auth.uid()::text) = (storage.foldername(name))[1]);
|
||||
|
||||
CREATE POLICY "Give users access to own folder images_delete" ON storage.objects FOR DELETE TO public USING (bucket_id = 'images' AND (select auth.uid()::text) = (storage.foldername(name))[1]);
|
||||
|
||||
CREATE POLICY "Give users access to own folder meshes_select" ON storage.objects FOR SELECT TO public USING (bucket_id = 'meshes' AND (select auth.uid()::text) = (storage.foldername(name))[1]);
|
||||
|
||||
CREATE POLICY "Give users access to own folder meshes_insert" ON storage.objects FOR INSERT TO public WITH CHECK (bucket_id = 'meshes' AND (select auth.uid()::text) = (storage.foldername(name))[1]);
|
||||
|
||||
CREATE POLICY "Give users access to own folder meshes_update" ON storage.objects FOR UPDATE TO public USING (bucket_id = 'meshes' AND (select auth.uid()::text) = (storage.foldername(name))[1]);
|
||||
|
||||
CREATE POLICY "Give users access to own folder meshes_delete" ON storage.objects FOR DELETE TO public USING (bucket_id = 'meshes' AND (select auth.uid()::text) = (storage.foldername(name))[1]);
|
||||
|
||||
-- Shared Conversations
|
||||
CREATE POLICY "Public conversations allow anyone to view images_select" ON storage.objects FOR SELECT TO anon, authenticated USING (((bucket_id = 'images'::text) AND (EXISTS ( SELECT 1 FROM conversations WHERE ((conversations.privacy = 'public') AND ((conversations.id)::text = (storage.foldername(objects.name))[2]))))));
|
||||
|
||||
CREATE POLICY "Public conversations allow anyone to view meshes_select" ON storage.objects FOR SELECT TO anon, authenticated USING (((bucket_id = 'meshes'::text) AND (EXISTS ( SELECT 1 FROM conversations WHERE ((conversations.privacy = 'public') AND ((conversations.id)::text = (storage.foldername(objects.name))[2]))))));
|
||||
|
||||
-- Previews
|
||||
CREATE POLICY "Give users access to own folder previews_select" ON storage.objects FOR SELECT TO public USING (bucket_id = 'previews' AND (select auth.uid()::text) = (storage.foldername(name))[1]);
|
||||
|
||||
CREATE POLICY "Give users access to own folder previews_insert" ON storage.objects FOR INSERT TO public WITH CHECK (bucket_id = 'previews' AND (select auth.uid()::text) = (storage.foldername(name))[1]);
|
||||
|
||||
CREATE POLICY "Give users access to own folder previews_update" ON storage.objects FOR UPDATE TO public USING (bucket_id = 'previews' AND (select auth.uid()::text) = (storage.foldername(name))[1]);
|
||||
|
||||
CREATE POLICY "Give users access to own folder previews_delete" ON storage.objects FOR DELETE TO public USING (bucket_id = 'previews' AND (select auth.uid()::text) = (storage.foldername(name))[1]);
|
||||
|
||||
-- Only allow service role to upload temp multiview images (from our functions)
|
||||
CREATE POLICY "Allow service role to upload temp multiview images"
|
||||
ON storage.objects FOR INSERT
|
||||
TO service_role
|
||||
WITH CHECK (bucket_id = 'temp-multiview');
|
||||
|
||||
-- Allow public read access (for Tripo to download)
|
||||
CREATE POLICY "Allow public read access to temp multiview images"
|
||||
ON storage.objects FOR SELECT
|
||||
TO public
|
||||
USING (bucket_id = 'temp-multiview');
|
||||
|
||||
-- Allow service role to delete expired files (for manual cleanup if needed)
|
||||
CREATE POLICY "Allow service role to delete temp multiview images"
|
||||
ON storage.objects FOR DELETE
|
||||
TO service_role
|
||||
USING (bucket_id = 'temp-multiview');
|
||||
@@ -0,0 +1,54 @@
|
||||
CREATE OR REPLACE FUNCTION "public"."update_conversation_leaf"() RETURNS "trigger"
|
||||
LANGUAGE "plpgsql"
|
||||
AS $$
|
||||
begin
|
||||
update conversations set
|
||||
current_message_leaf_id = new.id,
|
||||
updated_at = now()
|
||||
where id = new.conversation_id;
|
||||
return new;
|
||||
end;
|
||||
$$;
|
||||
|
||||
CREATE OR REPLACE TRIGGER "update_leaf_trigger" AFTER INSERT ON "public"."messages" FOR EACH ROW EXECUTE FUNCTION "public"."update_conversation_leaf"();
|
||||
|
||||
-- Previews updated_at trigger
|
||||
-- Function to update the updated_at column
|
||||
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = now();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ language 'plpgsql';
|
||||
|
||||
-- Trigger to automatically update updated_at for previews
|
||||
CREATE OR REPLACE TRIGGER update_previews_updated_at
|
||||
BEFORE UPDATE ON "public"."previews"
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
-- Profile creation trigger for new users
|
||||
-- Create function to handle new user sign ups
|
||||
CREATE OR REPLACE FUNCTION public.handle_new_user()
|
||||
RETURNS TRIGGER
|
||||
LANGUAGE plpgsql
|
||||
SECURITY DEFINER SET search_path = ''
|
||||
AS $$
|
||||
BEGIN
|
||||
INSERT INTO public.profiles (user_id, full_name)
|
||||
VALUES (
|
||||
NEW.id,
|
||||
COALESCE(
|
||||
NEW.raw_user_meta_data->>'full_name',
|
||||
split_part(NEW.email, '@', 1)
|
||||
)
|
||||
);
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- Create trigger to automatically create profile on user creation
|
||||
CREATE OR REPLACE TRIGGER on_auth_user_created
|
||||
AFTER INSERT ON auth.users
|
||||
FOR EACH ROW EXECUTE PROCEDURE public.handle_new_user();
|
||||
@@ -0,0 +1,34 @@
|
||||
CREATE TYPE "public"."conversation-type" AS ENUM (
|
||||
'parametric',
|
||||
'creative'
|
||||
);
|
||||
|
||||
CREATE TYPE "public"."generation-status" AS ENUM (
|
||||
'pending',
|
||||
'success',
|
||||
'failure'
|
||||
);
|
||||
|
||||
CREATE TYPE "public"."mesh_model_type" AS ENUM (
|
||||
'quality',
|
||||
'fast'
|
||||
);
|
||||
|
||||
CREATE TYPE "public"."mesh_file_type" AS ENUM (
|
||||
'glb',
|
||||
'stl',
|
||||
'obj',
|
||||
'fbx'
|
||||
);
|
||||
|
||||
CREATE TYPE "public"."privacy_type" AS ENUM (
|
||||
'public',
|
||||
'private'
|
||||
);
|
||||
|
||||
CREATE TYPE "public"."prompt_type" AS ENUM (
|
||||
'mesh',
|
||||
'image',
|
||||
'chat'
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user