diff --git a/README.md b/README.md index af8afa6..cccdd53 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Slack Clone ## Tech Stacks Used + - Vite React(for frontend) - Node.js (for backend) - Supabase (as a backend service) @@ -9,24 +10,31 @@ ### Features Overview #### Authentication + - Users can login using OAuth with Google and GitHub, or using their email and password. - Password reset functionality via email. #### Homepage Features + 1. **Todo List** + - Create, view, and delete personal todo lists. 2. **Google Calendar Integration** + - Authenticate with Google to manage events in the primary Google Calendar linked to the user's account. 3. **Direct Messaging** + - Initiate and manage conversations, share text and images (up to 50MB). 4. **Channel Management** + - Create channels, add/remove admins, assign/delete tasks to channel members and assign task to any member of the channel also, direct message members,channel deletion. - Role-based access (Admin, Member). 5. **Search Functionality** + - Search for channels and users(with whom conversation is started) within the Slack Clone. 6. **Responsive Design** @@ -37,12 +45,15 @@ ### Environment Variables - Create a .env file in the root directory of your project and define the following variables: -``` bash + +```bash VITE_SUPABASE_URL= VITE_SUPABASE_KEY= VITE_Backend_Port= ``` + - Create a .env file in the Back_end directory with following variables: + ```bash Port= EMAIL_USER= @@ -54,26 +65,54 @@ API_KEY= SESSION_SECRET= ``` +# Run Migration to Create Tables + +There is a migration file in this project that you can run to create tables. +Below are the commands you need to run: + +##### 1. Login to Supabase + +```bash +npx supabase login +``` + +This will prompt you to log in through the browser. + +##### 2. Link your project + +```bash +npx supabase link +``` + +This command will ask you to link your project, so make sure a project is already there in supabase + +##### 3. Run the migration + +```bash +npx supabase db push +``` + +This will execute the migration in your newly created project. + ### Database Tables 1. **user_data**: Disable the `RLS` and Enable the `Realtime` - + | Name | Type | Default value | Extra options | - |-------------------|-------------|---------------|---------------| - | `id` | `uuid` | NULL | - `primary` | + | ----------------- | ----------- | ------------- | ------------- | + | `id` | `uuid` | NULL | - `primary` | | `updated_at` | `timestamp` | NULL | `Is Nullable` | | `username` | `text` | NULL | `Is Nullable` | | `avatar_url` | `text` | NULL | `Is Nullable` | | `email` | `text` | NULL | `Is Nullable` | | `phone` | `text` | NULL | `Is Nullable` | | `hashed_password` | `text` | NULL | `Is Nullable` | - + - **Purpose**: Stores user information including username, avatar URL, email, phone number, and hashed password. - - `Foreign keys` - + | schema | auth | - |-------------------|-----------| + | ----------------- | --------- | | table | `users` | | public.user_data | `id` | | auth.users | `id` | @@ -81,8 +120,10 @@ SESSION_SECRET= | Action if removed | `Cascade` | **THROUGH SQL EDITOR** + - The above tables are the manual explanations for creating the `user_data` tables use the below code in the `SQL EDITOR` for Handling the triggers -``` bash + +```bash -- Create the user_data table create table user_data ( id uuid references auth.users on delete cascade not null primary key, @@ -103,8 +144,8 @@ SESSION_SECRET= insert into public.user_data (id, username, avatar_url, email, phone) values ( new.id, - new.raw_user_meta_data->>'username', - new.raw_user_meta_data->>'avatar_url', + new.raw_user_meta_data->>'username', + new.raw_user_meta_data->>'avatar_url', new.email, new.raw_user_meta_data->>'phone' ); @@ -117,101 +158,100 @@ create trigger on_auth_user_created after insert on auth.users for each row execute procedure public.handle_new_user(); - ``` - - -2. **direct_messages**: Disable the `RLS` and Enable the `Realtime` - - | Name | Type | Default Value | Extra options | - |--------------|-------------|---------------------|----------------------------------| - | `id` | `uuid` | `gen_random_uuid()` | `primary` | - | `created_at` | `timestamp` | `now()` | - | - | `dm_chats` | `json` | NULL | `Is Nullable` `define as Array` | - +``` + +2. **direct_messages**: Disable the `RLS` and Enable the `Realtime` + + | Name | Type | Default Value | Extra options | + | ------------ | ----------- | ------------------- | ------------------------------- | + | `id` | `uuid` | `gen_random_uuid()` | `primary` | + | `created_at` | `timestamp` | `now()` | - | + | `dm_chats` | `json` | NULL | `Is Nullable` `define as Array` | + No `foreign keys` needed - + - **Purpose**: Stores contact information related to direct messaging. -3. **chats_dm**: Disable the `RLS` and Enable the `Realtime` - - | Name | Type | Default Value | Extra options | - |--------------|-------------|---------------|----------------------------------| - | `id` | `text` | NULL | `primary` | - | `created_at` | `timestamp` | `now()` | - | - | `messages` | `jsonb` | NULL | `Is Nullable` `define as Array` | +3. **chats_dm**: Disable the `RLS` and Enable the `Realtime` + + | Name | Type | Default Value | Extra options | + | ------------ | ----------- | ------------- | ------------------------------- | + | `id` | `text` | NULL | `primary` | + | `created_at` | `timestamp` | `now()` | - | + | `messages` | `jsonb` | NULL | `Is Nullable` `define as Array` | + + No `foreign keys` needed - No `foreign keys` needed - - **Purpose**: Stores direct messages between users. -4. **channels_messages**: Disable the `RLS` and Enable the `Realtime` - - | Name | Type | Default Value | Extra options | - |-------------------|-------------|---------------------|----------------------------------| - | `channel_id` | `uuid` | `gen_random_uuid()` | `primary` | - | `created_at` | `timestamp` | `now()` | - | - | `messages` | `json` | NULL | `Is Nullable` `define as Array` | - | `channel_name` | `text` | NULL | - | - | `channel_members` | `json` | NULL | `Is Nullable` `define as Array` | +4. **channels_messages**: Disable the `RLS` and Enable the `Realtime` + + | Name | Type | Default Value | Extra options | + | ----------------- | ----------- | ------------------- | ------------------------------- | + | `channel_id` | `uuid` | `gen_random_uuid()` | `primary` | + | `created_at` | `timestamp` | `now()` | - | + | `messages` | `json` | NULL | `Is Nullable` `define as Array` | + | `channel_name` | `text` | NULL | - | + | `channel_members` | `json` | NULL | `Is Nullable` `define as Array` | No `foreign keys` needed - + - **Purpose**: Stores messages and metadata for channels. -5. **channels_list**: Disable the `RLS` and Enable the `Realtime` - - | Name | Type | Default Value | Extra options | - |--------------|-------------|---------------|----------------------------------| - | `id` | `uuid` | `auth.uid()` | `primary` | - | `created_at` | `timestamp` | `now()` | - | - | `messages` | `json` | NULL | `Is Nullable` `define as Array` | +5. **channels_list**: Disable the `RLS` and Enable the `Realtime` + + | Name | Type | Default Value | Extra options | + | ------------ | ----------- | ------------- | ------------------------------- | + | `id` | `uuid` | `auth.uid()` | `primary` | + | `created_at` | `timestamp` | `now()` | - | + | `messages` | `json` | NULL | `Is Nullable` `define as Array` | + + No `foreign keys` needed - No `foreign keys` needed - - **Purpose**: Lists channels that a user is a member of. -6. **Todo_list**: Disable the `RLS` and Enable the `Realtime` - - | Name | Type | Default Value | Extra options | - |--------------|-------------|---------------------|----------------------------------| - | `id` | `uuid` | `gen_random_uuid()` | `primary` | - | `created_at` | `timestamp` | `now()` | - | - | `todo_list` | `json` | NULL | `Is Nullable` `define as Array` | - - No `foreign keys` needed - - - **Purpose**: Stores user-specific todo lists. - -7. **Mails_sent**: Disable the `RLS` and Enable the `Realtime` - - | Name | Type | Default Value | Extra options | - |--------------|-------------|---------------------|---------------| - | `task_id` | `uuid` | `gen_random_uuid()` | `primary` | - | `created_at` | `timestamp` | `now()` | - | - | `last_sent` | `text` | NULL | `Is Nullable` | - | `t_f` | `bool` | NULL | `Is Nullable` | - - No `foreign keys` needed - - - **Purpose**: Tracks emails sent as reminders for tasks. - -8. **Channels_todolist**: Disable the `RLS` and Enable the `Realtime` - - | Name | Type | Default Value | Extra options | - |--------------|-------------|---------------------|----------------------------------| - | `id` | `uuid` | `gen_random_uuid()` | `primary` | - | `created_at` | `timestamp` | `now()` | - | - | `todo_list` | `json` | NULL | `Is Nullable` `Define as Array` | - - No `foreign keys` needed - - - **Purpose**: Stores tasks assigned to everyone in a channel. - +6. **Todo_list**: Disable the `RLS` and Enable the `Realtime` + +| Name | Type | Default Value | Extra options | +| ------------ | ----------- | ------------------- | ------------------------------- | +| `id` | `uuid` | `gen_random_uuid()` | `primary` | +| `created_at` | `timestamp` | `now()` | - | +| `todo_list` | `json` | NULL | `Is Nullable` `define as Array` | + +No `foreign keys` needed + +- **Purpose**: Stores user-specific todo lists. + +7. **Mails_sent**: Disable the `RLS` and Enable the `Realtime` + +| Name | Type | Default Value | Extra options | +| ------------ | ----------- | ------------------- | ------------- | +| `task_id` | `uuid` | `gen_random_uuid()` | `primary` | +| `created_at` | `timestamp` | `now()` | - | +| `last_sent` | `text` | NULL | `Is Nullable` | +| `t_f` | `bool` | NULL | `Is Nullable` | + +No `foreign keys` needed + +- **Purpose**: Tracks emails sent as reminders for tasks. + +8. **Channels_todolist**: Disable the `RLS` and Enable the `Realtime` + + | Name | Type | Default Value | Extra options | + | ------------ | ----------- | ------------------- | ------------------------------- | + | `id` | `uuid` | `gen_random_uuid()` | `primary` | + | `created_at` | `timestamp` | `now()` | - | + | `todo_list` | `json` | NULL | `Is Nullable` `Define as Array` | + +No `foreign keys` needed + +- **Purpose**: Stores tasks assigned to everyone in a channel. + ### Storage bucket + 1. Go to the Storage section and click on `new bucket`. 2. Name the bucket as `photos`, enable the `Public bucket`, and save it. 3. Under the configuration section, Click on `policies`. 4. Other Policies under storage.objects, Create a `New policy` as `For full customisation`. 5. Give the Policy name and `All` for allowed operation, keep the Target roles as default. 6. Provide `true` or `1` for `USING expression` and `With CHECK expression`. - diff --git a/supabase/.gitignore b/supabase/.gitignore new file mode 100644 index 0000000..a3ad880 --- /dev/null +++ b/supabase/.gitignore @@ -0,0 +1,4 @@ +# Supabase +.branches +.temp +.env diff --git a/supabase/config.toml b/supabase/config.toml new file mode 100644 index 0000000..a4438f8 --- /dev/null +++ b/supabase/config.toml @@ -0,0 +1,202 @@ +# A string used to distinguish different Supabase projects on the same host. Defaults to the +# working directory name when running `supabase init`. +project_id = "GSoC_Slack-Clone" + +[api] +enabled = true +# Port to use for the API URL. +port = 54321 +# Schemas to expose in your API. Tables, views and stored procedures in this schema will get API +# endpoints. `public` is always included. +schemas = ["public", "graphql_public"] +# Extra schemas to add to the search_path of every request. `public` is always included. +extra_search_path = ["public", "extensions"] +# The maximum number of rows returns from a view, table, or stored procedure. Limits payload size +# for accidental or malicious requests. +max_rows = 1000 + +[api.tls] +enabled = false + +[db] +# Port to use for the local database URL. +port = 54322 +# Port used by db diff command to initialize the shadow database. +shadow_port = 54320 +# The database major version to use. This has to be the same as your remote database's. Run `SHOW +# server_version;` on the remote database to check. +major_version = 15 + +[db.pooler] +enabled = false +# Port to use for the local connection pooler. +port = 54329 +# Specifies when a server connection can be reused by other clients. +# Configure one of the supported pooler modes: `transaction`, `session`. +pool_mode = "transaction" +# How many server connections to allow per user/database pair. +default_pool_size = 20 +# Maximum number of client connections allowed. +max_client_conn = 100 + +[realtime] +enabled = true +# Bind realtime via either IPv4 or IPv6. (default: IPv4) +# ip_version = "IPv6" +# The maximum length in bytes of HTTP request headers. (default: 4096) +# max_header_length = 4096 + +[studio] +enabled = true +# Port to use for Supabase Studio. +port = 54323 +# External URL of the API server that frontend connects to. +api_url = "http://127.0.0.1" +# OpenAI API Key to use for Supabase AI in the Supabase Studio. +openai_api_key = "env(OPENAI_API_KEY)" + +# Email testing server. Emails sent with the local dev setup are not actually sent - rather, they +# are monitored, and you can view the emails that would have been sent from the web interface. +[inbucket] +enabled = true +# Port to use for the email testing server web interface. +port = 54324 +# Uncomment to expose additional ports for testing user applications that send emails. +# smtp_port = 54325 +# pop3_port = 54326 + +[storage] +enabled = true +# The maximum file size allowed (e.g. "5MB", "500KB"). +file_size_limit = "50MiB" + +[storage.image_transformation] +enabled = true + +# Uncomment to configure local storage buckets +# [storage.buckets.images] +# public = false +# file_size_limit = "50MiB" +# allowed_mime_types = ["image/png", "image/jpeg"] + +[auth] +enabled = true +# The base URL of your website. Used as an allow-list for redirects and for constructing URLs used +# in emails. +site_url = "http://127.0.0.1:3000" +# A list of *exact* URLs that auth providers are permitted to redirect to post authentication. +additional_redirect_urls = ["https://127.0.0.1:3000"] +# How long tokens are valid for, in seconds. Defaults to 3600 (1 hour), maximum 604,800 (1 week). +jwt_expiry = 3600 +# If disabled, the refresh token will never expire. +enable_refresh_token_rotation = true +# Allows refresh tokens to be reused after expiry, up to the specified interval in seconds. +# Requires enable_refresh_token_rotation = true. +refresh_token_reuse_interval = 10 +# Allow/disallow new user signups to your project. +enable_signup = true +# Allow/disallow anonymous sign-ins to your project. +enable_anonymous_sign_ins = false +# Allow/disallow testing manual linking of accounts +enable_manual_linking = false + +[auth.email] +# Allow/disallow new user signups via email to your project. +enable_signup = true +# If enabled, a user will be required to confirm any email change on both the old, and new email +# addresses. If disabled, only the new email is required to confirm. +double_confirm_changes = true +# If enabled, users need to confirm their email address before signing in. +enable_confirmations = false +# Controls the minimum amount of time that must pass before sending another signup confirmation or password reset email. +max_frequency = "1s" + +# Use a production-ready SMTP server +# [auth.email.smtp] +# host = "smtp.sendgrid.net" +# port = 587 +# user = "apikey" +# pass = "env(SENDGRID_API_KEY)" +# admin_email = "admin@email.com" +# sender_name = "Admin" + +# Uncomment to customize email template +# [auth.email.template.invite] +# subject = "You have been invited" +# content_path = "./supabase/templates/invite.html" + +[auth.sms] +# Allow/disallow new user signups via SMS to your project. +enable_signup = true +# If enabled, users need to confirm their phone number before signing in. +enable_confirmations = false +# Template for sending OTP to users +template = "Your code is {{ .Code }} ." +# Controls the minimum amount of time that must pass before sending another sms otp. +max_frequency = "5s" + +# Use pre-defined map of phone number to OTP for testing. +# [auth.sms.test_otp] +# 4152127777 = "123456" + +# Configure logged in session timeouts. +# [auth.sessions] +# Force log out after the specified duration. +# timebox = "24h" +# Force log out if the user has been inactive longer than the specified duration. +# inactivity_timeout = "8h" + +# This hook runs before a token is issued and allows you to add additional claims based on the authentication method used. +# [auth.hook.custom_access_token] +# enabled = true +# uri = "pg-functions:////" + +# Configure one of the supported SMS providers: `twilio`, `twilio_verify`, `messagebird`, `textlocal`, `vonage`. +[auth.sms.twilio] +enabled = false +account_sid = "" +message_service_sid = "" +# DO NOT commit your Twilio auth token to git. Use environment variable substitution instead: +auth_token = "env(SUPABASE_AUTH_SMS_TWILIO_AUTH_TOKEN)" + +# Use an external OAuth provider. The full list of providers are: `apple`, `azure`, `bitbucket`, +# `discord`, `facebook`, `github`, `gitlab`, `google`, `keycloak`, `linkedin_oidc`, `notion`, `twitch`, +# `twitter`, `slack`, `spotify`, `workos`, `zoom`. +[auth.external.apple] +enabled = false +client_id = "" +# DO NOT commit your OAuth provider secret to git. Use environment variable substitution instead: +secret = "env(SUPABASE_AUTH_EXTERNAL_APPLE_SECRET)" +# Overrides the default auth redirectUrl. +redirect_uri = "" +# Overrides the default auth provider URL. Used to support self-hosted gitlab, single-tenant Azure, +# or any other third-party OIDC providers. +url = "" +# If enabled, the nonce check will be skipped. Required for local sign in with Google auth. +skip_nonce_check = false + +[edge_runtime] +enabled = true +# Configure one of the supported request policies: `oneshot`, `per_worker`. +# Use `oneshot` for hot reload, or `per_worker` for load testing. +policy = "oneshot" +inspector_port = 8083 + +[analytics] +enabled = true +port = 54327 +# Configure one of the supported backends: `postgres`, `bigquery`. +backend = "postgres" + +# Experimental features may be deprecated any time +[experimental] +# Configures Postgres storage engine to use OrioleDB (S3) +orioledb_version = "" +# Configures S3 bucket URL, eg. .s3-.amazonaws.com +s3_host = "env(S3_HOST)" +# Configures S3 bucket region, eg. us-east-1 +s3_region = "env(S3_REGION)" +# Configures AWS_ACCESS_KEY_ID for S3 bucket +s3_access_key = "env(S3_ACCESS_KEY)" +# Configures AWS_SECRET_ACCESS_KEY for S3 bucket +s3_secret_key = "env(S3_SECRET_KEY)" diff --git a/supabase/migrations/20241019162345_create_tables.sql b/supabase/migrations/20241019162345_create_tables.sql new file mode 100644 index 0000000..83fa836 --- /dev/null +++ b/supabase/migrations/20241019162345_create_tables.sql @@ -0,0 +1,142 @@ +-- Enable uuid generation extension (if not enabled) +CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; + +-- Table: user_data +CREATE TABLE IF NOT EXISTS user_data ( + id uuid DEFAULT uuid_generate_v4() PRIMARY KEY, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + username text NOT NULL, + avatar_url text, + email text NOT NULL, + phone text, + hashed_password text -- Allow NULL to avoid trigger failure if not set initially +); + +-- Disable RLS and Enable Realtime for user_data +ALTER TABLE user_data ENABLE REPLICA TRIGGER ALL; +ALTER TABLE user_data DISABLE ROW LEVEL SECURITY; + +-- Table: direct_messages +CREATE TABLE IF NOT EXISTS direct_messages ( + id uuid DEFAULT uuid_generate_v4() PRIMARY KEY, + created_at timestamp with time zone DEFAULT now() NOT NULL, + dm_chats jsonb NOT NULL +); + +-- Disable RLS and Enable Realtime for direct_messages +ALTER TABLE direct_messages ENABLE REPLICA TRIGGER ALL; +ALTER TABLE direct_messages DISABLE ROW LEVEL SECURITY; + +-- Table: chats_dm +CREATE TABLE IF NOT EXISTS chats_dm ( + id uuid DEFAULT uuid_generate_v4() PRIMARY KEY, + created_at timestamp with time zone DEFAULT now() NOT NULL, + messages jsonb NOT NULL +); + +-- Disable RLS and Enable Realtime for chats_dm +ALTER TABLE chats_dm ENABLE REPLICA TRIGGER ALL; +ALTER TABLE chats_dm DISABLE ROW LEVEL SECURITY; + +-- Table: channels_messages +CREATE TABLE IF NOT EXISTS channels_messages ( + id uuid DEFAULT uuid_generate_v4() PRIMARY KEY, + created_at timestamp with time zone DEFAULT now() NOT NULL, + messages jsonb NOT NULL, + channel_name text NOT NULL, + channel_members jsonb NOT NULL +); + +-- Disable RLS and Enable Realtime for channels_messages +ALTER TABLE channels_messages ENABLE REPLICA TRIGGER ALL; +ALTER TABLE channels_messages DISABLE ROW LEVEL SECURITY; + +-- Table: channels_list +CREATE TABLE IF NOT EXISTS channels_list ( + id uuid DEFAULT uuid_generate_v4() PRIMARY KEY, + created_at timestamp with time zone DEFAULT now() NOT NULL, + channels jsonb NOT NULL +); + +-- Disable RLS and Enable Realtime for channels_list +ALTER TABLE channels_list ENABLE REPLICA TRIGGER ALL; +ALTER TABLE channels_list DISABLE ROW LEVEL SECURITY; + +-- Table: todo_list +CREATE TABLE IF NOT EXISTS todo_list ( + id uuid DEFAULT uuid_generate_v4() PRIMARY KEY, + created_at timestamp with time zone DEFAULT now() NOT NULL, + todo_list jsonb NOT NULL +); + +-- Disable RLS and Enable Realtime for todo_list +ALTER TABLE todo_list ENABLE REPLICA TRIGGER ALL; +ALTER TABLE todo_list DISABLE ROW LEVEL SECURITY; + +-- Table: mails_sent +CREATE TABLE IF NOT EXISTS mails_sent ( + task_id uuid DEFAULT uuid_generate_v4() PRIMARY KEY, + created_at timestamp with time zone DEFAULT now() NOT NULL, + last_sent text NOT NULL, + t_f boolean NOT NULL +); + +-- Disable RLS and Enable Realtime for mails_sent +ALTER TABLE mails_sent ENABLE REPLICA TRIGGER ALL; +ALTER TABLE mails_sent DISABLE ROW LEVEL SECURITY; + +-- Table: channels_todolist +CREATE TABLE IF NOT EXISTS channels_todolist ( + id uuid DEFAULT uuid_generate_v4() PRIMARY KEY, + created_at timestamp with time zone DEFAULT now() NOT NULL, + todo_list jsonb NOT NULL +); + +-- Disable RLS and Enable Realtime for channels_todolist +ALTER TABLE channels_todolist ENABLE REPLICA TRIGGER ALL; +ALTER TABLE channels_todolist DISABLE ROW LEVEL SECURITY; + +-- Trigger function for handling new user sign-up +CREATE OR REPLACE FUNCTION public.handle_new_user() +RETURNS TRIGGER AS $$ +BEGIN + INSERT INTO public.user_data (id, username, avatar_url, email, phone, hashed_password) + VALUES ( + NEW.id, + NEW.raw_user_meta_data->>'username', + NEW.raw_user_meta_data->>'avatar_url', + NEW.email, + NEW.raw_user_meta_data->>'phone', + 'default_password_hash' -- Placeholder value to avoid NULL issues + ); + RETURN NEW; +END; +$$ LANGUAGE plpgsql SECURITY DEFINER; + +-- Create trigger for new user sign-up +CREATE TRIGGER on_auth_user_created +AFTER INSERT ON auth.users +FOR EACH ROW EXECUTE FUNCTION public.handle_new_user(); + +-- Trigger function to update channel members in channels_list +CREATE OR REPLACE FUNCTION public.handle_channel_member_add() +RETURNS TRIGGER AS $$ +BEGIN + UPDATE channels_list + SET channels = jsonb_set( + channels_list.channels, + '{members}', + coalesce( + channels_list.channels->'members', + '[]'::jsonb + ) || to_jsonb(NEW) + ) + WHERE id = NEW.channel_id; + RETURN NEW; +END; +$$ LANGUAGE plpgsql SECURITY DEFINER; + +-- Create trigger to invoke handle_channel_member_add() on new member insert +CREATE TRIGGER on_channel_member_added +AFTER INSERT ON channels_messages +FOR EACH ROW EXECUTE FUNCTION public.handle_channel_member_add(); diff --git a/supabase/seed.sql b/supabase/seed.sql new file mode 100644 index 0000000..e69de29