diff --git a/.gitignore b/.gitignore index 3c3dc2d..743f763 100644 --- a/.gitignore +++ b/.gitignore @@ -70,4 +70,5 @@ e2e/.env e2e/.env.local # Playwright generated files -.auth/ \ No newline at end of file +.auth/ +notes.md \ No newline at end of file diff --git a/nsc-events-nextjs/.env.example b/nsc-events-nextjs/.env.example deleted file mode 100644 index f84d58c..0000000 --- a/nsc-events-nextjs/.env.example +++ /dev/null @@ -1,17 +0,0 @@ -# Backend API URL - -# Local Development: Use http://localhost:3000/api (backend running locally) -# Production: needs update to our deployed backend API URL (e.g., https://api.nsc-events.com) -# -# The Next.js config automatically exposes this as NSC_EVENTS_PUBLIC_API_URL -NEXT_PUBLIC_API_URL=http://localhost:3000/api - -# =========================================== -# Production Configuration Notes: -# =========================================== -# When deploying to production (Vercel, AWS, Azure, etc.): -# 1. We need to Create a .env.production file with our production backend URL -# 2. OR set NEXT_PUBLIC_API_URL in our hosting platform's environment variables -# 3. Example production value: NEXT_PUBLIC_API_URL=https://api.ourdomain.com -# -# The frontend will automatically use this URL for all API calls diff --git a/nsc-events-nextjs/.env.local.example b/nsc-events-nextjs/.env.local.example index f2d9aa0..c5373e2 100644 --- a/nsc-events-nextjs/.env.local.example +++ b/nsc-events-nextjs/.env.local.example @@ -1,3 +1,3 @@ NEXT_PUBLIC_SUPABASE_URL=your_supabase_url -NEXT_PUBLIC_SUPABASE_PUBLISHABLE_DEFAULT_KEY=your_supabase_key \ No newline at end of file +NEXT_PUBLIC_SUPABASE_PUBLISHABLE_DEFAULT_KEY=your_supabase_key_aka_anon_key \ No newline at end of file diff --git a/nsc-events-nextjs/lib/supabaseClient.ts b/nsc-events-nextjs/lib/supabaseClient.ts index 79f2054..af039f3 100644 --- a/nsc-events-nextjs/lib/supabaseClient.ts +++ b/nsc-events-nextjs/lib/supabaseClient.ts @@ -3,8 +3,10 @@ import { createBrowserClient } from "@supabase/ssr"; const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL; const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_DEFAULT_KEY; -export const createClient = () => +export const supabase = () => createBrowserClient( supabaseUrl!, supabaseKey!, - ); \ No newline at end of file + ); + +export default supabase; \ No newline at end of file diff --git a/nsc-events-nextjs/lib/supabaseServer.ts b/nsc-events-nextjs/lib/supabaseServer.ts new file mode 100644 index 0000000..f2df5ea --- /dev/null +++ b/nsc-events-nextjs/lib/supabaseServer.ts @@ -0,0 +1,26 @@ +import { createServerClient } from '@supabase/ssr'; +import { cookies } from 'next/headers'; + +export function createSupabaseServerClient() { + const cookieStore = cookies(); + + return createServerClient( + process.env.NEXT_PUBLIC_SUPABASE_URL!, + process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, + { + cookies: { + getAll() { + return cookieStore.getAll(); + }, + setAll(cookiesToSet) { + try { + cookiesToSet.forEach(({ name, value, options }) => cookieStore.set(name, value, options)); + } catch { + // Ignore in some SSR contexts + } + }, + }, + } + ); +} +export default createSupabaseServerClient; \ No newline at end of file diff --git a/sql/0_create_update_updated_at_function.sql b/sql/0_create_update_updated_at_function.sql new file mode 100644 index 0000000..82f2f94 --- /dev/null +++ b/sql/0_create_update_updated_at_function.sql @@ -0,0 +1,7 @@ +CREATE OR REPLACE FUNCTION public.update_updated_at_column() +RETURNS TRIGGER AS $$ +BEGIN + NEW.updated_at = now(); + RETURN NEW; +END; +$$ LANGUAGE plpgsql; \ No newline at end of file diff --git a/sql/1_create_users_table.sql b/sql/1_create_users_table.sql new file mode 100644 index 0000000..b18c883 --- /dev/null +++ b/sql/1_create_users_table.sql @@ -0,0 +1,43 @@ +-- Migration: create_users_table +-- Creates users table for NSC Events + +CREATE TYPE public.user_role_type AS ENUM ('user', 'creator', 'admin'); + +CREATE TABLE IF NOT EXISTS public.users ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + first_name varchar NOT NULL, + last_name varchar NOT NULL, + pronouns varchar, + user_role user_role_type NOT NULL DEFAULT 'user', + email varchar NOT NULL UNIQUE, + google_credentials jsonb, + reset_password_token varchar UNIQUE, + reset_password_expires timestamptz, + created_at timestamptz DEFAULT now(), + updated_at timestamptz DEFAULT now() +); + +-- Enable Row Level Security (RLS) +ALTER TABLE public.users ENABLE ROW LEVEL SECURITY; + +CREATE POLICY "Admins manage all users" + ON public.users + FOR ALL + USING ((SELECT user_role FROM public.users WHERE id = auth.uid()) = 'admin'); + +-- users can only read/update their own row +CREATE POLICY "Users can view own profile" + ON public.users + FOR SELECT + USING (auth.uid() = id); + +CREATE POLICY "Users can update own profile" + ON public.users + FOR UPDATE + USING (auth.uid() = id) + WITH CHECK (auth.uid() = id); + +CREATE OR REPLACE TRIGGER update_users_updated_at + BEFORE UPDATE ON public.users + FOR EACH ROW + EXECUTE FUNCTION public.update_updated_at_column(); \ No newline at end of file diff --git a/sql/2_create_media_table.sql b/sql/2_create_media_table.sql new file mode 100644 index 0000000..9ad1299 --- /dev/null +++ b/sql/2_create_media_table.sql @@ -0,0 +1,35 @@ +CREATE TABLE IF NOT EXISTS public.media ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + filename varchar NOT NULL, + original_name varchar NOT NULL, + mime_type varchar, + size bigint NOT NULL, + s3_key varchar, + s3_url varchar, + type varchar, + uploaded_by_user_id uuid NOT NULL REFERENCES public.users(id) ON DELETE SET NULL, + created_at timestamptz DEFAULT now(), + updated_at timestamptz DEFAULT now() +); + +-- Enable Row Level Security (RLS) +ALTER TABLE public.media ENABLE ROW LEVEL SECURITY; + +-- Uploader can view/update/delete their own media +CREATE POLICY "Users can manage own media" + ON public.media + FOR ALL + USING (auth.uid() = uploaded_by_user_id) + WITH CHECK (auth.uid() = uploaded_by_user_id); + +-- public can access to media +CREATE POLICY "Public can view media" + ON public.media + FOR SELECT + USING (true); + +-- Update updated at +CREATE OR REPLACE TRIGGER update_media_updated_at + BEFORE UPDATE ON public.media + FOR EACH ROW + EXECUTE FUNCTION public.update_updated_at_column(); \ No newline at end of file diff --git a/sql/3_create_tags_table.sql b/sql/3_create_tags_table.sql new file mode 100644 index 0000000..cc7f03d --- /dev/null +++ b/sql/3_create_tags_table.sql @@ -0,0 +1,21 @@ +CREATE TABLE IF NOT EXISTS public.tags ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + name varchar NOT NULL UNIQUE, + slug varchar NOT NULL UNIQUE, + created_at timestamptz DEFAULT now(), + updated_at timestamptz DEFAULT now() +); + +-- Enable Row Level Security (RLS) +ALTER TABLE public.tags ENABLE ROW LEVEL SECURITY; + +-- everyone can see tags +CREATE POLICY "Public read access to tags" + ON public.tags + FOR SELECT + USING (true); + +CREATE OR REPLACE TRIGGER update_tags_updated_at + BEFORE UPDATE ON public.tags + FOR EACH ROW + EXECUTE FUNCTION public.update_updated_at_column(); \ No newline at end of file diff --git a/sql/4_create_activities_table.sql b/sql/4_create_activities_table.sql new file mode 100644 index 0000000..7e6bd94 --- /dev/null +++ b/sql/4_create_activities_table.sql @@ -0,0 +1,50 @@ +CREATE TABLE IF NOT EXISTS public.activities ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + created_by_user_id uuid NOT NULL REFERENCES public.users(id) ON DELETE SET NULL, + event_title varchar NOT NULL, + event_description text NOT NULL, + start_date timestamptz NOT NULL, + end_date timestamptz NOT NULL, + event_location varchar NOT NULL, + event_host varchar NOT NULL, + event_meeting_url varchar, + event_registration varchar, + event_capacity varchar NOT NULL, + event_schedule varchar, + event_speakers text, + event_prerequisites varchar, + event_cancellation_policy varchar, + event_contact varchar NOT NULL, + event_social_media jsonb, + event_privacy varchar, + event_accessibility varchar, + event_note varchar, + is_hidden boolean DEFAULT false, + is_archived boolean DEFAULT false, + cover_photo_id uuid REFERENCES public.media(id) ON DELETE SET NULL, + document_id uuid REFERENCES public.media(id) ON DELETE SET NULL, + created_at timestamptz DEFAULT now(), + updated_at timestamptz DEFAULT now() +); + +-- Enable Row Level Security (RLS) +ALTER TABLE public.activities ENABLE ROW LEVEL SECURITY; + +-- Creator can view/update/delete their own events +CREATE POLICY "Creators can manage own events" + ON public.activities + FOR ALL + USING (auth.uid() = created_by_user_id) + WITH CHECK (auth.uid() = created_by_user_id); + +-- public have read access to activities +CREATE POLICY "Public can view activity" + ON public.activities + FOR SELECT + USING (true); + +-- Update updated at +CREATE OR REPLACE TRIGGER update_activities_updated_at + BEFORE UPDATE ON public.activities + FOR EACH ROW + EXECUTE FUNCTION public.update_updated_at_column(); \ No newline at end of file diff --git a/sql/5_create_event_registrations_table.sql b/sql/5_create_event_registrations_table.sql new file mode 100644 index 0000000..38ed0e7 --- /dev/null +++ b/sql/5_create_event_registrations_table.sql @@ -0,0 +1,48 @@ +CREATE TABLE IF NOT EXISTS public.event_registrations ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + activity_id uuid NOT NULL REFERENCES public.activities(id) ON DELETE SET NULL, + user_id uuid NOT NULL REFERENCES public.users(id) ON DELETE SET NULL, + college varchar NOT NULL, + year_of_study varchar NOT NULL, + is_attended boolean DEFAULT false, + created_at timestamptz DEFAULT now(), + updated_at timestamptz DEFAULT now() +); + +-- Enable Row Level Security (RLS) +ALTER TABLE public.event_registrations ENABLE ROW LEVEL SECURITY; + +CREATE POLICY "Users view own registrations" + ON public.event_registrations + FOR SELECT + USING (auth.uid() = user_id); + +CREATE POLICY "Users can register themselves" + ON public.event_registrations + FOR INSERT + WITH CHECK (auth.uid() = user_id); + +CREATE POLICY "Users can remove own registrations" + ON public.event_registrations + FOR DELETE + USING (auth.uid() = user_id); + +CREATE POLICY "Users can change own registrations" + ON public.event_registrations + FOR UPDATE + USING (auth.uid() = user_id); + +CREATE POLICY "Creators view registrations for their events" + ON public.event_registrations + FOR SELECT + USING ( + EXISTS ( + SELECT 1 FROM public.activities activity + WHERE activity.id = activity_id + AND activity.created_by_user_id = auth.uid()) + ); +-- Update updated at +CREATE OR REPLACE TRIGGER update_event_registrations_updated_at + BEFORE UPDATE ON public.event_registrations + FOR EACH ROW + EXECUTE FUNCTION public.update_updated_at_column(); \ No newline at end of file diff --git a/sql/6_create_activity_tags_table.sql b/sql/6_create_activity_tags_table.sql new file mode 100644 index 0000000..d1c4b45 --- /dev/null +++ b/sql/6_create_activity_tags_table.sql @@ -0,0 +1,12 @@ +CREATE TABLE IF NOT EXISTS public.activity_tags ( + activity_id uuid NOT NULL REFERENCES public.activities(id) ON DELETE CASCADE, + tag_id uuid NOT NULL REFERENCES public.tags(id) ON DELETE CASCADE, + PRIMARY KEY (activity_id, tag_id) +); + +ALTER TABLE public.activity_tags ENABLE ROW LEVEL SECURITY; + +CREATE POLICY "Public can view activity tags" + ON public.activity_tags + FOR SELECT + USING (true); \ No newline at end of file diff --git a/supabase/.gitignore b/supabase/.gitignore index ad9264f..43080f8 100644 --- a/supabase/.gitignore +++ b/supabase/.gitignore @@ -6,3 +6,6 @@ .env.keys .env.local .env.*.local + +migrations/ +snippets/ \ No newline at end of file diff --git a/supabase/migrations/20260309032010_create_update_updated_at_function.sql b/supabase/migrations/20260309032010_create_update_updated_at_function.sql new file mode 100644 index 0000000..82f2f94 --- /dev/null +++ b/supabase/migrations/20260309032010_create_update_updated_at_function.sql @@ -0,0 +1,7 @@ +CREATE OR REPLACE FUNCTION public.update_updated_at_column() +RETURNS TRIGGER AS $$ +BEGIN + NEW.updated_at = now(); + RETURN NEW; +END; +$$ LANGUAGE plpgsql; \ No newline at end of file diff --git a/supabase/migrations/20260309032220_create_users_table.sql b/supabase/migrations/20260309032220_create_users_table.sql new file mode 100644 index 0000000..b18c883 --- /dev/null +++ b/supabase/migrations/20260309032220_create_users_table.sql @@ -0,0 +1,43 @@ +-- Migration: create_users_table +-- Creates users table for NSC Events + +CREATE TYPE public.user_role_type AS ENUM ('user', 'creator', 'admin'); + +CREATE TABLE IF NOT EXISTS public.users ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + first_name varchar NOT NULL, + last_name varchar NOT NULL, + pronouns varchar, + user_role user_role_type NOT NULL DEFAULT 'user', + email varchar NOT NULL UNIQUE, + google_credentials jsonb, + reset_password_token varchar UNIQUE, + reset_password_expires timestamptz, + created_at timestamptz DEFAULT now(), + updated_at timestamptz DEFAULT now() +); + +-- Enable Row Level Security (RLS) +ALTER TABLE public.users ENABLE ROW LEVEL SECURITY; + +CREATE POLICY "Admins manage all users" + ON public.users + FOR ALL + USING ((SELECT user_role FROM public.users WHERE id = auth.uid()) = 'admin'); + +-- users can only read/update their own row +CREATE POLICY "Users can view own profile" + ON public.users + FOR SELECT + USING (auth.uid() = id); + +CREATE POLICY "Users can update own profile" + ON public.users + FOR UPDATE + USING (auth.uid() = id) + WITH CHECK (auth.uid() = id); + +CREATE OR REPLACE TRIGGER update_users_updated_at + BEFORE UPDATE ON public.users + FOR EACH ROW + EXECUTE FUNCTION public.update_updated_at_column(); \ No newline at end of file diff --git a/supabase/migrations/20260309034258_create_media_table.sql b/supabase/migrations/20260309034258_create_media_table.sql new file mode 100644 index 0000000..9ad1299 --- /dev/null +++ b/supabase/migrations/20260309034258_create_media_table.sql @@ -0,0 +1,35 @@ +CREATE TABLE IF NOT EXISTS public.media ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + filename varchar NOT NULL, + original_name varchar NOT NULL, + mime_type varchar, + size bigint NOT NULL, + s3_key varchar, + s3_url varchar, + type varchar, + uploaded_by_user_id uuid NOT NULL REFERENCES public.users(id) ON DELETE SET NULL, + created_at timestamptz DEFAULT now(), + updated_at timestamptz DEFAULT now() +); + +-- Enable Row Level Security (RLS) +ALTER TABLE public.media ENABLE ROW LEVEL SECURITY; + +-- Uploader can view/update/delete their own media +CREATE POLICY "Users can manage own media" + ON public.media + FOR ALL + USING (auth.uid() = uploaded_by_user_id) + WITH CHECK (auth.uid() = uploaded_by_user_id); + +-- public can access to media +CREATE POLICY "Public can view media" + ON public.media + FOR SELECT + USING (true); + +-- Update updated at +CREATE OR REPLACE TRIGGER update_media_updated_at + BEFORE UPDATE ON public.media + FOR EACH ROW + EXECUTE FUNCTION public.update_updated_at_column(); \ No newline at end of file diff --git a/supabase/migrations/20260310014930_create_tags_table.sql b/supabase/migrations/20260310014930_create_tags_table.sql new file mode 100644 index 0000000..cc7f03d --- /dev/null +++ b/supabase/migrations/20260310014930_create_tags_table.sql @@ -0,0 +1,21 @@ +CREATE TABLE IF NOT EXISTS public.tags ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + name varchar NOT NULL UNIQUE, + slug varchar NOT NULL UNIQUE, + created_at timestamptz DEFAULT now(), + updated_at timestamptz DEFAULT now() +); + +-- Enable Row Level Security (RLS) +ALTER TABLE public.tags ENABLE ROW LEVEL SECURITY; + +-- everyone can see tags +CREATE POLICY "Public read access to tags" + ON public.tags + FOR SELECT + USING (true); + +CREATE OR REPLACE TRIGGER update_tags_updated_at + BEFORE UPDATE ON public.tags + FOR EACH ROW + EXECUTE FUNCTION public.update_updated_at_column(); \ No newline at end of file diff --git a/supabase/migrations/20260310022009_create_activities_table.sql b/supabase/migrations/20260310022009_create_activities_table.sql new file mode 100644 index 0000000..7e6bd94 --- /dev/null +++ b/supabase/migrations/20260310022009_create_activities_table.sql @@ -0,0 +1,50 @@ +CREATE TABLE IF NOT EXISTS public.activities ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + created_by_user_id uuid NOT NULL REFERENCES public.users(id) ON DELETE SET NULL, + event_title varchar NOT NULL, + event_description text NOT NULL, + start_date timestamptz NOT NULL, + end_date timestamptz NOT NULL, + event_location varchar NOT NULL, + event_host varchar NOT NULL, + event_meeting_url varchar, + event_registration varchar, + event_capacity varchar NOT NULL, + event_schedule varchar, + event_speakers text, + event_prerequisites varchar, + event_cancellation_policy varchar, + event_contact varchar NOT NULL, + event_social_media jsonb, + event_privacy varchar, + event_accessibility varchar, + event_note varchar, + is_hidden boolean DEFAULT false, + is_archived boolean DEFAULT false, + cover_photo_id uuid REFERENCES public.media(id) ON DELETE SET NULL, + document_id uuid REFERENCES public.media(id) ON DELETE SET NULL, + created_at timestamptz DEFAULT now(), + updated_at timestamptz DEFAULT now() +); + +-- Enable Row Level Security (RLS) +ALTER TABLE public.activities ENABLE ROW LEVEL SECURITY; + +-- Creator can view/update/delete their own events +CREATE POLICY "Creators can manage own events" + ON public.activities + FOR ALL + USING (auth.uid() = created_by_user_id) + WITH CHECK (auth.uid() = created_by_user_id); + +-- public have read access to activities +CREATE POLICY "Public can view activity" + ON public.activities + FOR SELECT + USING (true); + +-- Update updated at +CREATE OR REPLACE TRIGGER update_activities_updated_at + BEFORE UPDATE ON public.activities + FOR EACH ROW + EXECUTE FUNCTION public.update_updated_at_column(); \ No newline at end of file diff --git a/supabase/migrations/20260310031659_create_event_registrations_table.sql b/supabase/migrations/20260310031659_create_event_registrations_table.sql new file mode 100644 index 0000000..38ed0e7 --- /dev/null +++ b/supabase/migrations/20260310031659_create_event_registrations_table.sql @@ -0,0 +1,48 @@ +CREATE TABLE IF NOT EXISTS public.event_registrations ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + activity_id uuid NOT NULL REFERENCES public.activities(id) ON DELETE SET NULL, + user_id uuid NOT NULL REFERENCES public.users(id) ON DELETE SET NULL, + college varchar NOT NULL, + year_of_study varchar NOT NULL, + is_attended boolean DEFAULT false, + created_at timestamptz DEFAULT now(), + updated_at timestamptz DEFAULT now() +); + +-- Enable Row Level Security (RLS) +ALTER TABLE public.event_registrations ENABLE ROW LEVEL SECURITY; + +CREATE POLICY "Users view own registrations" + ON public.event_registrations + FOR SELECT + USING (auth.uid() = user_id); + +CREATE POLICY "Users can register themselves" + ON public.event_registrations + FOR INSERT + WITH CHECK (auth.uid() = user_id); + +CREATE POLICY "Users can remove own registrations" + ON public.event_registrations + FOR DELETE + USING (auth.uid() = user_id); + +CREATE POLICY "Users can change own registrations" + ON public.event_registrations + FOR UPDATE + USING (auth.uid() = user_id); + +CREATE POLICY "Creators view registrations for their events" + ON public.event_registrations + FOR SELECT + USING ( + EXISTS ( + SELECT 1 FROM public.activities activity + WHERE activity.id = activity_id + AND activity.created_by_user_id = auth.uid()) + ); +-- Update updated at +CREATE OR REPLACE TRIGGER update_event_registrations_updated_at + BEFORE UPDATE ON public.event_registrations + FOR EACH ROW + EXECUTE FUNCTION public.update_updated_at_column(); \ No newline at end of file diff --git a/supabase/migrations/20260310035012_create_activity_tags_table.sql b/supabase/migrations/20260310035012_create_activity_tags_table.sql new file mode 100644 index 0000000..d1c4b45 --- /dev/null +++ b/supabase/migrations/20260310035012_create_activity_tags_table.sql @@ -0,0 +1,12 @@ +CREATE TABLE IF NOT EXISTS public.activity_tags ( + activity_id uuid NOT NULL REFERENCES public.activities(id) ON DELETE CASCADE, + tag_id uuid NOT NULL REFERENCES public.tags(id) ON DELETE CASCADE, + PRIMARY KEY (activity_id, tag_id) +); + +ALTER TABLE public.activity_tags ENABLE ROW LEVEL SECURITY; + +CREATE POLICY "Public can view activity tags" + ON public.activity_tags + FOR SELECT + USING (true); \ No newline at end of file diff --git a/supabase/seed.sql b/supabase/seed.sql new file mode 100644 index 0000000..9f5a139 --- /dev/null +++ b/supabase/seed.sql @@ -0,0 +1,51 @@ +-- ============================================= +-- SEED DATA FOR LOCAL DEVELOPMENT ONLY +-- Creates a real auth user + matching public.users row +-- ============================================= + +-- 1. Create the admin user in Supabase Auth (auth.users) +-- Password = admin123 (CHANGE THIS AFTER FIRST LOGIN!) +INSERT INTO auth.users ( + instance_id, + id, + aud, + role, + email, + encrypted_password, + email_confirmed_at, + created_at, + updated_at +) +VALUES ( + '00000000-0000-0000-0000-000000000000', + gen_random_uuid(), + 'authenticated', + 'authenticated', + 'admin@gmail.com', + crypt('Admin123!', gen_salt('bf')), + now(), + now(), + now() +); + +INSERT INTO public.users ( + id, + first_name, + last_name, + pronouns, + user_role, + email, + created_at, + updated_at +) +SELECT + id, + 'Admin', + 'User', + 'They/Them', + 'admin', + 'admin@gmail.com', + now(), + now() +FROM auth.users +WHERE email = 'admin@gmail.com'; \ No newline at end of file diff --git a/supabase/snippets/Untitled query 790.sql b/supabase/snippets/Untitled query 790.sql new file mode 100644 index 0000000..69e5c6c --- /dev/null +++ b/supabase/snippets/Untitled query 790.sql @@ -0,0 +1 @@ +SELECT * from public.users \ No newline at end of file