From 96f0c5d939d9e8f15b032aebaf517c22be769e88 Mon Sep 17 00:00:00 2001 From: Bryce Date: Sun, 1 Sep 2024 14:42:26 -0400 Subject: [PATCH 1/4] create primary migrations. add seed data --- ...40901155537_create_auth_users_triggers.sql | 23 ++++ .../20240901155538_create_users_table.sql | 46 +++++++ .../20240901165124_create_posts_table.sql | 60 +++++++++ apps/api/supabase/seed.sql | 122 ++++++++++++++++++ 4 files changed, 251 insertions(+) create mode 100644 apps/api/supabase/migrations/20240901155537_create_auth_users_triggers.sql create mode 100644 apps/api/supabase/migrations/20240901155538_create_users_table.sql create mode 100644 apps/api/supabase/migrations/20240901165124_create_posts_table.sql create mode 100644 apps/api/supabase/seed.sql diff --git a/apps/api/supabase/migrations/20240901155537_create_auth_users_triggers.sql b/apps/api/supabase/migrations/20240901155537_create_auth_users_triggers.sql new file mode 100644 index 00000000..12162c09 --- /dev/null +++ b/apps/api/supabase/migrations/20240901155537_create_auth_users_triggers.sql @@ -0,0 +1,23 @@ +create function public .handle_new_user() returns trigger language plpgsql security definer +set + search_path = '' as $$ begin + insert into + public .users (id, email, full_name, username) + values + ( + new .id, + new .email, + new .raw_user_meta_data ->> 'full_name', + new .raw_user_meta_data ->> 'username' + ); + +return new; + +end; + +$$; + +-- trigger the function every time a user is created +create trigger on_auth_user_created after +insert + on auth.users for each row execute function public .handle_new_user(); \ No newline at end of file diff --git a/apps/api/supabase/migrations/20240901155538_create_users_table.sql b/apps/api/supabase/migrations/20240901155538_create_users_table.sql new file mode 100644 index 00000000..c8d1fc96 --- /dev/null +++ b/apps/api/supabase/migrations/20240901155538_create_users_table.sql @@ -0,0 +1,46 @@ +-- create users table +create table public .users ( + id uuid primary key, + username text unique not null, + email text unique not null, + full_name text, + avatar_url text, + bio text, + created_at timestamp with time zone default now(), + updated_at timestamp with time zone default now(), + constraint fk_auth_user foreign key (id) references auth.users(id) on + delete + cascade +); + +-- create index on username for faster lookups +create index idx_users_username on public .users (username); + +-- enable row level security (rls) +alter table + public .users enable row level security; + +-- create a trigger to update the updated_at column +create +or replace function update_updated_at() returns trigger as $$ begin + new .updated_at = now(); + +return new; + +end; + +$$ language plpgsql; + +create trigger users_updated_at before +update + on public .users for each row execute function update_updated_at(); + +-- create a policy to allow users to read all profiles +create policy read_all_profiles on public .users for +select + using (true); + +-- create a policy to allow users to update their own profile +create policy update_own_profile on public .users for +update + using (auth.uid() = id); \ No newline at end of file diff --git a/apps/api/supabase/migrations/20240901165124_create_posts_table.sql b/apps/api/supabase/migrations/20240901165124_create_posts_table.sql new file mode 100644 index 00000000..2c8254ea --- /dev/null +++ b/apps/api/supabase/migrations/20240901165124_create_posts_table.sql @@ -0,0 +1,60 @@ +-- create posts table +create table posts ( + id uuid primary key default gen_random_uuid(), + user_id uuid not null, + title text not null, + content text not null, + created_at timestamptz not null default now(), + updated_at timestamptz not null default now() +); + +-- add foreign key constraint +alter table + posts +add + constraint fk_posts_user foreign key (user_id) references public .users(id) on +delete + cascade; + +-- create index for faster queries +create index idx_posts_user_id on posts(user_id); + +-- add rls policies +alter table + posts enable row level security; + +-- policy to allow anyone to read all posts +create policy "allow read access for all users" on posts for +select + using (true); + +-- policy to allow users to insert their own posts +create policy "allow insert for authenticated users" on posts for +insert + with check (auth.uid() = user_id); + +-- policy to allow users to update their own posts +create policy "allow update for post owners" on posts for +update + using (auth.uid() = user_id); + +-- policy to allow users to delete their own posts +create policy "allow delete for post owners" on posts for +delete + using (auth.uid() = user_id); + +-- function to update the updated_at timestamp +create +or replace function update_updated_at() returns trigger as $$ begin + new .updated_at = now(); + +return new; + +end; + +$$ language plpgsql; + +-- trigger to call the update_updated_at function +create trigger update_posts_updated_at before +update + on posts for each row execute function update_updated_at(); \ No newline at end of file diff --git a/apps/api/supabase/seed.sql b/apps/api/supabase/seed.sql new file mode 100644 index 00000000..65559367 --- /dev/null +++ b/apps/api/supabase/seed.sql @@ -0,0 +1,122 @@ +INSERT INTO + auth.users ( + instance_id, + id, + aud, + role, + email, + encrypted_password, + email_confirmed_at, + invited_at, + confirmation_token, + confirmation_sent_at, + recovery_token, + recovery_sent_at, + email_change_token_new, + email_change, + email_change_sent_at, + last_sign_in_at, + raw_app_meta_data, + raw_user_meta_data, + is_super_admin, + created_at, + updated_at, + phone, + phone_confirmed_at, + phone_change, + phone_change_token, + phone_change_sent_at, + email_change_token_current, + email_change_confirm_status, + banned_until, + reauthentication_token, + reauthentication_sent_at, + is_sso_user, + deleted_at, + is_anonymous + ) +VALUES + ( + '00000000-0000-0000-0000-000000000000', + 'aec53558-767e-4408-b4d6-1c1e6f17ffe5', + 'authenticated', + 'authenticated', + 'user@example.com', + '$2a$10$nnqTShcTX48N6QWWjbPUee.wrGz1kGx/uq5lORviCm.fn04W1BeRe', + '2024-09-01 17:21:01.462788+00', + NULL, + '', + NULL, + '', + NULL, + '', + '', + NULL, + NULL, + '{"provider": "email", "providers": ["email"]}', + '{"username": "username", "full_name": "Test User"}', + NULL, + '2024-09-01 17:21:01.455486+00', + '2024-09-01 17:21:01.46295+00', + NULL, + NULL, + '', + '', + NULL, + '', + 0, + NULL, + '', + NULL, + false, + NULL, + false + ); + +INSERT INTO + auth.identities ( + provider_id, + user_id, + identity_data, + provider, + last_sign_in_at, + created_at, + updated_at, + id + ) +VALUES + ( + 'aec53558-767e-4408-b4d6-1c1e6f17ffe5', + 'aec53558-767e-4408-b4d6-1c1e6f17ffe5', + '{"sub": "aec53558-767e-4408-b4d6-1c1e6f17ffe5", "email": "user@example.com", "email_verified": false, "phone_verified": false}', + 'email', + '2024-09-01 17:21:01.459821+00', + '2024-09-01 17:21:01.459849+00', + '2024-09-01 17:21:01.459849+00', + 'c5e81668-437b-47c2-83e2-84b8566b3018' + ); + +-- Seed data for posts +INSERT INTO + posts (user_id, title, content) +VALUES + ( + 'aec53558-767e-4408-b4d6-1c1e6f17ffe5', + 'React Server Components: A Game Changer', + 'React Server Components are revolutionizing how we build React applications. They allow for better performance and smaller bundle sizes by running components on the server. This new paradigm is especially powerful when combined with frameworks like Next.js 13+.' + ), + ( + 'aec53558-767e-4408-b4d6-1c1e6f17ffe5', + 'The Rise of Bun: A New JavaScript Runtime', + 'Bun is gaining traction as a fast all-in-one JavaScript runtime. It aims to replace Node.js, npm, yarn, and more. With its focus on performance and developer experience, Bun is definitely worth keeping an eye on in 2024.' + ), + ( + 'aec53558-767e-4408-b4d6-1c1e6f17ffe5', + 'TypeScript 5.0: What''s New and Exciting', + 'TypeScript 5.0 brings several new features and improvements, including decorators, const type parameters, and more. These enhancements continue to make TypeScript an essential tool for building robust JavaScript applications.' + ), + ( + 'aec53558-767e-4408-b4d6-1c1e6f17ffe5', + 'The State of JavaScript Frameworks in 2024', + 'While React remains dominant, frameworks like Svelte and Solid are gaining popularity for their performance and simplicity. Meanwhile, meta-frameworks like Next.js and Remix are becoming increasingly important in the React ecosystem.' + ); \ No newline at end of file From 430f0cef74159cb5087977f2626994d3baf2a7c3 Mon Sep 17 00:00:00 2001 From: Bryce Date: Sun, 1 Sep 2024 14:43:49 -0400 Subject: [PATCH 2/4] reflect schema name field, inferred from zod update schema --- apps/app/src/components/users.server.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/app/src/components/users.server.tsx b/apps/app/src/components/users.server.tsx index 75c005e8..b1df108d 100644 --- a/apps/app/src/components/users.server.tsx +++ b/apps/app/src/components/users.server.tsx @@ -6,7 +6,7 @@ export async function UsersServer() { return (
{data?.map((user) => ( -
{user.name}
+
{user.full_name}
))}
); From bdddde02cc5482ebf558a07f82d0a4a9e2551cf7 Mon Sep 17 00:00:00 2001 From: Bryce Date: Sun, 1 Sep 2024 14:44:15 -0400 Subject: [PATCH 3/4] sync types --- packages/supabase/src/types/db.ts | 498 +++++++++++++++++++++++++++++- 1 file changed, 497 insertions(+), 1 deletion(-) diff --git a/packages/supabase/src/types/db.ts b/packages/supabase/src/types/db.ts index 2066a59b..de2b38ef 100644 --- a/packages/supabase/src/types/db.ts +++ b/packages/supabase/src/types/db.ts @@ -1,3 +1,499 @@ +export type Json = + | string + | number + | boolean + | null + | { [key: string]: Json | undefined } + | Json[]; + export type Database = { - public: unknown; + public: { + Tables: { + posts: { + Row: { + content: string; + created_at: string; + id: string; + title: string; + updated_at: string; + user_id: string; + }; + Insert: { + content: string; + created_at?: string; + id?: string; + title: string; + updated_at?: string; + user_id: string; + }; + Update: { + content?: string; + created_at?: string; + id?: string; + title?: string; + updated_at?: string; + user_id?: string; + }; + Relationships: [ + { + foreignKeyName: "fk_posts_user"; + columns: ["user_id"]; + isOneToOne: false; + referencedRelation: "users"; + referencedColumns: ["id"]; + }, + ]; + }; + users: { + Row: { + avatar_url: string | null; + bio: string | null; + created_at: string | null; + email: string; + full_name: string | null; + id: string; + updated_at: string | null; + username: string; + }; + Insert: { + avatar_url?: string | null; + bio?: string | null; + created_at?: string | null; + email: string; + full_name?: string | null; + id: string; + updated_at?: string | null; + username: string; + }; + Update: { + avatar_url?: string | null; + bio?: string | null; + created_at?: string | null; + email?: string; + full_name?: string | null; + id?: string; + updated_at?: string | null; + username?: string; + }; + Relationships: [ + { + foreignKeyName: "fk_auth_user"; + columns: ["id"]; + isOneToOne: true; + referencedRelation: "users"; + referencedColumns: ["id"]; + }, + ]; + }; + }; + Views: { + [_ in never]: never; + }; + Functions: { + [_ in never]: never; + }; + Enums: { + [_ in never]: never; + }; + CompositeTypes: { + [_ in never]: never; + }; + }; + storage: { + Tables: { + buckets: { + Row: { + allowed_mime_types: string[] | null; + avif_autodetection: boolean | null; + created_at: string | null; + file_size_limit: number | null; + id: string; + name: string; + owner: string | null; + owner_id: string | null; + public: boolean | null; + updated_at: string | null; + }; + Insert: { + allowed_mime_types?: string[] | null; + avif_autodetection?: boolean | null; + created_at?: string | null; + file_size_limit?: number | null; + id: string; + name: string; + owner?: string | null; + owner_id?: string | null; + public?: boolean | null; + updated_at?: string | null; + }; + Update: { + allowed_mime_types?: string[] | null; + avif_autodetection?: boolean | null; + created_at?: string | null; + file_size_limit?: number | null; + id?: string; + name?: string; + owner?: string | null; + owner_id?: string | null; + public?: boolean | null; + updated_at?: string | null; + }; + Relationships: []; + }; + migrations: { + Row: { + executed_at: string | null; + hash: string; + id: number; + name: string; + }; + Insert: { + executed_at?: string | null; + hash: string; + id: number; + name: string; + }; + Update: { + executed_at?: string | null; + hash?: string; + id?: number; + name?: string; + }; + Relationships: []; + }; + objects: { + Row: { + bucket_id: string | null; + created_at: string | null; + id: string; + last_accessed_at: string | null; + metadata: Json | null; + name: string | null; + owner: string | null; + owner_id: string | null; + path_tokens: string[] | null; + updated_at: string | null; + user_metadata: Json | null; + version: string | null; + }; + Insert: { + bucket_id?: string | null; + created_at?: string | null; + id?: string; + last_accessed_at?: string | null; + metadata?: Json | null; + name?: string | null; + owner?: string | null; + owner_id?: string | null; + path_tokens?: string[] | null; + updated_at?: string | null; + user_metadata?: Json | null; + version?: string | null; + }; + Update: { + bucket_id?: string | null; + created_at?: string | null; + id?: string; + last_accessed_at?: string | null; + metadata?: Json | null; + name?: string | null; + owner?: string | null; + owner_id?: string | null; + path_tokens?: string[] | null; + updated_at?: string | null; + user_metadata?: Json | null; + version?: string | null; + }; + Relationships: [ + { + foreignKeyName: "objects_bucketId_fkey"; + columns: ["bucket_id"]; + isOneToOne: false; + referencedRelation: "buckets"; + referencedColumns: ["id"]; + }, + ]; + }; + s3_multipart_uploads: { + Row: { + bucket_id: string; + created_at: string; + id: string; + in_progress_size: number; + key: string; + owner_id: string | null; + upload_signature: string; + user_metadata: Json | null; + version: string; + }; + Insert: { + bucket_id: string; + created_at?: string; + id: string; + in_progress_size?: number; + key: string; + owner_id?: string | null; + upload_signature: string; + user_metadata?: Json | null; + version: string; + }; + Update: { + bucket_id?: string; + created_at?: string; + id?: string; + in_progress_size?: number; + key?: string; + owner_id?: string | null; + upload_signature?: string; + user_metadata?: Json | null; + version?: string; + }; + Relationships: [ + { + foreignKeyName: "s3_multipart_uploads_bucket_id_fkey"; + columns: ["bucket_id"]; + isOneToOne: false; + referencedRelation: "buckets"; + referencedColumns: ["id"]; + }, + ]; + }; + s3_multipart_uploads_parts: { + Row: { + bucket_id: string; + created_at: string; + etag: string; + id: string; + key: string; + owner_id: string | null; + part_number: number; + size: number; + upload_id: string; + version: string; + }; + Insert: { + bucket_id: string; + created_at?: string; + etag: string; + id?: string; + key: string; + owner_id?: string | null; + part_number: number; + size?: number; + upload_id: string; + version: string; + }; + Update: { + bucket_id?: string; + created_at?: string; + etag?: string; + id?: string; + key?: string; + owner_id?: string | null; + part_number?: number; + size?: number; + upload_id?: string; + version?: string; + }; + Relationships: [ + { + foreignKeyName: "s3_multipart_uploads_parts_bucket_id_fkey"; + columns: ["bucket_id"]; + isOneToOne: false; + referencedRelation: "buckets"; + referencedColumns: ["id"]; + }, + { + foreignKeyName: "s3_multipart_uploads_parts_upload_id_fkey"; + columns: ["upload_id"]; + isOneToOne: false; + referencedRelation: "s3_multipart_uploads"; + referencedColumns: ["id"]; + }, + ]; + }; + }; + Views: { + [_ in never]: never; + }; + Functions: { + can_insert_object: { + Args: { + bucketid: string; + name: string; + owner: string; + metadata: Json; + }; + Returns: undefined; + }; + extension: { + Args: { + name: string; + }; + Returns: string; + }; + filename: { + Args: { + name: string; + }; + Returns: string; + }; + foldername: { + Args: { + name: string; + }; + Returns: string[]; + }; + get_size_by_bucket: { + Args: Record; + Returns: { + size: number; + bucket_id: string; + }[]; + }; + list_multipart_uploads_with_delimiter: { + Args: { + bucket_id: string; + prefix_param: string; + delimiter_param: string; + max_keys?: number; + next_key_token?: string; + next_upload_token?: string; + }; + Returns: { + key: string; + id: string; + created_at: string; + }[]; + }; + list_objects_with_delimiter: { + Args: { + bucket_id: string; + prefix_param: string; + delimiter_param: string; + max_keys?: number; + start_after?: string; + next_token?: string; + }; + Returns: { + name: string; + id: string; + metadata: Json; + updated_at: string; + }[]; + }; + operation: { + Args: Record; + Returns: string; + }; + search: { + Args: { + prefix: string; + bucketname: string; + limits?: number; + levels?: number; + offsets?: number; + search?: string; + sortcolumn?: string; + sortorder?: string; + }; + Returns: { + name: string; + id: string; + updated_at: string; + created_at: string; + last_accessed_at: string; + metadata: Json; + }[]; + }; + }; + Enums: { + [_ in never]: never; + }; + CompositeTypes: { + [_ in never]: never; + }; + }; }; + +type PublicSchema = Database[Extract]; + +export type Tables< + PublicTableNameOrOptions extends + | keyof (PublicSchema["Tables"] & PublicSchema["Views"]) + | { schema: keyof Database }, + TableName extends PublicTableNameOrOptions extends { schema: keyof Database } + ? keyof (Database[PublicTableNameOrOptions["schema"]]["Tables"] & + Database[PublicTableNameOrOptions["schema"]]["Views"]) + : never = never, +> = PublicTableNameOrOptions extends { schema: keyof Database } + ? (Database[PublicTableNameOrOptions["schema"]]["Tables"] & + Database[PublicTableNameOrOptions["schema"]]["Views"])[TableName] extends { + Row: infer R; + } + ? R + : never + : PublicTableNameOrOptions extends keyof (PublicSchema["Tables"] & + PublicSchema["Views"]) + ? (PublicSchema["Tables"] & + PublicSchema["Views"])[PublicTableNameOrOptions] extends { + Row: infer R; + } + ? R + : never + : never; + +export type TablesInsert< + PublicTableNameOrOptions extends + | keyof PublicSchema["Tables"] + | { schema: keyof Database }, + TableName extends PublicTableNameOrOptions extends { schema: keyof Database } + ? keyof Database[PublicTableNameOrOptions["schema"]]["Tables"] + : never = never, +> = PublicTableNameOrOptions extends { schema: keyof Database } + ? Database[PublicTableNameOrOptions["schema"]]["Tables"][TableName] extends { + Insert: infer I; + } + ? I + : never + : PublicTableNameOrOptions extends keyof PublicSchema["Tables"] + ? PublicSchema["Tables"][PublicTableNameOrOptions] extends { + Insert: infer I; + } + ? I + : never + : never; + +export type TablesUpdate< + PublicTableNameOrOptions extends + | keyof PublicSchema["Tables"] + | { schema: keyof Database }, + TableName extends PublicTableNameOrOptions extends { schema: keyof Database } + ? keyof Database[PublicTableNameOrOptions["schema"]]["Tables"] + : never = never, +> = PublicTableNameOrOptions extends { schema: keyof Database } + ? Database[PublicTableNameOrOptions["schema"]]["Tables"][TableName] extends { + Update: infer U; + } + ? U + : never + : PublicTableNameOrOptions extends keyof PublicSchema["Tables"] + ? PublicSchema["Tables"][PublicTableNameOrOptions] extends { + Update: infer U; + } + ? U + : never + : never; + +export type Enums< + PublicEnumNameOrOptions extends + | keyof PublicSchema["Enums"] + | { schema: keyof Database }, + EnumName extends PublicEnumNameOrOptions extends { schema: keyof Database } + ? keyof Database[PublicEnumNameOrOptions["schema"]]["Enums"] + : never = never, +> = PublicEnumNameOrOptions extends { schema: keyof Database } + ? Database[PublicEnumNameOrOptions["schema"]]["Enums"][EnumName] + : PublicEnumNameOrOptions extends keyof PublicSchema["Enums"] + ? PublicSchema["Enums"][PublicEnumNameOrOptions] + : never; From 01ba492cf27b8de6b2725202ffce33291b264308 Mon Sep 17 00:00:00 2001 From: Bryce Date: Sun, 1 Sep 2024 14:44:29 -0400 Subject: [PATCH 4/4] add getPosts query --- packages/supabase/src/queries/index.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/supabase/src/queries/index.ts b/packages/supabase/src/queries/index.ts index 2cfcc96d..162753b9 100644 --- a/packages/supabase/src/queries/index.ts +++ b/packages/supabase/src/queries/index.ts @@ -28,3 +28,16 @@ export async function getUsers() { throw error; } } + +export async function getPosts() { + const supabase = createClient(); + + try { + const result = await supabase.from("posts").select("*"); + + return result; + } catch (error) { + logger.error(error); + throw error; + } +}