diff --git a/lib/supabase.ts b/lib/supabase.ts new file mode 100644 index 0000000..bd511c4 --- /dev/null +++ b/lib/supabase.ts @@ -0,0 +1,24 @@ +import { createClient } from "@supabase/supabase-js"; + +const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL; +const supabasePublishableKey = process.env.SUPABASE_PUBLISHABLE_KEY; + +if (!supabaseUrl) { + throw new Error("Missing env.NEXT_PUBLIC_SUPABASE_URL"); +} + +if (!supabasePublishableKey) { + throw new Error("Missing env.SUPABASE_PUBLISHABLE_KEY"); +} + +// Client-side/public Supabase client +export const supabase = createClient(supabaseUrl, supabasePublishableKey); + +// Server-side admin/secret Supabase client +export const createServerSupabaseClient = () => { + const secretKey = process.env.SUPABASE_SECRET_KEY; + if (!secretKey) { + throw new Error("Missing env.SUPABASE_SECRET_KEY"); + } + return createClient(supabaseUrl, secretKey); +}; diff --git a/package-lock.json b/package-lock.json index 1f6d5e3..b8b5ab1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "temp-next-app", "version": "0.1.0", "dependencies": { + "@supabase/supabase-js": "^2.108.0", "next": "16.2.7", "pdf-parse": "^2.4.5", "react": "19.2.4", @@ -1439,6 +1440,90 @@ "dev": true, "license": "MIT" }, + "node_modules/@supabase/auth-js": { + "version": "2.108.0", + "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.108.0.tgz", + "integrity": "sha512-0CzVGVqHfNOhRQVEcAmu58Mex2Ce0zL3aGfyV+iFQjTK6OntLK/hLCLr/VDRX0E8/2CdsiY99L7fZ/8ys/op4w==", + "license": "MIT", + "dependencies": { + "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@supabase/functions-js": { + "version": "2.108.0", + "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.108.0.tgz", + "integrity": "sha512-lqEGDzT7QBUuKYzi5lHpV/XecXT9wikzcbXMbFo6krNpSDynD1sHM8wcsfB/BAqa4NkFuy3vF4JCV8MeakV8IQ==", + "license": "MIT", + "dependencies": { + "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@supabase/phoenix": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@supabase/phoenix/-/phoenix-0.4.2.tgz", + "integrity": "sha512-YSAGnmDAfuleFCVt3CeurQZAhxRfXWeZIIkwp7NhYzQ1UwW6ePSnzsFAiUm/mbCkfoCf70QQHKW/K6RKh52a4A==", + "license": "MIT" + }, + "node_modules/@supabase/postgrest-js": { + "version": "2.108.0", + "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-2.108.0.tgz", + "integrity": "sha512-8AwTkPqowDYv/qh016CyXeZ3Ukpw6NHyfqc7DWV4afLR2hAiapf3zRKV2ZLG+//T1LK84HrR6X8VBwfgHWmNyw==", + "license": "MIT", + "dependencies": { + "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@supabase/realtime-js": { + "version": "2.108.0", + "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.108.0.tgz", + "integrity": "sha512-N3xR0u7TNr+c5wuLSU60rcfu/H/8N0WBs7iHWwjI/NxKwY3XWSyLUbpbpU8bzmL0dA/Gk9Mupri8mxKUXBW+iw==", + "license": "MIT", + "dependencies": { + "@supabase/phoenix": "^0.4.2", + "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@supabase/storage-js": { + "version": "2.108.0", + "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.108.0.tgz", + "integrity": "sha512-zMYQmh87CId7d8i/1FIfv4fMDcXPutmIJSpoY58GLXi7M266MNzxWGNabDk4i555Oj1Nqtsu2i3Qo3rpWUXO6A==", + "license": "MIT", + "dependencies": { + "iceberg-js": "^0.8.1", + "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@supabase/supabase-js": { + "version": "2.108.0", + "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.108.0.tgz", + "integrity": "sha512-AjPoimM9MZLZbddnlDBGmpZ/Tas1dNcJvuZy/VD1AfmrjBC8J2RSw6UOqR4ISLLlEioOLca/5t1crFnAxa0wRQ==", + "license": "MIT", + "dependencies": { + "@supabase/auth-js": "2.108.0", + "@supabase/functions-js": "2.108.0", + "@supabase/postgrest-js": "2.108.0", + "@supabase/realtime-js": "2.108.0", + "@supabase/storage-js": "2.108.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/@swc/helpers": { "version": "0.5.15", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", @@ -4172,6 +4257,15 @@ "hermes-estree": "0.25.1" } }, + "node_modules/iceberg-js": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/iceberg-js/-/iceberg-js-0.8.1.tgz", + "integrity": "sha512-1dhVQZXhcHje7798IVM+xoo/1ZdVfzOMIc8/rgVSijRK38EDqOJoGula9N/8ZI5RD8QTxNQtK/Gozpr+qUqRRA==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", diff --git a/package.json b/package.json index 5537b1a..011384a 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "lint": "eslint" }, "dependencies": { + "@supabase/supabase-js": "^2.108.0", "next": "16.2.7", "pdf-parse": "^2.4.5", "react": "19.2.4", diff --git a/supabase/migrations/00001_initial_schema.sql b/supabase/migrations/00001_initial_schema.sql deleted file mode 100644 index f8b0006..0000000 --- a/supabase/migrations/00001_initial_schema.sql +++ /dev/null @@ -1,85 +0,0 @@ --- Enable the pgvector extension to work with embeddings -CREATE EXTENSION IF NOT EXISTS vector; - --- Users Table (Recruiters / Managers) -CREATE TABLE IF NOT EXISTS users ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - email TEXT UNIQUE NOT NULL, - role TEXT NOT NULL CHECK (role IN ('recruiter', 'manager')), - created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL -); - --- Jobs Table -CREATE TABLE IF NOT EXISTS jobs ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - title TEXT NOT NULL, - description TEXT NOT NULL, - requirements TEXT, - embedding vector(1536), - created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL -); - --- Candidates Table -CREATE TABLE IF NOT EXISTS candidates ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - name TEXT NOT NULL, - email TEXT UNIQUE NOT NULL, - phone TEXT, - resume_url TEXT, - summary TEXT, - seniority TEXT, - embedding vector(1536), - created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL -); - --- Interviews Table -CREATE TABLE IF NOT EXISTS interviews ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - candidate_id UUID REFERENCES candidates(id) ON DELETE CASCADE NOT NULL, - job_id UUID REFERENCES jobs(id) ON DELETE CASCADE NOT NULL, - scheduled_at TIMESTAMP WITH TIME ZONE NOT NULL, - status TEXT NOT NULL CHECK (status IN ('scheduled', 'completed', 'cancelled')) DEFAULT 'scheduled', - notes TEXT, - created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL -); - --- Scores Table -CREATE TABLE IF NOT EXISTS scores ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - candidate_id UUID REFERENCES candidates(id) ON DELETE CASCADE NOT NULL, - job_id UUID REFERENCES jobs(id) ON DELETE CASCADE NOT NULL, - interviewer_id UUID REFERENCES users(id) ON DELETE CASCADE, - score NUMERIC NOT NULL CHECK (score >= 0 AND score <= 100), - evaluation JSONB, -- MUST contain exactly: summary, classification, suggestions, riskLevel - created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL -); - --- RPC Function to Match Candidates using Cosine Distance -CREATE OR REPLACE FUNCTION match_candidates ( - query_embedding vector(1536), - match_threshold FLOAT, - match_count INT -) -RETURNS TABLE ( - id UUID, - name TEXT, - email TEXT, - seniority TEXT, - similarity FLOAT -) -LANGUAGE plpgsql -AS $$ -BEGIN - RETURN QUERY - SELECT - candidates.id, - candidates.name, - candidates.email, - candidates.seniority, - (1 - (candidates.embedding <=> query_embedding))::FLOAT AS similarity - FROM candidates - WHERE (1 - (candidates.embedding <=> query_embedding)) > match_threshold - ORDER BY candidates.embedding <=> query_embedding ASC - LIMIT match_count; -END; -$$;