feat(db): resolve migration conflict and setup supabase client

This commit is contained in:
Gabriel Ramos 2026-06-09 10:03:34 -04:00
parent 9d32f1d605
commit f663d04e10
4 changed files with 119 additions and 85 deletions

24
lib/supabase.ts Normal file
View file

@ -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);
};

94
package-lock.json generated
View file

@ -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",

View file

@ -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",

View file

@ -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;
$$;