diff --git a/.env.example b/.env.example
index 2f3a864..bd7a4d7 100644
--- a/.env.example
+++ b/.env.example
@@ -103,7 +103,12 @@ NUXT_PUBLIC_SITE_URL=http://localhost:3000
# When configured, "Continue with " buttons appear on the auth pages.
# Google — Create credentials at https://console.cloud.google.com/apis/credentials
-# Redirect URI: https://yourdomain.com/api/auth/callback/google
+# Redirect URIs (add BOTH):
+# https://yourdomain.com/api/auth/callback/google
+# https://yourdomain.com/api/portal/auth/google/callback
+# For local dev, also add:
+# http://localhost:3000/api/auth/callback/google
+# http://localhost:3000/api/portal/auth/google/callback
# AUTH_GOOGLE_CLIENT_ID=your-google-client-id.apps.googleusercontent.com
# AUTH_GOOGLE_CLIENT_SECRET=GOCSPX-your-google-client-secret
diff --git a/app/components/portal/InterviewCard.vue b/app/components/portal/InterviewCard.vue
new file mode 100644
index 0000000..7d19846
--- /dev/null
+++ b/app/components/portal/InterviewCard.vue
@@ -0,0 +1,95 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ interview.title }}
+
+
+ {{ interview.type }}
+
+
+
+
+
+
+
+ {{ formatDateTime(interview.scheduledAt, interview.timezone) }}
+
+
+
+ {{ interview.duration }} min
+
+
+
+ {{ interview.location }}
+
+
+
+
+
+
+ {{ responseLabel.text }}
+
+
+
+
+
+
diff --git a/app/components/portal/PipelineProgress.vue b/app/components/portal/PipelineProgress.vue
new file mode 100644
index 0000000..a239de0
--- /dev/null
+++ b/app/components/portal/PipelineProgress.vue
@@ -0,0 +1,152 @@
+
+
+
+
+
+
+
+
+
Application not selected
+
The team has decided not to move forward at this time. Don't be discouraged — keep applying!
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ stage.label }}
+
+
+
+
+
+
diff --git a/app/components/portal/StatusBadge.vue b/app/components/portal/StatusBadge.vue
new file mode 100644
index 0000000..0ee38ff
--- /dev/null
+++ b/app/components/portal/StatusBadge.vue
@@ -0,0 +1,50 @@
+
+
+
+
+ {{ statusConfig.label }}
+
+
diff --git a/app/components/portal/StatusTimeline.vue b/app/components/portal/StatusTimeline.vue
new file mode 100644
index 0000000..99ef2ee
--- /dev/null
+++ b/app/components/portal/StatusTimeline.vue
@@ -0,0 +1,111 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ entry.title }}
+
+
+ {{ formatDate(entry.date) }} at {{ formatTime(entry.date) }}
+
+
+
+ {{ entry.description }}
+
+
+
+
+
+
+
diff --git a/app/layouts/portal.vue b/app/layouts/portal.vue
new file mode 100644
index 0000000..15c5581
--- /dev/null
+++ b/app/layouts/portal.vue
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+ Application Portal
+ Powered by Reqcore
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/pages/jobs/[slug]/apply.vue b/app/pages/jobs/[slug]/apply.vue
index 9050f13..8358087 100644
--- a/app/pages/jobs/[slug]/apply.vue
+++ b/app/pages/jobs/[slug]/apply.vue
@@ -163,6 +163,8 @@ async function handleSubmit() {
const hasAnyFiles = Object.keys(fileUploads.value).length > 0
|| !!resumeFile.value
+ let result: { success: boolean; portalToken?: string | null } | undefined
+
if (hasAnyFiles) {
// Use FormData when files are present
const formData = new FormData()
@@ -201,13 +203,13 @@ async function handleSubmit() {
if (utmTerm) formData.append('utmTerm', utmTerm)
if (utmContent) formData.append('utmContent', utmContent)
- await $fetch(`/api/public/jobs/${jobSlug}/apply`, {
+ result = await $fetch<{ success: boolean; portalToken?: string | null }>(`/api/public/jobs/${jobSlug}/apply`, {
method: 'POST',
body: formData,
})
} else {
// No files — use JSON as before
- await $fetch(`/api/public/jobs/${jobSlug}/apply`, {
+ result = await $fetch<{ success: boolean; portalToken?: string | null }>(`/api/public/jobs/${jobSlug}/apply`, {
method: 'POST',
body: {
firstName: form.value.firstName.trim(),
@@ -228,7 +230,13 @@ async function handleSubmit() {
}
track('application_submitted', { slug: jobSlug })
- await navigateTo(`/jobs/${jobSlug}/confirmation`)
+ // Redirect directly to the portal dashboard if we have a token,
+ // otherwise fall back to the confirmation page
+ if (result?.portalToken) {
+ await navigateTo(`/portal/t/${result.portalToken}?fresh=1`)
+ } else {
+ await navigateTo({ path: `/jobs/${jobSlug}/confirmation` })
+ }
} catch (err: any) {
const message = err.data?.statusMessage ?? 'Something went wrong. Please try again.'
submitError.value = message
diff --git a/app/pages/jobs/[slug]/confirmation.vue b/app/pages/jobs/[slug]/confirmation.vue
index 329443a..7e67760 100644
--- a/app/pages/jobs/[slug]/confirmation.vue
+++ b/app/pages/jobs/[slug]/confirmation.vue
@@ -1,5 +1,5 @@
@@ -43,6 +67,38 @@ useSeoMeta({
Your application has been received. The hiring team will review it and get back to you if there’s a match.
+
+
+
Track Your Application
+
+ Bookmark this link to check your application status anytime. You can also sign in with Google to track all applications.
+
+
+
+ {{ portalUrl }}
+
+
+
+ {{ copied ? 'Copied!' : 'Copy' }}
+
+
+
+
+
+ View your dashboard now
+
+
+
+
+import {
+ Building2,
+ MapPin,
+ Briefcase,
+ Calendar,
+ FileText,
+ Clock,
+ ChevronLeft,
+ Shield,
+ Globe,
+} from 'lucide-vue-next'
+
+definePageMeta({
+ layout: 'portal',
+})
+
+const route = useRoute()
+const applicationId = computed(() => route.params.id as string)
+
+// Check session
+const { data: sessionData } = await useFetch('/api/portal/auth/session')
+if (!sessionData.value?.authenticated) {
+ await navigateTo('/portal/auth/sign-in')
+}
+
+const { data, error, status } = await useFetch(`/api/portal/applications/${applicationId.value}`, {
+ key: `portal-app-${applicationId.value}`,
+ headers: useRequestHeaders(['cookie']),
+})
+
+useHead({
+ title: data.value ? `${data.value.job.title} — Application Status` : 'Application Portal',
+ meta: [
+ { name: 'robots', content: 'noindex, nofollow' },
+ ],
+})
+
+function formatDate(date: string | Date): string {
+ return new Date(date).toLocaleDateString(undefined, {
+ month: 'long',
+ day: 'numeric',
+ year: 'numeric',
+ })
+}
+
+function getRelativeTime(date: string | Date): string {
+ const now = new Date()
+ const d = new Date(date)
+ const diff = now.getTime() - d.getTime()
+ const days = Math.floor(diff / (1000 * 60 * 60 * 24))
+
+ if (days === 0) return 'Today'
+ if (days === 1) return 'Yesterday'
+ if (days < 7) return `${days} days ago`
+ if (days < 30) return `${Math.floor(days / 7)} weeks ago`
+ return `${Math.floor(days / 30)} months ago`
+}
+
+
+
+
+
+
+
+ Back to all applications
+
+
+
+
+
+
+
+
+
Application Not Found
+
+ This application could not be loaded.
+
+
+ Back to dashboard
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ data.job.title }}
+
+
+
+
+ {{ data.organization.name }}
+
+
+
+ {{ data.job.location }}
+
+
+
+ {{ data.job.type }}
+
+
+
+ {{ data.job.remoteStatus }}
+
+
+
+
+
+
+
+
+
+ Applied {{ formatDate(data.application.appliedAt) }}
+
+
+
+ Updated {{ getRelativeTime(data.application.lastUpdated) }}
+
+
+
+
+
+
+
+
Application Progress
+
+
+
+
+
+
+
+
+ Upcoming Interviews
+
+
+
+
+
No upcoming interviews
+
+
+
+
+
+
+
+ Your Documents
+
+
+
+
+
+
+
+
{{ doc.filename }}
+
+ {{ doc.type === 'resume' ? 'Resume' : doc.type === 'cover_letter' ? 'Cover Letter' : 'Document' }}
+
+
+
+
+
+
+
No documents uploaded
+
+
+
+
+
+
+
+
+ Activity Timeline
+
+
+
+
+
+
diff --git a/app/pages/portal/auth/sign-in.vue b/app/pages/portal/auth/sign-in.vue
new file mode 100644
index 0000000..3f27b0d
--- /dev/null
+++ b/app/pages/portal/auth/sign-in.vue
@@ -0,0 +1,95 @@
+
+
+
+
+
+
+
+
+
+
+
Application Portal
+
+ Sign in to track all your job applications in one place
+
+
+
+
+
+
+ You cancelled the sign-in process. Please try again.
+
+
+ Something went wrong. Please try again.
+
+
+
+
+
+
+
+ Continue with Google
+
+
+
+
+
+
+ Have an access link? Paste it in your browser to view your application directly.
+
+
+
+
+
+
+
+
+
+
Read-only access
+
Your data is safe — view only, no changes
+
+
+
+
+
+
All applications
+
Track every job you've applied to
+
+
+
+
+
+
diff --git a/app/pages/portal/index.vue b/app/pages/portal/index.vue
new file mode 100644
index 0000000..9396897
--- /dev/null
+++ b/app/pages/portal/index.vue
@@ -0,0 +1,217 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ data.account.name ?? 'Applicant' }}
+
+
+ {{ data.account.email }}
+
+
+
+
+
+ Sign out
+
+
+
+
+
+
My Applications
+
+ Track all your job applications in one place
+
+
+
+
+
+
+
+
+
{{ error.data?.message || 'Failed to load applications' }}
+
+ Try again
+
+
+
+
+
+
+
+
+
No applications found
+
+ No applications were found for {{ data?.account?.email }}. Make sure you applied using this email address.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ app.job.title }}
+
+
+
+
+ {{ app.organization.name }}
+
+
+
+ {{ app.job.location }}
+
+
+
+ {{ app.job.type }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ app.upcomingInterviewCount }} upcoming
+
+ Applied {{ getRelativeTime(app.appliedAt) }}
+
+
+
+
+
+
diff --git a/app/pages/portal/t/[token].vue b/app/pages/portal/t/[token].vue
new file mode 100644
index 0000000..f4e1b7e
--- /dev/null
+++ b/app/pages/portal/t/[token].vue
@@ -0,0 +1,385 @@
+
+
+
+
+
+
+
+
+
Access Link Invalid
+
+ This link may have expired or is no longer valid. If you signed in with Google, you can access your applications from the portal dashboard.
+
+
+
+ Sign in to view your applications
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Application submitted successfully!
+
+ Bookmark this page (Ctrl+D ) to check your application status anytime. This is your personal dashboard — it updates automatically as your application progresses.
+
+
+
+
+ Dismiss
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ data.job.title }}
+
+
+
+
+ {{ data.organization.name }}
+
+
+
+ {{ data.job.location }}
+
+
+
+ {{ data.job.type }}
+
+
+
+ {{ data.job.remoteStatus }}
+
+
+
+
+
+
+
+
+ {{ isRefreshing ? 'Refreshing...' : 'Refresh' }}
+
+
+
+
+
+
+
+
+
+
+ Applied {{ formatDateTime(data.application.appliedAt) }}
+ ·
+ {{ getRelativeTime(data.application.appliedAt) }}
+
+
+
+ Updated {{ formatDateTime(data.application.lastUpdated) }}
+
+
+
+
+
+ Viewed by hiring team on {{ formatDateTime(data.application.viewedAt) }}
+
+
+
+
+
+
+
+
+
+
Save this to your account
+
+ Sign in with Google to track all your applications in one place — even across different companies.
+
+
+
+
+ Sign in with Google
+
+
+
+
+
+
+
Application Progress
+
+
+
+
+
+
+
+
+
+ Upcoming Interviews
+
+
+
+
+
+
+
No upcoming interviews
+
Interviews will appear here when scheduled
+
+
+
+
+
+
+
+
+
+
+ Your Documents
+
+
+
+
+
+
+
+
+
+ {{ doc.filename }}
+
+
+ {{ doc.type === 'resume' ? 'Resume' : doc.type === 'cover_letter' ? 'Cover Letter' : 'Document' }}
+ · Uploaded {{ formatDate(doc.uploadedAt) }}
+
+
+
+
+
+
+
+
No documents uploaded
+
+
+
+
+
+
+
+
+ Activity Timeline
+
+
+
+
+
+
+
+
+
+
What happens next?
+
+
+
1
+
+
Review
+
The hiring team reviews your application
+
+
+
+
2
+
+
Interview
+
If selected, you'll be invited to interview
+
+
+
+
3
+
+
Decision
+
You'll receive a final decision via this portal
+
+
+
+
+
+
+
+
+ This link is private to you. Bookmark it to check back anytime.
+
+
+
diff --git a/server/api/applications/[id].get.ts b/server/api/applications/[id].get.ts
index 0ebd54d..5f4ac58 100644
--- a/server/api/applications/[id].get.ts
+++ b/server/api/applications/[id].get.ts
@@ -1,10 +1,11 @@
-import { eq, and } from 'drizzle-orm'
+import { eq, and, isNull } from 'drizzle-orm'
import { application } from '../../database/schema'
import { applicationIdParamSchema } from '../../utils/schemas/application'
/**
* GET /api/applications/:id
* Single application detail with related candidate, job, and question responses.
+ * Also records the first time a recruiter views the application.
*/
export default defineEventHandler(async (event) => {
const session = await requirePermission(event, { application: ['read'] })
@@ -48,5 +49,13 @@ export default defineEventHandler(async (event) => {
throw createError({ statusCode: 404, statusMessage: 'Not found' })
}
+ // Record first view by a recruiter (fire-and-forget, non-blocking)
+ if (!result.viewedAt) {
+ db.update(application)
+ .set({ viewedAt: new Date(), viewedBy: session.user.id })
+ .where(and(eq(application.id, id), isNull(application.viewedAt)))
+ .catch(() => {})
+ }
+
return result
})
diff --git a/server/api/portal/applications/[id].get.ts b/server/api/portal/applications/[id].get.ts
new file mode 100644
index 0000000..a577bb6
--- /dev/null
+++ b/server/api/portal/applications/[id].get.ts
@@ -0,0 +1,58 @@
+import { eq, and } from 'drizzle-orm'
+import { application } from '../../../database/schema'
+
+/**
+ * GET /api/portal/applications/:id
+ *
+ * Returns detailed dashboard data for a specific application.
+ * Requires a valid portal session cookie (Google OAuth).
+ *
+ * Security: Read-only. Session-authenticated. Only returns data
+ * for applications belonging to the authenticated email.
+ */
+export default defineEventHandler(async (event) => {
+ const sessionToken = getCookie(event, 'portal_session')
+
+ if (!sessionToken) {
+ throw createError({ statusCode: 401, statusMessage: 'Please sign in to view your applications' })
+ }
+
+ const account = await validateApplicantSession(sessionToken)
+
+ if (!account) {
+ deleteCookie(event, 'portal_session', { path: '/' })
+ throw createError({ statusCode: 401, statusMessage: 'Session expired. Please sign in again.' })
+ }
+
+ const applicationId = getRouterParam(event, 'id')
+ if (!applicationId) {
+ throw createError({ statusCode: 400, statusMessage: 'Application ID is required' })
+ }
+
+ // Find the candidate record matching this email + application
+ const app = await db.query.application.findFirst({
+ where: eq(application.id, applicationId),
+ columns: { id: true, candidateId: true, organizationId: true },
+ with: {
+ candidate: {
+ columns: { email: true },
+ },
+ },
+ })
+
+ if (!app || app.candidate.email.toLowerCase() !== account.email.toLowerCase()) {
+ throw createError({ statusCode: 404, statusMessage: 'Application not found' })
+ }
+
+ const dashboardData = await getApplicationDashboard(
+ app.id,
+ app.candidateId,
+ app.organizationId,
+ )
+
+ if (!dashboardData) {
+ throw createError({ statusCode: 404, statusMessage: 'Application not found' })
+ }
+
+ return dashboardData
+})
diff --git a/server/api/portal/auth/google/callback.get.ts b/server/api/portal/auth/google/callback.get.ts
new file mode 100644
index 0000000..800eed9
--- /dev/null
+++ b/server/api/portal/auth/google/callback.get.ts
@@ -0,0 +1,105 @@
+/**
+ * GET /api/portal/auth/google/callback
+ *
+ * Google OAuth callback for the applicant portal.
+ * Exchanges the authorization code for tokens, fetches the user profile,
+ * creates/updates an applicant account, and issues a portal session cookie.
+ */
+export default defineEventHandler(async (event) => {
+ const query = getQuery(event)
+ const code = typeof query.code === 'string' ? query.code : ''
+ const stateParam = typeof query.state === 'string' ? query.state : ''
+ const error = typeof query.error === 'string' ? query.error : ''
+
+ if (error) {
+ return sendRedirect(event, '/portal/auth/sign-in?error=oauth_denied')
+ }
+
+ if (!code) {
+ throw createError({ statusCode: 400, statusMessage: 'Missing authorization code' })
+ }
+
+ // Parse state
+ let returnTo = ''
+ let portalToken = ''
+ try {
+ const state = JSON.parse(Buffer.from(stateParam, 'base64url').toString())
+ returnTo = state.returnTo || ''
+ portalToken = state.portalToken || ''
+ } catch {
+ // Ignore invalid state
+ }
+
+ const clientId = env.AUTH_GOOGLE_CLIENT_ID!
+ const clientSecret = env.AUTH_GOOGLE_CLIENT_SECRET!
+ // Must match the redirect_uri used in the init endpoint
+ const baseUrl = getRequestURL(event).origin
+ const redirectUri = `${baseUrl}/api/portal/auth/google/callback`
+
+ // Exchange code for tokens
+ const tokenResponse = await $fetch<{
+ access_token: string
+ id_token?: string
+ token_type: string
+ }>('https://oauth2.googleapis.com/token', {
+ method: 'POST',
+ body: {
+ code,
+ client_id: clientId,
+ client_secret: clientSecret,
+ redirect_uri: redirectUri,
+ grant_type: 'authorization_code',
+ },
+ }).catch((err) => {
+ logError('portal.google_token_exchange_failed', {
+ error_message: err instanceof Error ? err.message : String(err),
+ })
+ throw createError({ statusCode: 502, statusMessage: 'Failed to authenticate with Google' })
+ })
+
+ // Fetch user profile
+ const profile = await $fetch<{
+ sub: string
+ email: string
+ name?: string
+ picture?: string
+ email_verified?: boolean
+ }>('https://openidconnect.googleapis.com/v1/userinfo', {
+ headers: {
+ Authorization: `Bearer ${tokenResponse.access_token}`,
+ },
+ }).catch((err) => {
+ logError('portal.google_profile_fetch_failed', {
+ error_message: err instanceof Error ? err.message : String(err),
+ })
+ throw createError({ statusCode: 502, statusMessage: 'Failed to fetch Google profile' })
+ })
+
+ if (!profile.email) {
+ throw createError({ statusCode: 400, statusMessage: 'Google account does not have an email address' })
+ }
+
+ // Find or create applicant account
+ const account = await findOrCreateApplicantAccount({
+ googleId: profile.sub,
+ email: profile.email,
+ name: profile.name,
+ image: profile.picture,
+ })
+
+ // Create session
+ const sessionToken = await createApplicantSession(account.id)
+
+ // Set secure session cookie
+ setCookie(event, 'portal_session', sessionToken, {
+ httpOnly: true,
+ secure: !import.meta.dev,
+ sameSite: 'lax',
+ path: '/',
+ maxAge: 72 * 60 * 60, // 72 hours
+ })
+
+ // Redirect to portal dashboard or return URL
+ const redirectTo = returnTo || '/portal'
+ return sendRedirect(event, redirectTo)
+})
diff --git a/server/api/portal/auth/google/index.get.ts b/server/api/portal/auth/google/index.get.ts
new file mode 100644
index 0000000..a64f12c
--- /dev/null
+++ b/server/api/portal/auth/google/index.get.ts
@@ -0,0 +1,47 @@
+import { eq, and } from 'drizzle-orm'
+import { candidate } from '../../../../database/schema'
+
+/**
+ * GET /api/portal/auth/google
+ *
+ * Initiates Google OAuth flow for the applicant portal.
+ * Redirects the user to Google's consent screen.
+ * Reuses the same AUTH_GOOGLE credentials as the recruiter OAuth,
+ * but with a portal-specific redirect URI.
+ */
+export default defineEventHandler(async (event) => {
+ const clientId = env.AUTH_GOOGLE_CLIENT_ID
+ const clientSecret = env.AUTH_GOOGLE_CLIENT_SECRET
+
+ if (!clientId || !clientSecret) {
+ throw createError({
+ statusCode: 503,
+ statusMessage: 'Google sign-in is not configured for the applicant portal',
+ })
+ }
+
+ // Read optional return URL and portal token from query
+ const query = getQuery(event)
+ const returnTo = typeof query.returnTo === 'string' ? query.returnTo : ''
+ const portalToken = typeof query.portalToken === 'string' ? query.portalToken : ''
+
+ // Build state to pass through OAuth flow
+ const state = Buffer.from(JSON.stringify({ returnTo, portalToken })).toString('base64url')
+
+ // Derive base URL from the actual request so the redirect_uri always matches
+ // the origin the browser is using (handles localhost, Railway, custom domains).
+ const baseUrl = getRequestURL(event).origin
+ const redirectUri = `${baseUrl}/api/portal/auth/google/callback`
+
+ const params = new URLSearchParams({
+ client_id: clientId,
+ redirect_uri: redirectUri,
+ response_type: 'code',
+ scope: 'openid email profile',
+ access_type: 'online',
+ prompt: 'select_account',
+ state,
+ })
+
+ return sendRedirect(event, `https://accounts.google.com/o/oauth2/v2/auth?${params}`)
+})
diff --git a/server/api/portal/auth/session.get.ts b/server/api/portal/auth/session.get.ts
new file mode 100644
index 0000000..a64ffc3
--- /dev/null
+++ b/server/api/portal/auth/session.get.ts
@@ -0,0 +1,33 @@
+import { eq } from 'drizzle-orm'
+
+/**
+ * GET /api/portal/auth/session
+ *
+ * Returns the current applicant portal session.
+ * Used by the client to check authentication status.
+ */
+export default defineEventHandler(async (event) => {
+ const sessionToken = getCookie(event, 'portal_session')
+
+ if (!sessionToken) {
+ return { authenticated: false, account: null }
+ }
+
+ const account = await validateApplicantSession(sessionToken)
+
+ if (!account) {
+ // Clear stale cookie
+ deleteCookie(event, 'portal_session', { path: '/' })
+ return { authenticated: false, account: null }
+ }
+
+ return {
+ authenticated: true,
+ account: {
+ id: account.id,
+ email: account.email,
+ name: account.name,
+ image: account.image,
+ },
+ }
+})
diff --git a/server/api/portal/auth/sign-out.post.ts b/server/api/portal/auth/sign-out.post.ts
new file mode 100644
index 0000000..2801f1b
--- /dev/null
+++ b/server/api/portal/auth/sign-out.post.ts
@@ -0,0 +1,21 @@
+import { eq } from 'drizzle-orm'
+import { applicantSession } from '../../../database/schema'
+
+/**
+ * POST /api/portal/auth/sign-out
+ *
+ * Signs the applicant out by deleting their session.
+ */
+export default defineEventHandler(async (event) => {
+ const sessionToken = getCookie(event, 'portal_session')
+
+ if (sessionToken) {
+ await db.delete(applicantSession)
+ .where(eq(applicantSession.token, sessionToken))
+ .catch(() => {})
+
+ deleteCookie(event, 'portal_session', { path: '/' })
+ }
+
+ return { success: true }
+})
diff --git a/server/api/portal/dashboard.get.ts b/server/api/portal/dashboard.get.ts
new file mode 100644
index 0000000..8e2ce3c
--- /dev/null
+++ b/server/api/portal/dashboard.get.ts
@@ -0,0 +1,34 @@
+/**
+ * GET /api/portal/dashboard
+ *
+ * Returns all applications for the authenticated applicant.
+ * Requires a valid portal session cookie (Google OAuth).
+ *
+ * Security: Read-only. Session-authenticated.
+ */
+export default defineEventHandler(async (event) => {
+ const sessionToken = getCookie(event, 'portal_session')
+
+ if (!sessionToken) {
+ throw createError({ statusCode: 401, statusMessage: 'Please sign in to view your applications' })
+ }
+
+ const account = await validateApplicantSession(sessionToken)
+
+ if (!account) {
+ deleteCookie(event, 'portal_session', { path: '/' })
+ throw createError({ statusCode: 401, statusMessage: 'Session expired. Please sign in again.' })
+ }
+
+ const applications = await getApplicantDashboardByEmail(account.email)
+
+ return {
+ account: {
+ id: account.id,
+ email: account.email,
+ name: account.name,
+ image: account.image,
+ },
+ applications,
+ }
+})
diff --git a/server/api/portal/token/[token].get.ts b/server/api/portal/token/[token].get.ts
new file mode 100644
index 0000000..8f82809
--- /dev/null
+++ b/server/api/portal/token/[token].get.ts
@@ -0,0 +1,50 @@
+import { eq, and } from 'drizzle-orm'
+import { application, job, candidate, interview, organization, applicantPortalToken } from '../../../database/schema'
+
+/** Rate limit: max 30 requests per IP per minute */
+const portalTokenRateLimit = createRateLimiter({
+ windowMs: 60 * 1000,
+ maxRequests: 30,
+ message: 'Too many requests. Please try again shortly.',
+})
+
+/**
+ * GET /api/portal/token/:token
+ *
+ * Validates a portal access token and returns full application dashboard data.
+ * This is the primary endpoint for token-based (no-login) portal access.
+ *
+ * Returns: job details, application status, timeline, upcoming interviews,
+ * organization name, and document list.
+ *
+ * Security: Read-only. Token is validated and rate-limited.
+ */
+export default defineEventHandler(async (event) => {
+ if (process.env.NODE_ENV === 'production' && !process.env.CI) {
+ await portalTokenRateLimit(event)
+ }
+
+ const token = getRouterParam(event, 'token')
+
+ if (!token || token.length !== 64 || !/^[0-9a-f]+$/.test(token)) {
+ throw createError({ statusCode: 400, statusMessage: 'Invalid token format' })
+ }
+
+ const tokenData = await validatePortalToken(token)
+
+ if (!tokenData) {
+ throw createError({ statusCode: 404, statusMessage: 'Invalid or expired access link' })
+ }
+
+ const dashboardData = await getApplicationDashboard(
+ tokenData.applicationId,
+ tokenData.candidateId,
+ tokenData.organizationId,
+ )
+
+ if (!dashboardData) {
+ throw createError({ statusCode: 404, statusMessage: 'Application not found' })
+ }
+
+ return dashboardData
+})
diff --git a/server/api/public/jobs/[slug]/apply.post.ts b/server/api/public/jobs/[slug]/apply.post.ts
index 774e399..c894a03 100644
--- a/server/api/public/jobs/[slug]/apply.post.ts
+++ b/server/api/public/jobs/[slug]/apply.post.ts
@@ -648,8 +648,30 @@ export default defineEventHandler(async (event) => {
is_returning_candidate: !!existingCandidate,
})
+ // ─────────────────────────────────────────────
+ // 13. Generate portal access token for applicant dashboard
+ // ─────────────────────────────────────────────
+
+ let portalAccessToken: string | null = null
+ try {
+ portalAccessToken = await createPortalToken(
+ newApplication!.id,
+ candidateId,
+ orgId,
+ )
+ } catch (portalErr) {
+ // Portal token is best-effort — never fail an application for it
+ logWarn('application.portal_token_failed', {
+ application_id: newApplication?.id,
+ error_message: portalErr instanceof Error ? portalErr.message : String(portalErr),
+ })
+ }
+
setResponseStatus(event, 201)
- return { success: true }
+ return {
+ success: true,
+ portalToken: portalAccessToken,
+ }
})
// ─────────────────────────────────────────────
diff --git a/server/database/migrations/0021_blushing_thing.sql b/server/database/migrations/0021_blushing_thing.sql
new file mode 100644
index 0000000..bdf43c4
--- /dev/null
+++ b/server/database/migrations/0021_blushing_thing.sql
@@ -0,0 +1,44 @@
+CREATE TABLE "applicant_account" (
+ "id" text PRIMARY KEY NOT NULL,
+ "google_id" text NOT NULL,
+ "email" text NOT NULL,
+ "name" text,
+ "image" text,
+ "created_at" timestamp DEFAULT now() NOT NULL,
+ "updated_at" timestamp DEFAULT now() NOT NULL,
+ CONSTRAINT "applicant_account_google_id_unique" UNIQUE("google_id")
+);
+--> statement-breakpoint
+CREATE TABLE "applicant_portal_token" (
+ "id" text PRIMARY KEY NOT NULL,
+ "token" text NOT NULL,
+ "application_id" text NOT NULL,
+ "candidate_id" text NOT NULL,
+ "organization_id" text NOT NULL,
+ "expires_at" timestamp NOT NULL,
+ "last_accessed_at" timestamp,
+ "created_at" timestamp DEFAULT now() NOT NULL,
+ CONSTRAINT "applicant_portal_token_token_unique" UNIQUE("token")
+);
+--> statement-breakpoint
+CREATE TABLE "applicant_session" (
+ "id" text PRIMARY KEY NOT NULL,
+ "applicant_account_id" text NOT NULL,
+ "token" text NOT NULL,
+ "expires_at" timestamp NOT NULL,
+ "created_at" timestamp DEFAULT now() NOT NULL,
+ CONSTRAINT "applicant_session_token_unique" UNIQUE("token")
+);
+--> statement-breakpoint
+ALTER TABLE "applicant_portal_token" ADD CONSTRAINT "applicant_portal_token_application_id_application_id_fk" FOREIGN KEY ("application_id") REFERENCES "public"."application"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "applicant_portal_token" ADD CONSTRAINT "applicant_portal_token_candidate_id_candidate_id_fk" FOREIGN KEY ("candidate_id") REFERENCES "public"."candidate"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "applicant_portal_token" ADD CONSTRAINT "applicant_portal_token_organization_id_organization_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organization"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "applicant_session" ADD CONSTRAINT "applicant_session_applicant_account_id_applicant_account_id_fk" FOREIGN KEY ("applicant_account_id") REFERENCES "public"."applicant_account"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+CREATE INDEX "applicant_account_email_idx" ON "applicant_account" USING btree ("email");--> statement-breakpoint
+CREATE UNIQUE INDEX "applicant_account_google_id_idx" ON "applicant_account" USING btree ("google_id");--> statement-breakpoint
+CREATE INDEX "portal_token_application_id_idx" ON "applicant_portal_token" USING btree ("application_id");--> statement-breakpoint
+CREATE INDEX "portal_token_candidate_id_idx" ON "applicant_portal_token" USING btree ("candidate_id");--> statement-breakpoint
+CREATE INDEX "portal_token_organization_id_idx" ON "applicant_portal_token" USING btree ("organization_id");--> statement-breakpoint
+CREATE INDEX "portal_token_token_idx" ON "applicant_portal_token" USING btree ("token");--> statement-breakpoint
+CREATE INDEX "applicant_session_account_id_idx" ON "applicant_session" USING btree ("applicant_account_id");--> statement-breakpoint
+CREATE INDEX "applicant_session_token_idx" ON "applicant_session" USING btree ("token");
\ No newline at end of file
diff --git a/server/database/migrations/0022_mute_green_goblin.sql b/server/database/migrations/0022_mute_green_goblin.sql
new file mode 100644
index 0000000..9e57cc9
--- /dev/null
+++ b/server/database/migrations/0022_mute_green_goblin.sql
@@ -0,0 +1,3 @@
+ALTER TABLE "application" ADD COLUMN "viewed_at" timestamp;--> statement-breakpoint
+ALTER TABLE "application" ADD COLUMN "viewed_by" text;--> statement-breakpoint
+ALTER TABLE "application" ADD CONSTRAINT "application_viewed_by_user_id_fk" FOREIGN KEY ("viewed_by") REFERENCES "public"."user"("id") ON DELETE set null ON UPDATE no action;
\ No newline at end of file
diff --git a/server/database/migrations/meta/0021_snapshot.json b/server/database/migrations/meta/0021_snapshot.json
new file mode 100644
index 0000000..0f6a053
--- /dev/null
+++ b/server/database/migrations/meta/0021_snapshot.json
@@ -0,0 +1,4505 @@
+{
+ "id": "c3684bb5-9152-4a7d-bb6a-26979662e546",
+ "prevId": "6383b697-d628-4372-b8cc-54e02b88b5bd",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.account": {
+ "name": "account",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "account_id": {
+ "name": "account_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_id": {
+ "name": "provider_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "access_token": {
+ "name": "access_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "refresh_token": {
+ "name": "refresh_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "id_token": {
+ "name": "id_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "access_token_expires_at": {
+ "name": "access_token_expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "refresh_token_expires_at": {
+ "name": "refresh_token_expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "scope": {
+ "name": "scope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "password": {
+ "name": "password",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "account_user_id_idx": {
+ "name": "account_user_id_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "account_user_id_user_id_fk": {
+ "name": "account_user_id_user_id_fk",
+ "tableFrom": "account",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.invitation": {
+ "name": "invitation",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "inviter_id": {
+ "name": "inviter_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "invitation_organization_id_idx": {
+ "name": "invitation_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "invitation_email_idx": {
+ "name": "invitation_email_idx",
+ "columns": [
+ {
+ "expression": "email",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "invitation_inviter_id_user_id_fk": {
+ "name": "invitation_inviter_id_user_id_fk",
+ "tableFrom": "invitation",
+ "tableTo": "user",
+ "columnsFrom": [
+ "inviter_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "invitation_organization_id_organization_id_fk": {
+ "name": "invitation_organization_id_organization_id_fk",
+ "tableFrom": "invitation",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.member": {
+ "name": "member",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'member'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "member_user_id_idx": {
+ "name": "member_user_id_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "member_organization_id_idx": {
+ "name": "member_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "member_user_org_unique_idx": {
+ "name": "member_user_org_unique_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "member_user_id_user_id_fk": {
+ "name": "member_user_id_user_id_fk",
+ "tableFrom": "member",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "member_organization_id_organization_id_fk": {
+ "name": "member_organization_id_organization_id_fk",
+ "tableFrom": "member",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.organization": {
+ "name": "organization",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "logo": {
+ "name": "logo",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "metadata": {
+ "name": "metadata",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "organization_slug_unique": {
+ "name": "organization_slug_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "slug"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.rate_limit": {
+ "name": "rate_limit",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "count": {
+ "name": "count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "last_request": {
+ "name": "last_request",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "rate_limit_key_idx": {
+ "name": "rate_limit_key_idx",
+ "columns": [
+ {
+ "expression": "key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.session": {
+ "name": "session",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "token": {
+ "name": "token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ip_address": {
+ "name": "ip_address",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_agent": {
+ "name": "user_agent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "active_organization_id": {
+ "name": "active_organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "session_user_id_idx": {
+ "name": "session_user_id_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "session_user_id_user_id_fk": {
+ "name": "session_user_id_user_id_fk",
+ "tableFrom": "session",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "session_token_unique": {
+ "name": "session_token_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "token"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.user": {
+ "name": "user",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email_verified": {
+ "name": "email_verified",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "image": {
+ "name": "image",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "user_email_unique": {
+ "name": "user_email_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "email"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.verification": {
+ "name": "verification",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "identifier": {
+ "name": "identifier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "verification_identifier_idx": {
+ "name": "verification_identifier_idx",
+ "columns": [
+ {
+ "expression": "identifier",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.activity_log": {
+ "name": "activity_log",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "actor_id": {
+ "name": "actor_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "action": {
+ "name": "action",
+ "type": "activity_action",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "resource_type": {
+ "name": "resource_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "resource_id": {
+ "name": "resource_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "metadata": {
+ "name": "metadata",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "activity_log_organization_id_idx": {
+ "name": "activity_log_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "activity_log_actor_id_idx": {
+ "name": "activity_log_actor_id_idx",
+ "columns": [
+ {
+ "expression": "actor_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "activity_log_resource_idx": {
+ "name": "activity_log_resource_idx",
+ "columns": [
+ {
+ "expression": "resource_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "resource_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "activity_log_created_at_idx": {
+ "name": "activity_log_created_at_idx",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "activity_log_organization_id_organization_id_fk": {
+ "name": "activity_log_organization_id_organization_id_fk",
+ "tableFrom": "activity_log",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "activity_log_actor_id_user_id_fk": {
+ "name": "activity_log_actor_id_user_id_fk",
+ "tableFrom": "activity_log",
+ "tableTo": "user",
+ "columnsFrom": [
+ "actor_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.ai_config": {
+ "name": "ai_config",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'openai'"
+ },
+ "model": {
+ "name": "model",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'gpt-4o-mini'"
+ },
+ "api_key_encrypted": {
+ "name": "api_key_encrypted",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "base_url": {
+ "name": "base_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "max_tokens": {
+ "name": "max_tokens",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 4096
+ },
+ "input_price_per_1m": {
+ "name": "input_price_per_1m",
+ "type": "numeric(10, 4)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "output_price_per_1m": {
+ "name": "output_price_per_1m",
+ "type": "numeric(10, 4)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "ai_config_organization_id_idx": {
+ "name": "ai_config_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "ai_config_organization_id_organization_id_fk": {
+ "name": "ai_config_organization_id_organization_id_fk",
+ "tableFrom": "ai_config",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.analysis_run": {
+ "name": "analysis_run",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "application_id": {
+ "name": "application_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "analysis_run_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'completed'"
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "model": {
+ "name": "model",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "criteria_snapshot": {
+ "name": "criteria_snapshot",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "composite_score": {
+ "name": "composite_score",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "prompt_tokens": {
+ "name": "prompt_tokens",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "completion_tokens": {
+ "name": "completion_tokens",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "raw_response": {
+ "name": "raw_response",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "error_message": {
+ "name": "error_message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "scored_by_id": {
+ "name": "scored_by_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "analysis_run_organization_id_idx": {
+ "name": "analysis_run_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "analysis_run_application_id_idx": {
+ "name": "analysis_run_application_id_idx",
+ "columns": [
+ {
+ "expression": "application_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "analysis_run_created_at_idx": {
+ "name": "analysis_run_created_at_idx",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "analysis_run_organization_id_organization_id_fk": {
+ "name": "analysis_run_organization_id_organization_id_fk",
+ "tableFrom": "analysis_run",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "analysis_run_application_id_application_id_fk": {
+ "name": "analysis_run_application_id_application_id_fk",
+ "tableFrom": "analysis_run",
+ "tableTo": "application",
+ "columnsFrom": [
+ "application_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "analysis_run_scored_by_id_user_id_fk": {
+ "name": "analysis_run_scored_by_id_user_id_fk",
+ "tableFrom": "analysis_run",
+ "tableTo": "user",
+ "columnsFrom": [
+ "scored_by_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.application": {
+ "name": "application",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "candidate_id": {
+ "name": "candidate_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "job_id": {
+ "name": "job_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "application_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'new'"
+ },
+ "score": {
+ "name": "score",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "notes": {
+ "name": "notes",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cover_letter_text": {
+ "name": "cover_letter_text",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "application_organization_id_idx": {
+ "name": "application_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "application_candidate_id_idx": {
+ "name": "application_candidate_id_idx",
+ "columns": [
+ {
+ "expression": "candidate_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "application_job_id_idx": {
+ "name": "application_job_id_idx",
+ "columns": [
+ {
+ "expression": "job_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "application_org_candidate_job_idx": {
+ "name": "application_org_candidate_job_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "candidate_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "job_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "application_organization_id_organization_id_fk": {
+ "name": "application_organization_id_organization_id_fk",
+ "tableFrom": "application",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "application_candidate_id_candidate_id_fk": {
+ "name": "application_candidate_id_candidate_id_fk",
+ "tableFrom": "application",
+ "tableTo": "candidate",
+ "columnsFrom": [
+ "candidate_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "application_job_id_job_id_fk": {
+ "name": "application_job_id_job_id_fk",
+ "tableFrom": "application",
+ "tableTo": "job",
+ "columnsFrom": [
+ "job_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.application_source": {
+ "name": "application_source",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "application_id": {
+ "name": "application_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel": {
+ "name": "channel",
+ "type": "source_channel",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'direct'"
+ },
+ "tracking_link_id": {
+ "name": "tracking_link_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_source": {
+ "name": "utm_source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_medium": {
+ "name": "utm_medium",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_campaign": {
+ "name": "utm_campaign",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_term": {
+ "name": "utm_term",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_content": {
+ "name": "utm_content",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "referrer_domain": {
+ "name": "referrer_domain",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "application_source_organization_id_idx": {
+ "name": "application_source_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "application_source_application_id_idx": {
+ "name": "application_source_application_id_idx",
+ "columns": [
+ {
+ "expression": "application_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "application_source_channel_idx": {
+ "name": "application_source_channel_idx",
+ "columns": [
+ {
+ "expression": "channel",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "application_source_tracking_link_id_idx": {
+ "name": "application_source_tracking_link_id_idx",
+ "columns": [
+ {
+ "expression": "tracking_link_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "application_source_application_idx": {
+ "name": "application_source_application_idx",
+ "columns": [
+ {
+ "expression": "application_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "application_source_organization_id_organization_id_fk": {
+ "name": "application_source_organization_id_organization_id_fk",
+ "tableFrom": "application_source",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "application_source_application_id_application_id_fk": {
+ "name": "application_source_application_id_application_id_fk",
+ "tableFrom": "application_source",
+ "tableTo": "application",
+ "columnsFrom": [
+ "application_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "application_source_tracking_link_id_tracking_link_id_fk": {
+ "name": "application_source_tracking_link_id_tracking_link_id_fk",
+ "tableFrom": "application_source",
+ "tableTo": "tracking_link",
+ "columnsFrom": [
+ "tracking_link_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.calendar_integration": {
+ "name": "calendar_integration",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider": {
+ "name": "provider",
+ "type": "calendar_provider",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'google'"
+ },
+ "access_token_encrypted": {
+ "name": "access_token_encrypted",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "refresh_token_encrypted": {
+ "name": "refresh_token_encrypted",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "calendar_id": {
+ "name": "calendar_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'primary'"
+ },
+ "account_email": {
+ "name": "account_email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "webhook_channel_id": {
+ "name": "webhook_channel_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "webhook_resource_id": {
+ "name": "webhook_resource_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "webhook_expiration": {
+ "name": "webhook_expiration",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "sync_token": {
+ "name": "sync_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "calendar_integration_user_provider_idx": {
+ "name": "calendar_integration_user_provider_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "provider",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "calendar_integration_webhook_channel_idx": {
+ "name": "calendar_integration_webhook_channel_idx",
+ "columns": [
+ {
+ "expression": "webhook_channel_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "calendar_integration_user_id_user_id_fk": {
+ "name": "calendar_integration_user_id_user_id_fk",
+ "tableFrom": "calendar_integration",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.candidate": {
+ "name": "candidate",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "first_name": {
+ "name": "first_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "last_name": {
+ "name": "last_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "phone": {
+ "name": "phone",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "candidate_organization_id_idx": {
+ "name": "candidate_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "candidate_org_email_idx": {
+ "name": "candidate_org_email_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "email",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "candidate_organization_id_organization_id_fk": {
+ "name": "candidate_organization_id_organization_id_fk",
+ "tableFrom": "candidate",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.comment": {
+ "name": "comment",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "author_id": {
+ "name": "author_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_type": {
+ "name": "target_type",
+ "type": "comment_target",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_id": {
+ "name": "target_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "body": {
+ "name": "body",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "comment_organization_id_idx": {
+ "name": "comment_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "comment_target_idx": {
+ "name": "comment_target_idx",
+ "columns": [
+ {
+ "expression": "target_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "target_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "comment_author_id_idx": {
+ "name": "comment_author_id_idx",
+ "columns": [
+ {
+ "expression": "author_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "comment_organization_id_organization_id_fk": {
+ "name": "comment_organization_id_organization_id_fk",
+ "tableFrom": "comment",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "comment_author_id_user_id_fk": {
+ "name": "comment_author_id_user_id_fk",
+ "tableFrom": "comment",
+ "tableTo": "user",
+ "columnsFrom": [
+ "author_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.criterion_score": {
+ "name": "criterion_score",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "application_id": {
+ "name": "application_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "criterion_key": {
+ "name": "criterion_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "max_score": {
+ "name": "max_score",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "applicant_score": {
+ "name": "applicant_score",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "confidence": {
+ "name": "confidence",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "evidence": {
+ "name": "evidence",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "strengths": {
+ "name": "strengths",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "gaps": {
+ "name": "gaps",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "criterion_score_organization_id_idx": {
+ "name": "criterion_score_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "criterion_score_application_id_idx": {
+ "name": "criterion_score_application_id_idx",
+ "columns": [
+ {
+ "expression": "application_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "criterion_score_app_criterion_idx": {
+ "name": "criterion_score_app_criterion_idx",
+ "columns": [
+ {
+ "expression": "application_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "criterion_key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "criterion_score_organization_id_organization_id_fk": {
+ "name": "criterion_score_organization_id_organization_id_fk",
+ "tableFrom": "criterion_score",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "criterion_score_application_id_application_id_fk": {
+ "name": "criterion_score_application_id_application_id_fk",
+ "tableFrom": "criterion_score",
+ "tableTo": "application",
+ "columnsFrom": [
+ "application_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.document": {
+ "name": "document",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "candidate_id": {
+ "name": "candidate_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "document_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'resume'"
+ },
+ "storage_key": {
+ "name": "storage_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "original_filename": {
+ "name": "original_filename",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "mime_type": {
+ "name": "mime_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "size_bytes": {
+ "name": "size_bytes",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "parsed_content": {
+ "name": "parsed_content",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "document_organization_id_idx": {
+ "name": "document_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "document_candidate_id_idx": {
+ "name": "document_candidate_id_idx",
+ "columns": [
+ {
+ "expression": "candidate_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "document_organization_id_organization_id_fk": {
+ "name": "document_organization_id_organization_id_fk",
+ "tableFrom": "document",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "document_candidate_id_candidate_id_fk": {
+ "name": "document_candidate_id_candidate_id_fk",
+ "tableFrom": "document",
+ "tableTo": "candidate",
+ "columnsFrom": [
+ "candidate_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "document_storage_key_unique": {
+ "name": "document_storage_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "storage_key"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.email_template": {
+ "name": "email_template",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "subject": {
+ "name": "subject",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "body": {
+ "name": "body",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_by_id": {
+ "name": "created_by_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "email_template_organization_id_idx": {
+ "name": "email_template_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "email_template_created_by_id_idx": {
+ "name": "email_template_created_by_id_idx",
+ "columns": [
+ {
+ "expression": "created_by_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "email_template_organization_id_organization_id_fk": {
+ "name": "email_template_organization_id_organization_id_fk",
+ "tableFrom": "email_template",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "email_template_created_by_id_user_id_fk": {
+ "name": "email_template_created_by_id_user_id_fk",
+ "tableFrom": "email_template",
+ "tableTo": "user",
+ "columnsFrom": [
+ "created_by_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.interview": {
+ "name": "interview",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "application_id": {
+ "name": "application_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "interview_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'video'"
+ },
+ "status": {
+ "name": "status",
+ "type": "interview_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'scheduled'"
+ },
+ "scheduled_at": {
+ "name": "scheduled_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "duration": {
+ "name": "duration",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 60
+ },
+ "location": {
+ "name": "location",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "notes": {
+ "name": "notes",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "interviewers": {
+ "name": "interviewers",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_by_id": {
+ "name": "created_by_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "invitation_sent_at": {
+ "name": "invitation_sent_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "candidate_response": {
+ "name": "candidate_response",
+ "type": "candidate_response",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "candidate_responded_at": {
+ "name": "candidate_responded_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "google_calendar_event_id": {
+ "name": "google_calendar_event_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "google_calendar_event_link": {
+ "name": "google_calendar_event_link",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "timezone": {
+ "name": "timezone",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'UTC'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "interview_organization_id_idx": {
+ "name": "interview_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "interview_application_id_idx": {
+ "name": "interview_application_id_idx",
+ "columns": [
+ {
+ "expression": "application_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "interview_scheduled_at_idx": {
+ "name": "interview_scheduled_at_idx",
+ "columns": [
+ {
+ "expression": "scheduled_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "interview_status_idx": {
+ "name": "interview_status_idx",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "interview_created_by_id_idx": {
+ "name": "interview_created_by_id_idx",
+ "columns": [
+ {
+ "expression": "created_by_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "interview_organization_id_organization_id_fk": {
+ "name": "interview_organization_id_organization_id_fk",
+ "tableFrom": "interview",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "interview_application_id_application_id_fk": {
+ "name": "interview_application_id_application_id_fk",
+ "tableFrom": "interview",
+ "tableTo": "application",
+ "columnsFrom": [
+ "application_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "interview_created_by_id_user_id_fk": {
+ "name": "interview_created_by_id_user_id_fk",
+ "tableFrom": "interview",
+ "tableTo": "user",
+ "columnsFrom": [
+ "created_by_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.invite_link": {
+ "name": "invite_link",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_by_id": {
+ "name": "created_by_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "token": {
+ "name": "token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'member'"
+ },
+ "max_uses": {
+ "name": "max_uses",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "use_count": {
+ "name": "use_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "revoked_at": {
+ "name": "revoked_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "invite_link_organization_id_idx": {
+ "name": "invite_link_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "invite_link_token_idx": {
+ "name": "invite_link_token_idx",
+ "columns": [
+ {
+ "expression": "token",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "invite_link_organization_id_organization_id_fk": {
+ "name": "invite_link_organization_id_organization_id_fk",
+ "tableFrom": "invite_link",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "invite_link_created_by_id_user_id_fk": {
+ "name": "invite_link_created_by_id_user_id_fk",
+ "tableFrom": "invite_link",
+ "tableTo": "user",
+ "columnsFrom": [
+ "created_by_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "invite_link_token_unique": {
+ "name": "invite_link_token_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "token"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.job": {
+ "name": "job",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "location": {
+ "name": "location",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "type": {
+ "name": "type",
+ "type": "job_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'full_time'"
+ },
+ "status": {
+ "name": "status",
+ "type": "job_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'draft'"
+ },
+ "salary_min": {
+ "name": "salary_min",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "salary_max": {
+ "name": "salary_max",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "salary_currency": {
+ "name": "salary_currency",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "salary_unit": {
+ "name": "salary_unit",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "remote_status": {
+ "name": "remote_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "valid_through": {
+ "name": "valid_through",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "require_resume": {
+ "name": "require_resume",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "require_cover_letter": {
+ "name": "require_cover_letter",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "auto_score_on_apply": {
+ "name": "auto_score_on_apply",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "job_organization_id_idx": {
+ "name": "job_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "job_organization_id_organization_id_fk": {
+ "name": "job_organization_id_organization_id_fk",
+ "tableFrom": "job",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "job_slug_unique": {
+ "name": "job_slug_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "slug"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.job_question": {
+ "name": "job_question",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "job_id": {
+ "name": "job_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "question_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'short_text'"
+ },
+ "label": {
+ "name": "label",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "required": {
+ "name": "required",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "options": {
+ "name": "options",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "display_order": {
+ "name": "display_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "job_question_organization_id_idx": {
+ "name": "job_question_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "job_question_job_id_idx": {
+ "name": "job_question_job_id_idx",
+ "columns": [
+ {
+ "expression": "job_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "job_question_organization_id_organization_id_fk": {
+ "name": "job_question_organization_id_organization_id_fk",
+ "tableFrom": "job_question",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "job_question_job_id_job_id_fk": {
+ "name": "job_question_job_id_job_id_fk",
+ "tableFrom": "job_question",
+ "tableTo": "job",
+ "columnsFrom": [
+ "job_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.join_request": {
+ "name": "join_request",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "message": {
+ "name": "message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "join_request_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "reviewed_by_id": {
+ "name": "reviewed_by_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "reviewed_at": {
+ "name": "reviewed_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "join_request_organization_id_idx": {
+ "name": "join_request_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "join_request_user_id_idx": {
+ "name": "join_request_user_id_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "join_request_status_idx": {
+ "name": "join_request_status_idx",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "join_request_user_id_user_id_fk": {
+ "name": "join_request_user_id_user_id_fk",
+ "tableFrom": "join_request",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "join_request_organization_id_organization_id_fk": {
+ "name": "join_request_organization_id_organization_id_fk",
+ "tableFrom": "join_request",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "join_request_reviewed_by_id_user_id_fk": {
+ "name": "join_request_reviewed_by_id_user_id_fk",
+ "tableFrom": "join_request",
+ "tableTo": "user",
+ "columnsFrom": [
+ "reviewed_by_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.question_response": {
+ "name": "question_response",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "application_id": {
+ "name": "application_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "question_id": {
+ "name": "question_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "question_response_organization_id_idx": {
+ "name": "question_response_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "question_response_application_id_idx": {
+ "name": "question_response_application_id_idx",
+ "columns": [
+ {
+ "expression": "application_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "question_response_question_id_idx": {
+ "name": "question_response_question_id_idx",
+ "columns": [
+ {
+ "expression": "question_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "question_response_organization_id_organization_id_fk": {
+ "name": "question_response_organization_id_organization_id_fk",
+ "tableFrom": "question_response",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "question_response_application_id_application_id_fk": {
+ "name": "question_response_application_id_application_id_fk",
+ "tableFrom": "question_response",
+ "tableTo": "application",
+ "columnsFrom": [
+ "application_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "question_response_question_id_job_question_id_fk": {
+ "name": "question_response_question_id_job_question_id_fk",
+ "tableFrom": "question_response",
+ "tableTo": "job_question",
+ "columnsFrom": [
+ "question_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.scoring_criterion": {
+ "name": "scoring_criterion",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "job_id": {
+ "name": "job_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "category": {
+ "name": "category",
+ "type": "criterion_category",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'custom'"
+ },
+ "max_score": {
+ "name": "max_score",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 10
+ },
+ "weight": {
+ "name": "weight",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 50
+ },
+ "display_order": {
+ "name": "display_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "scoring_criterion_organization_id_idx": {
+ "name": "scoring_criterion_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "scoring_criterion_job_id_idx": {
+ "name": "scoring_criterion_job_id_idx",
+ "columns": [
+ {
+ "expression": "job_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "scoring_criterion_job_key_idx": {
+ "name": "scoring_criterion_job_key_idx",
+ "columns": [
+ {
+ "expression": "job_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "scoring_criterion_organization_id_organization_id_fk": {
+ "name": "scoring_criterion_organization_id_organization_id_fk",
+ "tableFrom": "scoring_criterion",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "scoring_criterion_job_id_job_id_fk": {
+ "name": "scoring_criterion_job_id_job_id_fk",
+ "tableFrom": "scoring_criterion",
+ "tableTo": "job",
+ "columnsFrom": [
+ "job_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.tracking_link": {
+ "name": "tracking_link",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "job_id": {
+ "name": "job_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "channel": {
+ "name": "channel",
+ "type": "source_channel",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'custom'"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "code": {
+ "name": "code",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "utm_source": {
+ "name": "utm_source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_medium": {
+ "name": "utm_medium",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_campaign": {
+ "name": "utm_campaign",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_term": {
+ "name": "utm_term",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_content": {
+ "name": "utm_content",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "click_count": {
+ "name": "click_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "application_count": {
+ "name": "application_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "is_active": {
+ "name": "is_active",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "created_by_id": {
+ "name": "created_by_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "tracking_link_organization_id_idx": {
+ "name": "tracking_link_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "tracking_link_job_id_idx": {
+ "name": "tracking_link_job_id_idx",
+ "columns": [
+ {
+ "expression": "job_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "tracking_link_code_idx": {
+ "name": "tracking_link_code_idx",
+ "columns": [
+ {
+ "expression": "code",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "tracking_link_channel_idx": {
+ "name": "tracking_link_channel_idx",
+ "columns": [
+ {
+ "expression": "channel",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "tracking_link_organization_id_organization_id_fk": {
+ "name": "tracking_link_organization_id_organization_id_fk",
+ "tableFrom": "tracking_link",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "tracking_link_job_id_job_id_fk": {
+ "name": "tracking_link_job_id_job_id_fk",
+ "tableFrom": "tracking_link",
+ "tableTo": "job",
+ "columnsFrom": [
+ "job_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "tracking_link_created_by_id_user_id_fk": {
+ "name": "tracking_link_created_by_id_user_id_fk",
+ "tableFrom": "tracking_link",
+ "tableTo": "user",
+ "columnsFrom": [
+ "created_by_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "tracking_link_code_unique": {
+ "name": "tracking_link_code_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "code"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.sso_provider": {
+ "name": "sso_provider",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "issuer": {
+ "name": "issuer",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "domain": {
+ "name": "domain",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "oidc_config": {
+ "name": "oidc_config",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "saml_config": {
+ "name": "saml_config",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_id": {
+ "name": "provider_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "sso_provider_domain_idx": {
+ "name": "sso_provider_domain_idx",
+ "columns": [
+ {
+ "expression": "domain",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "sso_provider_provider_id_idx": {
+ "name": "sso_provider_provider_id_idx",
+ "columns": [
+ {
+ "expression": "provider_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "sso_provider_organization_id_idx": {
+ "name": "sso_provider_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "sso_provider_user_id_user_id_fk": {
+ "name": "sso_provider_user_id_user_id_fk",
+ "tableFrom": "sso_provider",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "sso_provider_organization_id_organization_id_fk": {
+ "name": "sso_provider_organization_id_organization_id_fk",
+ "tableFrom": "sso_provider",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.applicant_account": {
+ "name": "applicant_account",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "google_id": {
+ "name": "google_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "image": {
+ "name": "image",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "applicant_account_email_idx": {
+ "name": "applicant_account_email_idx",
+ "columns": [
+ {
+ "expression": "email",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "applicant_account_google_id_idx": {
+ "name": "applicant_account_google_id_idx",
+ "columns": [
+ {
+ "expression": "google_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "applicant_account_google_id_unique": {
+ "name": "applicant_account_google_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "google_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.applicant_portal_token": {
+ "name": "applicant_portal_token",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "token": {
+ "name": "token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "application_id": {
+ "name": "application_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "candidate_id": {
+ "name": "candidate_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "last_accessed_at": {
+ "name": "last_accessed_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "portal_token_application_id_idx": {
+ "name": "portal_token_application_id_idx",
+ "columns": [
+ {
+ "expression": "application_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "portal_token_candidate_id_idx": {
+ "name": "portal_token_candidate_id_idx",
+ "columns": [
+ {
+ "expression": "candidate_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "portal_token_organization_id_idx": {
+ "name": "portal_token_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "portal_token_token_idx": {
+ "name": "portal_token_token_idx",
+ "columns": [
+ {
+ "expression": "token",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "applicant_portal_token_application_id_application_id_fk": {
+ "name": "applicant_portal_token_application_id_application_id_fk",
+ "tableFrom": "applicant_portal_token",
+ "tableTo": "application",
+ "columnsFrom": [
+ "application_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "applicant_portal_token_candidate_id_candidate_id_fk": {
+ "name": "applicant_portal_token_candidate_id_candidate_id_fk",
+ "tableFrom": "applicant_portal_token",
+ "tableTo": "candidate",
+ "columnsFrom": [
+ "candidate_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "applicant_portal_token_organization_id_organization_id_fk": {
+ "name": "applicant_portal_token_organization_id_organization_id_fk",
+ "tableFrom": "applicant_portal_token",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "applicant_portal_token_token_unique": {
+ "name": "applicant_portal_token_token_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "token"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.applicant_session": {
+ "name": "applicant_session",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "applicant_account_id": {
+ "name": "applicant_account_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "token": {
+ "name": "token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "applicant_session_account_id_idx": {
+ "name": "applicant_session_account_id_idx",
+ "columns": [
+ {
+ "expression": "applicant_account_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "applicant_session_token_idx": {
+ "name": "applicant_session_token_idx",
+ "columns": [
+ {
+ "expression": "token",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "applicant_session_applicant_account_id_applicant_account_id_fk": {
+ "name": "applicant_session_applicant_account_id_applicant_account_id_fk",
+ "tableFrom": "applicant_session",
+ "tableTo": "applicant_account",
+ "columnsFrom": [
+ "applicant_account_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "applicant_session_token_unique": {
+ "name": "applicant_session_token_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "token"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ }
+ },
+ "enums": {
+ "public.activity_action": {
+ "name": "activity_action",
+ "schema": "public",
+ "values": [
+ "created",
+ "updated",
+ "deleted",
+ "status_changed",
+ "comment_added",
+ "member_invited",
+ "member_removed",
+ "member_role_changed",
+ "scored"
+ ]
+ },
+ "public.analysis_run_status": {
+ "name": "analysis_run_status",
+ "schema": "public",
+ "values": [
+ "completed",
+ "failed",
+ "partial"
+ ]
+ },
+ "public.application_status": {
+ "name": "application_status",
+ "schema": "public",
+ "values": [
+ "new",
+ "screening",
+ "interview",
+ "offer",
+ "hired",
+ "rejected"
+ ]
+ },
+ "public.calendar_provider": {
+ "name": "calendar_provider",
+ "schema": "public",
+ "values": [
+ "google"
+ ]
+ },
+ "public.candidate_response": {
+ "name": "candidate_response",
+ "schema": "public",
+ "values": [
+ "pending",
+ "accepted",
+ "declined",
+ "tentative"
+ ]
+ },
+ "public.comment_target": {
+ "name": "comment_target",
+ "schema": "public",
+ "values": [
+ "candidate",
+ "application",
+ "job"
+ ]
+ },
+ "public.criterion_category": {
+ "name": "criterion_category",
+ "schema": "public",
+ "values": [
+ "technical",
+ "experience",
+ "soft_skills",
+ "education",
+ "culture",
+ "custom"
+ ]
+ },
+ "public.document_type": {
+ "name": "document_type",
+ "schema": "public",
+ "values": [
+ "resume",
+ "cover_letter",
+ "other"
+ ]
+ },
+ "public.interview_status": {
+ "name": "interview_status",
+ "schema": "public",
+ "values": [
+ "scheduled",
+ "completed",
+ "cancelled",
+ "no_show"
+ ]
+ },
+ "public.interview_type": {
+ "name": "interview_type",
+ "schema": "public",
+ "values": [
+ "phone",
+ "video",
+ "in_person",
+ "panel",
+ "technical",
+ "take_home"
+ ]
+ },
+ "public.job_status": {
+ "name": "job_status",
+ "schema": "public",
+ "values": [
+ "draft",
+ "open",
+ "closed",
+ "archived"
+ ]
+ },
+ "public.job_type": {
+ "name": "job_type",
+ "schema": "public",
+ "values": [
+ "full_time",
+ "part_time",
+ "contract",
+ "internship"
+ ]
+ },
+ "public.join_request_status": {
+ "name": "join_request_status",
+ "schema": "public",
+ "values": [
+ "pending",
+ "approved",
+ "rejected"
+ ]
+ },
+ "public.question_type": {
+ "name": "question_type",
+ "schema": "public",
+ "values": [
+ "short_text",
+ "long_text",
+ "single_select",
+ "multi_select",
+ "number",
+ "date",
+ "url",
+ "checkbox",
+ "file_upload"
+ ]
+ },
+ "public.source_channel": {
+ "name": "source_channel",
+ "schema": "public",
+ "values": [
+ "linkedin",
+ "indeed",
+ "glassdoor",
+ "ziprecruiter",
+ "monster",
+ "handshake",
+ "angellist",
+ "wellfound",
+ "dice",
+ "stackoverflow",
+ "weworkremotely",
+ "remoteok",
+ "builtin",
+ "hired",
+ "lever",
+ "greenhouse_board",
+ "google_jobs",
+ "facebook",
+ "twitter",
+ "instagram",
+ "tiktok",
+ "reddit",
+ "referral",
+ "career_site",
+ "email",
+ "event",
+ "agency",
+ "direct",
+ "other",
+ "custom"
+ ]
+ }
+ },
+ "schemas": {},
+ "sequences": {},
+ "roles": {},
+ "policies": {},
+ "views": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
\ No newline at end of file
diff --git a/server/database/migrations/meta/0022_snapshot.json b/server/database/migrations/meta/0022_snapshot.json
new file mode 100644
index 0000000..df51ea0
--- /dev/null
+++ b/server/database/migrations/meta/0022_snapshot.json
@@ -0,0 +1,4530 @@
+{
+ "id": "37783ea2-ce5b-4afa-859d-82e5a915188d",
+ "prevId": "c3684bb5-9152-4a7d-bb6a-26979662e546",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.account": {
+ "name": "account",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "account_id": {
+ "name": "account_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_id": {
+ "name": "provider_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "access_token": {
+ "name": "access_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "refresh_token": {
+ "name": "refresh_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "id_token": {
+ "name": "id_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "access_token_expires_at": {
+ "name": "access_token_expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "refresh_token_expires_at": {
+ "name": "refresh_token_expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "scope": {
+ "name": "scope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "password": {
+ "name": "password",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "account_user_id_idx": {
+ "name": "account_user_id_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "account_user_id_user_id_fk": {
+ "name": "account_user_id_user_id_fk",
+ "tableFrom": "account",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.invitation": {
+ "name": "invitation",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "inviter_id": {
+ "name": "inviter_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "invitation_organization_id_idx": {
+ "name": "invitation_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "invitation_email_idx": {
+ "name": "invitation_email_idx",
+ "columns": [
+ {
+ "expression": "email",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "invitation_inviter_id_user_id_fk": {
+ "name": "invitation_inviter_id_user_id_fk",
+ "tableFrom": "invitation",
+ "tableTo": "user",
+ "columnsFrom": [
+ "inviter_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "invitation_organization_id_organization_id_fk": {
+ "name": "invitation_organization_id_organization_id_fk",
+ "tableFrom": "invitation",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.member": {
+ "name": "member",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'member'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "member_user_id_idx": {
+ "name": "member_user_id_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "member_organization_id_idx": {
+ "name": "member_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "member_user_org_unique_idx": {
+ "name": "member_user_org_unique_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "member_user_id_user_id_fk": {
+ "name": "member_user_id_user_id_fk",
+ "tableFrom": "member",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "member_organization_id_organization_id_fk": {
+ "name": "member_organization_id_organization_id_fk",
+ "tableFrom": "member",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.organization": {
+ "name": "organization",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "logo": {
+ "name": "logo",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "metadata": {
+ "name": "metadata",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "organization_slug_unique": {
+ "name": "organization_slug_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "slug"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.rate_limit": {
+ "name": "rate_limit",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "count": {
+ "name": "count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "last_request": {
+ "name": "last_request",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "rate_limit_key_idx": {
+ "name": "rate_limit_key_idx",
+ "columns": [
+ {
+ "expression": "key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.session": {
+ "name": "session",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "token": {
+ "name": "token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ip_address": {
+ "name": "ip_address",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_agent": {
+ "name": "user_agent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "active_organization_id": {
+ "name": "active_organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "session_user_id_idx": {
+ "name": "session_user_id_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "session_user_id_user_id_fk": {
+ "name": "session_user_id_user_id_fk",
+ "tableFrom": "session",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "session_token_unique": {
+ "name": "session_token_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "token"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.user": {
+ "name": "user",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email_verified": {
+ "name": "email_verified",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "image": {
+ "name": "image",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "user_email_unique": {
+ "name": "user_email_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "email"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.verification": {
+ "name": "verification",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "identifier": {
+ "name": "identifier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "verification_identifier_idx": {
+ "name": "verification_identifier_idx",
+ "columns": [
+ {
+ "expression": "identifier",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.activity_log": {
+ "name": "activity_log",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "actor_id": {
+ "name": "actor_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "action": {
+ "name": "action",
+ "type": "activity_action",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "resource_type": {
+ "name": "resource_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "resource_id": {
+ "name": "resource_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "metadata": {
+ "name": "metadata",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "activity_log_organization_id_idx": {
+ "name": "activity_log_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "activity_log_actor_id_idx": {
+ "name": "activity_log_actor_id_idx",
+ "columns": [
+ {
+ "expression": "actor_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "activity_log_resource_idx": {
+ "name": "activity_log_resource_idx",
+ "columns": [
+ {
+ "expression": "resource_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "resource_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "activity_log_created_at_idx": {
+ "name": "activity_log_created_at_idx",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "activity_log_organization_id_organization_id_fk": {
+ "name": "activity_log_organization_id_organization_id_fk",
+ "tableFrom": "activity_log",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "activity_log_actor_id_user_id_fk": {
+ "name": "activity_log_actor_id_user_id_fk",
+ "tableFrom": "activity_log",
+ "tableTo": "user",
+ "columnsFrom": [
+ "actor_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.ai_config": {
+ "name": "ai_config",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'openai'"
+ },
+ "model": {
+ "name": "model",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'gpt-4o-mini'"
+ },
+ "api_key_encrypted": {
+ "name": "api_key_encrypted",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "base_url": {
+ "name": "base_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "max_tokens": {
+ "name": "max_tokens",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 4096
+ },
+ "input_price_per_1m": {
+ "name": "input_price_per_1m",
+ "type": "numeric(10, 4)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "output_price_per_1m": {
+ "name": "output_price_per_1m",
+ "type": "numeric(10, 4)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "ai_config_organization_id_idx": {
+ "name": "ai_config_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "ai_config_organization_id_organization_id_fk": {
+ "name": "ai_config_organization_id_organization_id_fk",
+ "tableFrom": "ai_config",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.analysis_run": {
+ "name": "analysis_run",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "application_id": {
+ "name": "application_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "analysis_run_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'completed'"
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "model": {
+ "name": "model",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "criteria_snapshot": {
+ "name": "criteria_snapshot",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "composite_score": {
+ "name": "composite_score",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "prompt_tokens": {
+ "name": "prompt_tokens",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "completion_tokens": {
+ "name": "completion_tokens",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "raw_response": {
+ "name": "raw_response",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "error_message": {
+ "name": "error_message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "scored_by_id": {
+ "name": "scored_by_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "analysis_run_organization_id_idx": {
+ "name": "analysis_run_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "analysis_run_application_id_idx": {
+ "name": "analysis_run_application_id_idx",
+ "columns": [
+ {
+ "expression": "application_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "analysis_run_created_at_idx": {
+ "name": "analysis_run_created_at_idx",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "analysis_run_organization_id_organization_id_fk": {
+ "name": "analysis_run_organization_id_organization_id_fk",
+ "tableFrom": "analysis_run",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "analysis_run_application_id_application_id_fk": {
+ "name": "analysis_run_application_id_application_id_fk",
+ "tableFrom": "analysis_run",
+ "tableTo": "application",
+ "columnsFrom": [
+ "application_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "analysis_run_scored_by_id_user_id_fk": {
+ "name": "analysis_run_scored_by_id_user_id_fk",
+ "tableFrom": "analysis_run",
+ "tableTo": "user",
+ "columnsFrom": [
+ "scored_by_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.application": {
+ "name": "application",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "candidate_id": {
+ "name": "candidate_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "job_id": {
+ "name": "job_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "application_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'new'"
+ },
+ "score": {
+ "name": "score",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "notes": {
+ "name": "notes",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cover_letter_text": {
+ "name": "cover_letter_text",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "viewed_at": {
+ "name": "viewed_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "viewed_by": {
+ "name": "viewed_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "application_organization_id_idx": {
+ "name": "application_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "application_candidate_id_idx": {
+ "name": "application_candidate_id_idx",
+ "columns": [
+ {
+ "expression": "candidate_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "application_job_id_idx": {
+ "name": "application_job_id_idx",
+ "columns": [
+ {
+ "expression": "job_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "application_org_candidate_job_idx": {
+ "name": "application_org_candidate_job_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "candidate_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "job_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "application_organization_id_organization_id_fk": {
+ "name": "application_organization_id_organization_id_fk",
+ "tableFrom": "application",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "application_candidate_id_candidate_id_fk": {
+ "name": "application_candidate_id_candidate_id_fk",
+ "tableFrom": "application",
+ "tableTo": "candidate",
+ "columnsFrom": [
+ "candidate_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "application_job_id_job_id_fk": {
+ "name": "application_job_id_job_id_fk",
+ "tableFrom": "application",
+ "tableTo": "job",
+ "columnsFrom": [
+ "job_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "application_viewed_by_user_id_fk": {
+ "name": "application_viewed_by_user_id_fk",
+ "tableFrom": "application",
+ "tableTo": "user",
+ "columnsFrom": [
+ "viewed_by"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.application_source": {
+ "name": "application_source",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "application_id": {
+ "name": "application_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel": {
+ "name": "channel",
+ "type": "source_channel",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'direct'"
+ },
+ "tracking_link_id": {
+ "name": "tracking_link_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_source": {
+ "name": "utm_source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_medium": {
+ "name": "utm_medium",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_campaign": {
+ "name": "utm_campaign",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_term": {
+ "name": "utm_term",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_content": {
+ "name": "utm_content",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "referrer_domain": {
+ "name": "referrer_domain",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "application_source_organization_id_idx": {
+ "name": "application_source_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "application_source_application_id_idx": {
+ "name": "application_source_application_id_idx",
+ "columns": [
+ {
+ "expression": "application_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "application_source_channel_idx": {
+ "name": "application_source_channel_idx",
+ "columns": [
+ {
+ "expression": "channel",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "application_source_tracking_link_id_idx": {
+ "name": "application_source_tracking_link_id_idx",
+ "columns": [
+ {
+ "expression": "tracking_link_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "application_source_application_idx": {
+ "name": "application_source_application_idx",
+ "columns": [
+ {
+ "expression": "application_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "application_source_organization_id_organization_id_fk": {
+ "name": "application_source_organization_id_organization_id_fk",
+ "tableFrom": "application_source",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "application_source_application_id_application_id_fk": {
+ "name": "application_source_application_id_application_id_fk",
+ "tableFrom": "application_source",
+ "tableTo": "application",
+ "columnsFrom": [
+ "application_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "application_source_tracking_link_id_tracking_link_id_fk": {
+ "name": "application_source_tracking_link_id_tracking_link_id_fk",
+ "tableFrom": "application_source",
+ "tableTo": "tracking_link",
+ "columnsFrom": [
+ "tracking_link_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.calendar_integration": {
+ "name": "calendar_integration",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider": {
+ "name": "provider",
+ "type": "calendar_provider",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'google'"
+ },
+ "access_token_encrypted": {
+ "name": "access_token_encrypted",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "refresh_token_encrypted": {
+ "name": "refresh_token_encrypted",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "calendar_id": {
+ "name": "calendar_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'primary'"
+ },
+ "account_email": {
+ "name": "account_email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "webhook_channel_id": {
+ "name": "webhook_channel_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "webhook_resource_id": {
+ "name": "webhook_resource_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "webhook_expiration": {
+ "name": "webhook_expiration",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "sync_token": {
+ "name": "sync_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "calendar_integration_user_provider_idx": {
+ "name": "calendar_integration_user_provider_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "provider",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "calendar_integration_webhook_channel_idx": {
+ "name": "calendar_integration_webhook_channel_idx",
+ "columns": [
+ {
+ "expression": "webhook_channel_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "calendar_integration_user_id_user_id_fk": {
+ "name": "calendar_integration_user_id_user_id_fk",
+ "tableFrom": "calendar_integration",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.candidate": {
+ "name": "candidate",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "first_name": {
+ "name": "first_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "last_name": {
+ "name": "last_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "phone": {
+ "name": "phone",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "candidate_organization_id_idx": {
+ "name": "candidate_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "candidate_org_email_idx": {
+ "name": "candidate_org_email_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "email",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "candidate_organization_id_organization_id_fk": {
+ "name": "candidate_organization_id_organization_id_fk",
+ "tableFrom": "candidate",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.comment": {
+ "name": "comment",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "author_id": {
+ "name": "author_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_type": {
+ "name": "target_type",
+ "type": "comment_target",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_id": {
+ "name": "target_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "body": {
+ "name": "body",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "comment_organization_id_idx": {
+ "name": "comment_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "comment_target_idx": {
+ "name": "comment_target_idx",
+ "columns": [
+ {
+ "expression": "target_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "target_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "comment_author_id_idx": {
+ "name": "comment_author_id_idx",
+ "columns": [
+ {
+ "expression": "author_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "comment_organization_id_organization_id_fk": {
+ "name": "comment_organization_id_organization_id_fk",
+ "tableFrom": "comment",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "comment_author_id_user_id_fk": {
+ "name": "comment_author_id_user_id_fk",
+ "tableFrom": "comment",
+ "tableTo": "user",
+ "columnsFrom": [
+ "author_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.criterion_score": {
+ "name": "criterion_score",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "application_id": {
+ "name": "application_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "criterion_key": {
+ "name": "criterion_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "max_score": {
+ "name": "max_score",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "applicant_score": {
+ "name": "applicant_score",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "confidence": {
+ "name": "confidence",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "evidence": {
+ "name": "evidence",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "strengths": {
+ "name": "strengths",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "gaps": {
+ "name": "gaps",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "criterion_score_organization_id_idx": {
+ "name": "criterion_score_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "criterion_score_application_id_idx": {
+ "name": "criterion_score_application_id_idx",
+ "columns": [
+ {
+ "expression": "application_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "criterion_score_app_criterion_idx": {
+ "name": "criterion_score_app_criterion_idx",
+ "columns": [
+ {
+ "expression": "application_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "criterion_key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "criterion_score_organization_id_organization_id_fk": {
+ "name": "criterion_score_organization_id_organization_id_fk",
+ "tableFrom": "criterion_score",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "criterion_score_application_id_application_id_fk": {
+ "name": "criterion_score_application_id_application_id_fk",
+ "tableFrom": "criterion_score",
+ "tableTo": "application",
+ "columnsFrom": [
+ "application_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.document": {
+ "name": "document",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "candidate_id": {
+ "name": "candidate_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "document_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'resume'"
+ },
+ "storage_key": {
+ "name": "storage_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "original_filename": {
+ "name": "original_filename",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "mime_type": {
+ "name": "mime_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "size_bytes": {
+ "name": "size_bytes",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "parsed_content": {
+ "name": "parsed_content",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "document_organization_id_idx": {
+ "name": "document_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "document_candidate_id_idx": {
+ "name": "document_candidate_id_idx",
+ "columns": [
+ {
+ "expression": "candidate_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "document_organization_id_organization_id_fk": {
+ "name": "document_organization_id_organization_id_fk",
+ "tableFrom": "document",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "document_candidate_id_candidate_id_fk": {
+ "name": "document_candidate_id_candidate_id_fk",
+ "tableFrom": "document",
+ "tableTo": "candidate",
+ "columnsFrom": [
+ "candidate_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "document_storage_key_unique": {
+ "name": "document_storage_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "storage_key"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.email_template": {
+ "name": "email_template",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "subject": {
+ "name": "subject",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "body": {
+ "name": "body",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_by_id": {
+ "name": "created_by_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "email_template_organization_id_idx": {
+ "name": "email_template_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "email_template_created_by_id_idx": {
+ "name": "email_template_created_by_id_idx",
+ "columns": [
+ {
+ "expression": "created_by_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "email_template_organization_id_organization_id_fk": {
+ "name": "email_template_organization_id_organization_id_fk",
+ "tableFrom": "email_template",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "email_template_created_by_id_user_id_fk": {
+ "name": "email_template_created_by_id_user_id_fk",
+ "tableFrom": "email_template",
+ "tableTo": "user",
+ "columnsFrom": [
+ "created_by_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.interview": {
+ "name": "interview",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "application_id": {
+ "name": "application_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "interview_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'video'"
+ },
+ "status": {
+ "name": "status",
+ "type": "interview_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'scheduled'"
+ },
+ "scheduled_at": {
+ "name": "scheduled_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "duration": {
+ "name": "duration",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 60
+ },
+ "location": {
+ "name": "location",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "notes": {
+ "name": "notes",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "interviewers": {
+ "name": "interviewers",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_by_id": {
+ "name": "created_by_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "invitation_sent_at": {
+ "name": "invitation_sent_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "candidate_response": {
+ "name": "candidate_response",
+ "type": "candidate_response",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "candidate_responded_at": {
+ "name": "candidate_responded_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "google_calendar_event_id": {
+ "name": "google_calendar_event_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "google_calendar_event_link": {
+ "name": "google_calendar_event_link",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "timezone": {
+ "name": "timezone",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'UTC'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "interview_organization_id_idx": {
+ "name": "interview_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "interview_application_id_idx": {
+ "name": "interview_application_id_idx",
+ "columns": [
+ {
+ "expression": "application_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "interview_scheduled_at_idx": {
+ "name": "interview_scheduled_at_idx",
+ "columns": [
+ {
+ "expression": "scheduled_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "interview_status_idx": {
+ "name": "interview_status_idx",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "interview_created_by_id_idx": {
+ "name": "interview_created_by_id_idx",
+ "columns": [
+ {
+ "expression": "created_by_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "interview_organization_id_organization_id_fk": {
+ "name": "interview_organization_id_organization_id_fk",
+ "tableFrom": "interview",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "interview_application_id_application_id_fk": {
+ "name": "interview_application_id_application_id_fk",
+ "tableFrom": "interview",
+ "tableTo": "application",
+ "columnsFrom": [
+ "application_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "interview_created_by_id_user_id_fk": {
+ "name": "interview_created_by_id_user_id_fk",
+ "tableFrom": "interview",
+ "tableTo": "user",
+ "columnsFrom": [
+ "created_by_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.invite_link": {
+ "name": "invite_link",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_by_id": {
+ "name": "created_by_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "token": {
+ "name": "token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'member'"
+ },
+ "max_uses": {
+ "name": "max_uses",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "use_count": {
+ "name": "use_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "revoked_at": {
+ "name": "revoked_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "invite_link_organization_id_idx": {
+ "name": "invite_link_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "invite_link_token_idx": {
+ "name": "invite_link_token_idx",
+ "columns": [
+ {
+ "expression": "token",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "invite_link_organization_id_organization_id_fk": {
+ "name": "invite_link_organization_id_organization_id_fk",
+ "tableFrom": "invite_link",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "invite_link_created_by_id_user_id_fk": {
+ "name": "invite_link_created_by_id_user_id_fk",
+ "tableFrom": "invite_link",
+ "tableTo": "user",
+ "columnsFrom": [
+ "created_by_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "invite_link_token_unique": {
+ "name": "invite_link_token_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "token"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.job": {
+ "name": "job",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "location": {
+ "name": "location",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "type": {
+ "name": "type",
+ "type": "job_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'full_time'"
+ },
+ "status": {
+ "name": "status",
+ "type": "job_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'draft'"
+ },
+ "salary_min": {
+ "name": "salary_min",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "salary_max": {
+ "name": "salary_max",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "salary_currency": {
+ "name": "salary_currency",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "salary_unit": {
+ "name": "salary_unit",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "remote_status": {
+ "name": "remote_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "valid_through": {
+ "name": "valid_through",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "require_resume": {
+ "name": "require_resume",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "require_cover_letter": {
+ "name": "require_cover_letter",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "auto_score_on_apply": {
+ "name": "auto_score_on_apply",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "job_organization_id_idx": {
+ "name": "job_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "job_organization_id_organization_id_fk": {
+ "name": "job_organization_id_organization_id_fk",
+ "tableFrom": "job",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "job_slug_unique": {
+ "name": "job_slug_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "slug"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.job_question": {
+ "name": "job_question",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "job_id": {
+ "name": "job_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "question_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'short_text'"
+ },
+ "label": {
+ "name": "label",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "required": {
+ "name": "required",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "options": {
+ "name": "options",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "display_order": {
+ "name": "display_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "job_question_organization_id_idx": {
+ "name": "job_question_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "job_question_job_id_idx": {
+ "name": "job_question_job_id_idx",
+ "columns": [
+ {
+ "expression": "job_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "job_question_organization_id_organization_id_fk": {
+ "name": "job_question_organization_id_organization_id_fk",
+ "tableFrom": "job_question",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "job_question_job_id_job_id_fk": {
+ "name": "job_question_job_id_job_id_fk",
+ "tableFrom": "job_question",
+ "tableTo": "job",
+ "columnsFrom": [
+ "job_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.join_request": {
+ "name": "join_request",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "message": {
+ "name": "message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "join_request_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "reviewed_by_id": {
+ "name": "reviewed_by_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "reviewed_at": {
+ "name": "reviewed_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "join_request_organization_id_idx": {
+ "name": "join_request_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "join_request_user_id_idx": {
+ "name": "join_request_user_id_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "join_request_status_idx": {
+ "name": "join_request_status_idx",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "join_request_user_id_user_id_fk": {
+ "name": "join_request_user_id_user_id_fk",
+ "tableFrom": "join_request",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "join_request_organization_id_organization_id_fk": {
+ "name": "join_request_organization_id_organization_id_fk",
+ "tableFrom": "join_request",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "join_request_reviewed_by_id_user_id_fk": {
+ "name": "join_request_reviewed_by_id_user_id_fk",
+ "tableFrom": "join_request",
+ "tableTo": "user",
+ "columnsFrom": [
+ "reviewed_by_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.question_response": {
+ "name": "question_response",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "application_id": {
+ "name": "application_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "question_id": {
+ "name": "question_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "question_response_organization_id_idx": {
+ "name": "question_response_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "question_response_application_id_idx": {
+ "name": "question_response_application_id_idx",
+ "columns": [
+ {
+ "expression": "application_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "question_response_question_id_idx": {
+ "name": "question_response_question_id_idx",
+ "columns": [
+ {
+ "expression": "question_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "question_response_organization_id_organization_id_fk": {
+ "name": "question_response_organization_id_organization_id_fk",
+ "tableFrom": "question_response",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "question_response_application_id_application_id_fk": {
+ "name": "question_response_application_id_application_id_fk",
+ "tableFrom": "question_response",
+ "tableTo": "application",
+ "columnsFrom": [
+ "application_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "question_response_question_id_job_question_id_fk": {
+ "name": "question_response_question_id_job_question_id_fk",
+ "tableFrom": "question_response",
+ "tableTo": "job_question",
+ "columnsFrom": [
+ "question_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.scoring_criterion": {
+ "name": "scoring_criterion",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "job_id": {
+ "name": "job_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "category": {
+ "name": "category",
+ "type": "criterion_category",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'custom'"
+ },
+ "max_score": {
+ "name": "max_score",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 10
+ },
+ "weight": {
+ "name": "weight",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 50
+ },
+ "display_order": {
+ "name": "display_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "scoring_criterion_organization_id_idx": {
+ "name": "scoring_criterion_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "scoring_criterion_job_id_idx": {
+ "name": "scoring_criterion_job_id_idx",
+ "columns": [
+ {
+ "expression": "job_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "scoring_criterion_job_key_idx": {
+ "name": "scoring_criterion_job_key_idx",
+ "columns": [
+ {
+ "expression": "job_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "scoring_criterion_organization_id_organization_id_fk": {
+ "name": "scoring_criterion_organization_id_organization_id_fk",
+ "tableFrom": "scoring_criterion",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "scoring_criterion_job_id_job_id_fk": {
+ "name": "scoring_criterion_job_id_job_id_fk",
+ "tableFrom": "scoring_criterion",
+ "tableTo": "job",
+ "columnsFrom": [
+ "job_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.tracking_link": {
+ "name": "tracking_link",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "job_id": {
+ "name": "job_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "channel": {
+ "name": "channel",
+ "type": "source_channel",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'custom'"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "code": {
+ "name": "code",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "utm_source": {
+ "name": "utm_source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_medium": {
+ "name": "utm_medium",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_campaign": {
+ "name": "utm_campaign",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_term": {
+ "name": "utm_term",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_content": {
+ "name": "utm_content",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "click_count": {
+ "name": "click_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "application_count": {
+ "name": "application_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "is_active": {
+ "name": "is_active",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "created_by_id": {
+ "name": "created_by_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "tracking_link_organization_id_idx": {
+ "name": "tracking_link_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "tracking_link_job_id_idx": {
+ "name": "tracking_link_job_id_idx",
+ "columns": [
+ {
+ "expression": "job_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "tracking_link_code_idx": {
+ "name": "tracking_link_code_idx",
+ "columns": [
+ {
+ "expression": "code",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "tracking_link_channel_idx": {
+ "name": "tracking_link_channel_idx",
+ "columns": [
+ {
+ "expression": "channel",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "tracking_link_organization_id_organization_id_fk": {
+ "name": "tracking_link_organization_id_organization_id_fk",
+ "tableFrom": "tracking_link",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "tracking_link_job_id_job_id_fk": {
+ "name": "tracking_link_job_id_job_id_fk",
+ "tableFrom": "tracking_link",
+ "tableTo": "job",
+ "columnsFrom": [
+ "job_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "tracking_link_created_by_id_user_id_fk": {
+ "name": "tracking_link_created_by_id_user_id_fk",
+ "tableFrom": "tracking_link",
+ "tableTo": "user",
+ "columnsFrom": [
+ "created_by_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "tracking_link_code_unique": {
+ "name": "tracking_link_code_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "code"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.sso_provider": {
+ "name": "sso_provider",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "issuer": {
+ "name": "issuer",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "domain": {
+ "name": "domain",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "oidc_config": {
+ "name": "oidc_config",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "saml_config": {
+ "name": "saml_config",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_id": {
+ "name": "provider_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "sso_provider_domain_idx": {
+ "name": "sso_provider_domain_idx",
+ "columns": [
+ {
+ "expression": "domain",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "sso_provider_provider_id_idx": {
+ "name": "sso_provider_provider_id_idx",
+ "columns": [
+ {
+ "expression": "provider_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "sso_provider_organization_id_idx": {
+ "name": "sso_provider_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "sso_provider_user_id_user_id_fk": {
+ "name": "sso_provider_user_id_user_id_fk",
+ "tableFrom": "sso_provider",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "sso_provider_organization_id_organization_id_fk": {
+ "name": "sso_provider_organization_id_organization_id_fk",
+ "tableFrom": "sso_provider",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.applicant_account": {
+ "name": "applicant_account",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "google_id": {
+ "name": "google_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "image": {
+ "name": "image",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "applicant_account_email_idx": {
+ "name": "applicant_account_email_idx",
+ "columns": [
+ {
+ "expression": "email",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "applicant_account_google_id_idx": {
+ "name": "applicant_account_google_id_idx",
+ "columns": [
+ {
+ "expression": "google_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "applicant_account_google_id_unique": {
+ "name": "applicant_account_google_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "google_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.applicant_portal_token": {
+ "name": "applicant_portal_token",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "token": {
+ "name": "token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "application_id": {
+ "name": "application_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "candidate_id": {
+ "name": "candidate_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "last_accessed_at": {
+ "name": "last_accessed_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "portal_token_application_id_idx": {
+ "name": "portal_token_application_id_idx",
+ "columns": [
+ {
+ "expression": "application_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "portal_token_candidate_id_idx": {
+ "name": "portal_token_candidate_id_idx",
+ "columns": [
+ {
+ "expression": "candidate_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "portal_token_organization_id_idx": {
+ "name": "portal_token_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "portal_token_token_idx": {
+ "name": "portal_token_token_idx",
+ "columns": [
+ {
+ "expression": "token",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "applicant_portal_token_application_id_application_id_fk": {
+ "name": "applicant_portal_token_application_id_application_id_fk",
+ "tableFrom": "applicant_portal_token",
+ "tableTo": "application",
+ "columnsFrom": [
+ "application_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "applicant_portal_token_candidate_id_candidate_id_fk": {
+ "name": "applicant_portal_token_candidate_id_candidate_id_fk",
+ "tableFrom": "applicant_portal_token",
+ "tableTo": "candidate",
+ "columnsFrom": [
+ "candidate_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "applicant_portal_token_organization_id_organization_id_fk": {
+ "name": "applicant_portal_token_organization_id_organization_id_fk",
+ "tableFrom": "applicant_portal_token",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "applicant_portal_token_token_unique": {
+ "name": "applicant_portal_token_token_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "token"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.applicant_session": {
+ "name": "applicant_session",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "applicant_account_id": {
+ "name": "applicant_account_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "token": {
+ "name": "token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "applicant_session_account_id_idx": {
+ "name": "applicant_session_account_id_idx",
+ "columns": [
+ {
+ "expression": "applicant_account_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "applicant_session_token_idx": {
+ "name": "applicant_session_token_idx",
+ "columns": [
+ {
+ "expression": "token",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "applicant_session_applicant_account_id_applicant_account_id_fk": {
+ "name": "applicant_session_applicant_account_id_applicant_account_id_fk",
+ "tableFrom": "applicant_session",
+ "tableTo": "applicant_account",
+ "columnsFrom": [
+ "applicant_account_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "applicant_session_token_unique": {
+ "name": "applicant_session_token_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "token"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ }
+ },
+ "enums": {
+ "public.activity_action": {
+ "name": "activity_action",
+ "schema": "public",
+ "values": [
+ "created",
+ "updated",
+ "deleted",
+ "status_changed",
+ "comment_added",
+ "member_invited",
+ "member_removed",
+ "member_role_changed",
+ "scored"
+ ]
+ },
+ "public.analysis_run_status": {
+ "name": "analysis_run_status",
+ "schema": "public",
+ "values": [
+ "completed",
+ "failed",
+ "partial"
+ ]
+ },
+ "public.application_status": {
+ "name": "application_status",
+ "schema": "public",
+ "values": [
+ "new",
+ "screening",
+ "interview",
+ "offer",
+ "hired",
+ "rejected"
+ ]
+ },
+ "public.calendar_provider": {
+ "name": "calendar_provider",
+ "schema": "public",
+ "values": [
+ "google"
+ ]
+ },
+ "public.candidate_response": {
+ "name": "candidate_response",
+ "schema": "public",
+ "values": [
+ "pending",
+ "accepted",
+ "declined",
+ "tentative"
+ ]
+ },
+ "public.comment_target": {
+ "name": "comment_target",
+ "schema": "public",
+ "values": [
+ "candidate",
+ "application",
+ "job"
+ ]
+ },
+ "public.criterion_category": {
+ "name": "criterion_category",
+ "schema": "public",
+ "values": [
+ "technical",
+ "experience",
+ "soft_skills",
+ "education",
+ "culture",
+ "custom"
+ ]
+ },
+ "public.document_type": {
+ "name": "document_type",
+ "schema": "public",
+ "values": [
+ "resume",
+ "cover_letter",
+ "other"
+ ]
+ },
+ "public.interview_status": {
+ "name": "interview_status",
+ "schema": "public",
+ "values": [
+ "scheduled",
+ "completed",
+ "cancelled",
+ "no_show"
+ ]
+ },
+ "public.interview_type": {
+ "name": "interview_type",
+ "schema": "public",
+ "values": [
+ "phone",
+ "video",
+ "in_person",
+ "panel",
+ "technical",
+ "take_home"
+ ]
+ },
+ "public.job_status": {
+ "name": "job_status",
+ "schema": "public",
+ "values": [
+ "draft",
+ "open",
+ "closed",
+ "archived"
+ ]
+ },
+ "public.job_type": {
+ "name": "job_type",
+ "schema": "public",
+ "values": [
+ "full_time",
+ "part_time",
+ "contract",
+ "internship"
+ ]
+ },
+ "public.join_request_status": {
+ "name": "join_request_status",
+ "schema": "public",
+ "values": [
+ "pending",
+ "approved",
+ "rejected"
+ ]
+ },
+ "public.question_type": {
+ "name": "question_type",
+ "schema": "public",
+ "values": [
+ "short_text",
+ "long_text",
+ "single_select",
+ "multi_select",
+ "number",
+ "date",
+ "url",
+ "checkbox",
+ "file_upload"
+ ]
+ },
+ "public.source_channel": {
+ "name": "source_channel",
+ "schema": "public",
+ "values": [
+ "linkedin",
+ "indeed",
+ "glassdoor",
+ "ziprecruiter",
+ "monster",
+ "handshake",
+ "angellist",
+ "wellfound",
+ "dice",
+ "stackoverflow",
+ "weworkremotely",
+ "remoteok",
+ "builtin",
+ "hired",
+ "lever",
+ "greenhouse_board",
+ "google_jobs",
+ "facebook",
+ "twitter",
+ "instagram",
+ "tiktok",
+ "reddit",
+ "referral",
+ "career_site",
+ "email",
+ "event",
+ "agency",
+ "direct",
+ "other",
+ "custom"
+ ]
+ }
+ },
+ "schemas": {},
+ "sequences": {},
+ "roles": {},
+ "policies": {},
+ "views": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
\ No newline at end of file
diff --git a/server/database/migrations/meta/_journal.json b/server/database/migrations/meta/_journal.json
index c88691e..3226e84 100644
--- a/server/database/migrations/meta/_journal.json
+++ b/server/database/migrations/meta/_journal.json
@@ -148,6 +148,20 @@
"when": 1776076881696,
"tag": "0020_goofy_grey_gargoyle",
"breakpoints": true
+ },
+ {
+ "idx": 21,
+ "version": "7",
+ "when": 1776080752775,
+ "tag": "0021_blushing_thing",
+ "breakpoints": true
+ },
+ {
+ "idx": 22,
+ "version": "7",
+ "when": 1776082244843,
+ "tag": "0022_mute_green_goblin",
+ "breakpoints": true
}
]
}
\ No newline at end of file
diff --git a/server/database/schema/app.ts b/server/database/schema/app.ts
index 509ce5b..f52990e 100644
--- a/server/database/schema/app.ts
+++ b/server/database/schema/app.ts
@@ -92,6 +92,8 @@ export const application = pgTable('application', {
score: integer('score'),
notes: text('notes'),
coverLetterText: text('cover_letter_text'),
+ viewedAt: timestamp('viewed_at'),
+ viewedBy: text('viewed_by').references(() => user.id, { onDelete: 'set null' }),
createdAt: timestamp('created_at').notNull().defaultNow(),
updatedAt: timestamp('updated_at').notNull().defaultNow(),
}, (t) => ([
diff --git a/server/database/schema/index.ts b/server/database/schema/index.ts
index aee8d3e..ada9694 100644
--- a/server/database/schema/index.ts
+++ b/server/database/schema/index.ts
@@ -1,3 +1,4 @@
export * from './auth'
export * from './app'
export * from './sso'
+export * from './portal'
diff --git a/server/database/schema/portal.ts b/server/database/schema/portal.ts
new file mode 100644
index 0000000..9a49a9e
--- /dev/null
+++ b/server/database/schema/portal.ts
@@ -0,0 +1,87 @@
+import {
+ pgTable,
+ text,
+ timestamp,
+ index,
+ uniqueIndex,
+} from 'drizzle-orm/pg-core'
+import { relations } from 'drizzle-orm'
+import { organization } from './auth'
+import { application, candidate } from './app'
+
+// ─────────────────────────────────────────────
+// Applicant Portal — Token-based + OAuth Access
+// ─────────────────────────────────────────────
+
+/**
+ * Secure portal access tokens. Generated when an application is submitted.
+ * Each token grants read-only access to a single application's dashboard.
+ * Token is a 64-char cryptographic hex string — NOT the primary key.
+ */
+export const applicantPortalToken = pgTable('applicant_portal_token', {
+ id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),
+ token: text('token').notNull().unique(),
+ applicationId: text('application_id').notNull().references(() => application.id, { onDelete: 'cascade' }),
+ candidateId: text('candidate_id').notNull().references(() => candidate.id, { onDelete: 'cascade' }),
+ organizationId: text('organization_id').notNull().references(() => organization.id, { onDelete: 'cascade' }),
+ /** Token expires after 90 days by default */
+ expiresAt: timestamp('expires_at').notNull(),
+ lastAccessedAt: timestamp('last_accessed_at'),
+ createdAt: timestamp('created_at').notNull().defaultNow(),
+}, (t) => ([
+ index('portal_token_application_id_idx').on(t.applicationId),
+ index('portal_token_candidate_id_idx').on(t.candidateId),
+ index('portal_token_organization_id_idx').on(t.organizationId),
+ index('portal_token_token_idx').on(t.token),
+]))
+
+/**
+ * Applicant portal accounts — separate from recruiter users.
+ * Created when an applicant signs in with Google to save their dashboard.
+ * Linked to candidate records via email matching.
+ */
+export const applicantAccount = pgTable('applicant_account', {
+ id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),
+ googleId: text('google_id').notNull().unique(),
+ email: text('email').notNull(),
+ name: text('name'),
+ image: text('image'),
+ createdAt: timestamp('created_at').notNull().defaultNow(),
+ updatedAt: timestamp('updated_at').notNull().defaultNow(),
+}, (t) => ([
+ index('applicant_account_email_idx').on(t.email),
+ uniqueIndex('applicant_account_google_id_idx').on(t.googleId),
+]))
+
+/**
+ * Applicant portal sessions — lightweight session management for portal access.
+ * Separate from Better Auth sessions (which are for recruiters).
+ */
+export const applicantSession = pgTable('applicant_session', {
+ id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),
+ applicantAccountId: text('applicant_account_id').notNull().references(() => applicantAccount.id, { onDelete: 'cascade' }),
+ token: text('token').notNull().unique(),
+ expiresAt: timestamp('expires_at').notNull(),
+ createdAt: timestamp('created_at').notNull().defaultNow(),
+}, (t) => ([
+ index('applicant_session_account_id_idx').on(t.applicantAccountId),
+ index('applicant_session_token_idx').on(t.token),
+]))
+
+// ─────────────────────────────────────────────
+// Relations
+// ─────────────────────────────────────────────
+
+export const applicantPortalTokenRelations = relations(applicantPortalToken, ({ one }) => ({
+ application: one(application, { fields: [applicantPortalToken.applicationId], references: [application.id] }),
+ candidate: one(candidate, { fields: [applicantPortalToken.candidateId], references: [candidate.id] }),
+ organization: one(organization, { fields: [applicantPortalToken.organizationId], references: [organization.id] }),
+}))
+
+export const applicantAccountRelations = relations(applicantAccount, ({ many }) => ({
+ sessions: many(applicantSession),
+}))
+
+export const applicantSessionRelations = relations(applicantSession, ({ one }) => ({
+ account: one(applicantAccount, { fields: [applicantSession.applicantAccountId], references: [applicantAccount.id] }),
+}))
diff --git a/server/utils/portal-auth.ts b/server/utils/portal-auth.ts
new file mode 100644
index 0000000..33fa7c6
--- /dev/null
+++ b/server/utils/portal-auth.ts
@@ -0,0 +1,150 @@
+import { eq, and } from 'drizzle-orm'
+import { applicantPortalToken, applicantSession, applicantAccount } from '../database/schema'
+
+const TOKEN_EXPIRY_DAYS = 90
+const SESSION_EXPIRY_HOURS = 72
+
+/**
+ * Generate a cryptographically secure portal access token.
+ * Returns a 64-character hex string.
+ */
+export function generatePortalToken(): string {
+ const bytes = new Uint8Array(32)
+ crypto.getRandomValues(bytes)
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, '0')).join('')
+}
+
+/**
+ * Create a portal token for a given application.
+ * Called during the apply flow after a successful submission.
+ */
+export async function createPortalToken(
+ applicationId: string,
+ candidateId: string,
+ organizationId: string,
+): Promise {
+ const token = generatePortalToken()
+ const expiresAt = new Date()
+ expiresAt.setDate(expiresAt.getDate() + TOKEN_EXPIRY_DAYS)
+
+ await db.insert(applicantPortalToken).values({
+ token,
+ applicationId,
+ candidateId,
+ organizationId,
+ expiresAt,
+ })
+
+ return token
+}
+
+/**
+ * Validate a portal token and return the associated data.
+ * Returns null if the token is invalid or expired.
+ * Updates lastAccessedAt on valid access.
+ */
+export async function validatePortalToken(token: string) {
+ const record = await db.query.applicantPortalToken.findFirst({
+ where: eq(applicantPortalToken.token, token),
+ columns: {
+ id: true,
+ applicationId: true,
+ candidateId: true,
+ organizationId: true,
+ expiresAt: true,
+ },
+ })
+
+ if (!record) return null
+ if (record.expiresAt < new Date()) return null
+
+ // Update last accessed (fire-and-forget)
+ db.update(applicantPortalToken)
+ .set({ lastAccessedAt: new Date() })
+ .where(eq(applicantPortalToken.id, record.id))
+ .catch(() => {})
+
+ return record
+}
+
+/**
+ * Create a session for an authenticated applicant account.
+ */
+export async function createApplicantSession(accountId: string): Promise {
+ const token = generatePortalToken()
+ const expiresAt = new Date()
+ expiresAt.setTime(expiresAt.getTime() + SESSION_EXPIRY_HOURS * 60 * 60 * 1000)
+
+ await db.insert(applicantSession).values({
+ applicantAccountId: accountId,
+ token,
+ expiresAt,
+ })
+
+ return token
+}
+
+/**
+ * Validate an applicant session token (from cookie).
+ * Returns the applicant account or null.
+ */
+export async function validateApplicantSession(sessionToken: string) {
+ const session = await db.query.applicantSession.findFirst({
+ where: and(
+ eq(applicantSession.token, sessionToken),
+ ),
+ with: {
+ account: true,
+ },
+ })
+
+ if (!session) return null
+ if (session.expiresAt < new Date()) {
+ // Cleanup expired session
+ db.delete(applicantSession)
+ .where(eq(applicantSession.id, session.id))
+ .catch(() => {})
+ return null
+ }
+
+ return session.account
+}
+
+/**
+ * Find or create an applicant account from Google OAuth profile.
+ */
+export async function findOrCreateApplicantAccount(profile: {
+ googleId: string
+ email: string
+ name?: string
+ image?: string
+}) {
+ let account = await db.query.applicantAccount.findFirst({
+ where: eq(applicantAccount.googleId, profile.googleId),
+ })
+
+ if (account) {
+ // Update profile data on each login
+ const [updated] = await db
+ .update(applicantAccount)
+ .set({
+ email: profile.email,
+ name: profile.name ?? account.name,
+ image: profile.image ?? account.image,
+ updatedAt: new Date(),
+ })
+ .where(eq(applicantAccount.id, account.id))
+ .returning()
+ return updated!
+ }
+
+ // Check if email already has an account (different google ID — shouldn't happen, but guard)
+ const [created] = await db.insert(applicantAccount).values({
+ googleId: profile.googleId,
+ email: profile.email,
+ name: profile.name,
+ image: profile.image,
+ }).returning()
+
+ return created!
+}
diff --git a/server/utils/portal-dashboard.ts b/server/utils/portal-dashboard.ts
new file mode 100644
index 0000000..9d1c804
--- /dev/null
+++ b/server/utils/portal-dashboard.ts
@@ -0,0 +1,399 @@
+import { eq, and, desc, asc } from 'drizzle-orm'
+import {
+ application,
+ job,
+ candidate,
+ interview,
+ organization,
+ document,
+ activityLog,
+ applicationStatusEnum,
+} from '../database/schema'
+
+/**
+ * Pipeline stage definitions with human-friendly labels and descriptions.
+ * Used to show applicants where they are in the process.
+ */
+const PIPELINE_STAGES = [
+ { key: 'new', label: 'Applied', description: 'Your application has been received and is in the queue.' },
+ { key: 'screening', label: 'Screening', description: 'A recruiter is reviewing your application and qualifications.' },
+ { key: 'interview', label: 'Interview', description: 'You\'ve been selected for an interview stage.' },
+ { key: 'offer', label: 'Offer', description: 'An offer is being prepared or has been extended to you.' },
+ { key: 'hired', label: 'Hired', description: 'Congratulations! You\'ve been hired.' },
+] as const
+
+/**
+ * Fetch complete dashboard data for a single application.
+ * Used by both token-based and authenticated portal endpoints.
+ *
+ * All data is READ-ONLY — no mutations happen in this function.
+ * Sensitive internal data (notes, scores, internal comments) is excluded.
+ */
+export async function getApplicationDashboard(
+ applicationId: string,
+ candidateId: string,
+ organizationId: string,
+) {
+ // Fetch application with job and organization in parallel
+ const [app, org, interviews, documents, statusHistory] = await Promise.all([
+ db.query.application.findFirst({
+ where: and(
+ eq(application.id, applicationId),
+ eq(application.candidateId, candidateId),
+ eq(application.organizationId, organizationId),
+ ),
+ columns: {
+ id: true,
+ status: true,
+ viewedAt: true,
+ createdAt: true,
+ updatedAt: true,
+ // Explicitly exclude sensitive fields
+ // notes: excluded
+ // score: excluded
+ },
+ with: {
+ job: {
+ columns: {
+ id: true,
+ title: true,
+ location: true,
+ type: true,
+ status: true,
+ remoteStatus: true,
+ },
+ },
+ },
+ }),
+
+ db.query.organization.findFirst({
+ where: eq(organization.id, organizationId),
+ columns: {
+ name: true,
+ logo: true,
+ },
+ }),
+
+ // Upcoming & past interviews (only applicant-visible fields)
+ db.query.interview.findMany({
+ where: and(
+ eq(interview.applicationId, applicationId),
+ eq(interview.organizationId, organizationId),
+ ),
+ columns: {
+ id: true,
+ title: true,
+ type: true,
+ status: true,
+ scheduledAt: true,
+ duration: true,
+ location: true,
+ timezone: true,
+ candidateResponse: true,
+ // Exclude: notes, interviewers (internal)
+ },
+ orderBy: [asc(interview.scheduledAt)],
+ }),
+
+ // Uploaded documents (names only — no download links)
+ db.select({
+ id: document.id,
+ type: document.type,
+ originalFilename: document.originalFilename,
+ createdAt: document.createdAt,
+ })
+ .from(document)
+ .where(and(
+ eq(document.candidateId, candidateId),
+ eq(document.organizationId, organizationId),
+ ))
+ .orderBy(desc(document.createdAt)),
+
+ // Status change history from activity log
+ db.select({
+ id: activityLog.id,
+ action: activityLog.action,
+ metadata: activityLog.metadata,
+ createdAt: activityLog.createdAt,
+ })
+ .from(activityLog)
+ .where(and(
+ eq(activityLog.organizationId, organizationId),
+ eq(activityLog.resourceType, 'application'),
+ eq(activityLog.resourceId, applicationId),
+ ))
+ .orderBy(desc(activityLog.createdAt))
+ .limit(50),
+ ])
+
+ if (!app) return null
+
+ // Build pipeline progress
+ const currentStatus = app.status
+ const isRejected = currentStatus === 'rejected'
+ const currentIndex = PIPELINE_STAGES.findIndex((s) => s.key === currentStatus)
+ const pipeline = PIPELINE_STAGES.map((stage, index) => ({
+ ...stage,
+ status: isRejected
+ ? 'inactive' as const
+ : index < currentIndex
+ ? 'completed' as const
+ : index === currentIndex
+ ? 'current' as const
+ : 'upcoming' as const,
+ }))
+
+ // Build timeline from activity log + application creation
+ const timeline = [
+ {
+ id: 'applied',
+ type: 'status_change' as const,
+ title: 'Application Submitted',
+ description: `You applied for ${app.job.title}`,
+ date: app.createdAt,
+ },
+ // Viewed by hiring team entry
+ ...(app.viewedAt
+ ? [{
+ id: 'viewed',
+ type: 'status_change' as const,
+ title: 'Viewed by Hiring Team',
+ description: 'A member of the hiring team reviewed your application.',
+ date: app.viewedAt,
+ }]
+ : []),
+ ...statusHistory
+ .filter((entry) => entry.action === 'status_changed')
+ .map((entry) => {
+ const meta = entry.metadata as Record | null
+ const to = meta?.to as string | undefined
+ const from = meta?.from as string | undefined
+ return {
+ id: entry.id,
+ type: 'status_change' as const,
+ title: getStatusChangeTitle(from, to),
+ description: getStatusChangeDescription(to),
+ date: entry.createdAt,
+ }
+ }),
+ ...interviews.map((iv) => ({
+ id: iv.id,
+ type: 'interview' as const,
+ title: iv.title,
+ description: `${formatInterviewType(iv.type)} scheduled`,
+ date: iv.scheduledAt,
+ })),
+ ].sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime())
+
+ // Separate upcoming vs past interviews
+ const now = new Date()
+ const upcomingInterviews = interviews.filter(
+ (iv) => new Date(iv.scheduledAt) > now && iv.status === 'scheduled',
+ )
+ const pastInterviews = interviews.filter(
+ (iv) => new Date(iv.scheduledAt) <= now || iv.status !== 'scheduled',
+ )
+
+ return {
+ application: {
+ id: app.id,
+ status: app.status,
+ appliedAt: app.createdAt,
+ lastUpdated: app.updatedAt,
+ viewedAt: app.viewedAt,
+ isRejected,
+ },
+ job: {
+ title: app.job.title,
+ location: app.job.location,
+ type: formatJobType(app.job.type),
+ remoteStatus: app.job.remoteStatus,
+ isOpen: app.job.status === 'open',
+ },
+ organization: {
+ name: org?.name ?? 'Unknown',
+ logo: org?.logo,
+ },
+ pipeline,
+ timeline,
+ upcomingInterviews: upcomingInterviews.map(formatInterview),
+ pastInterviews: pastInterviews.map(formatInterview),
+ documents: documents.map((doc) => ({
+ id: doc.id,
+ type: doc.type,
+ filename: doc.originalFilename,
+ uploadedAt: doc.createdAt,
+ })),
+ }
+}
+
+/**
+ * Fetch all applications for a candidate email (authenticated portal).
+ */
+export async function getApplicantDashboardByEmail(email: string) {
+ const candidates = await db.query.candidate.findMany({
+ where: eq(candidate.email, email.toLowerCase()),
+ columns: { id: true, organizationId: true },
+ })
+
+ if (candidates.length === 0) return []
+
+ const applications = []
+
+ for (const cand of candidates) {
+ const apps = await db.query.application.findMany({
+ where: and(
+ eq(application.candidateId, cand.id),
+ eq(application.organizationId, cand.organizationId),
+ ),
+ columns: {
+ id: true,
+ status: true,
+ createdAt: true,
+ updatedAt: true,
+ },
+ with: {
+ job: {
+ columns: {
+ id: true,
+ title: true,
+ location: true,
+ type: true,
+ status: true,
+ remoteStatus: true,
+ },
+ },
+ },
+ orderBy: [desc(application.createdAt)],
+ })
+
+ if (apps.length === 0) continue
+
+ // Get org details
+ const org = await db.query.organization.findFirst({
+ where: eq(organization.id, cand.organizationId),
+ columns: { name: true, logo: true },
+ })
+
+ for (const app of apps) {
+ const currentStatus = app.status
+ const isRejected = currentStatus === 'rejected'
+ const currentIndex = PIPELINE_STAGES.findIndex((s) => s.key === currentStatus)
+ const pipeline = PIPELINE_STAGES.map((stage, index) => ({
+ ...stage,
+ status: isRejected
+ ? 'inactive' as const
+ : index < currentIndex
+ ? 'completed' as const
+ : index === currentIndex
+ ? 'current' as const
+ : 'upcoming' as const,
+ }))
+
+ // Count upcoming interviews
+ const interviews = await db.query.interview.findMany({
+ where: and(
+ eq(interview.applicationId, app.id),
+ eq(interview.organizationId, cand.organizationId),
+ eq(interview.status, 'scheduled'),
+ ),
+ columns: { id: true, scheduledAt: true },
+ })
+
+ const upcomingCount = interviews.filter(
+ (iv) => new Date(iv.scheduledAt) > new Date(),
+ ).length
+
+ applications.push({
+ id: app.id,
+ candidateId: cand.id,
+ organizationId: cand.organizationId,
+ status: app.status,
+ appliedAt: app.createdAt,
+ lastUpdated: app.updatedAt,
+ isRejected,
+ job: {
+ title: app.job.title,
+ location: app.job.location,
+ type: formatJobType(app.job.type),
+ remoteStatus: app.job.remoteStatus,
+ },
+ organization: {
+ name: org?.name ?? 'Unknown',
+ logo: org?.logo,
+ },
+ pipeline,
+ upcomingInterviewCount: upcomingCount,
+ })
+ }
+ }
+
+ return applications
+}
+
+// ─── Formatting Helpers ────────────────────────────────
+
+function formatJobType(type: string): string {
+ const map: Record = {
+ full_time: 'Full Time',
+ part_time: 'Part Time',
+ contract: 'Contract',
+ internship: 'Internship',
+ }
+ return map[type] ?? type
+}
+
+function formatInterviewType(type: string): string {
+ const map: Record = {
+ phone: 'Phone Screen',
+ video: 'Video Call',
+ in_person: 'In-Person',
+ panel: 'Panel Interview',
+ technical: 'Technical Interview',
+ take_home: 'Take-Home Assessment',
+ }
+ return map[type] ?? type
+}
+
+function formatInterview(iv: {
+ id: string
+ title: string
+ type: string
+ status: string
+ scheduledAt: Date
+ duration: number
+ location: string | null
+ timezone: string
+ candidateResponse: string
+}) {
+ return {
+ id: iv.id,
+ title: iv.title,
+ type: formatInterviewType(iv.type),
+ status: iv.status,
+ scheduledAt: iv.scheduledAt,
+ duration: iv.duration,
+ location: iv.location,
+ timezone: iv.timezone,
+ candidateResponse: iv.candidateResponse,
+ }
+}
+
+function getStatusChangeTitle(from: string | undefined, to: string | undefined): string {
+ if (to === 'rejected') return 'Application Not Moving Forward'
+ if (to === 'hired') return 'You\'re Hired!'
+ if (to === 'offer') return 'Offer Extended'
+ if (to === 'interview') return 'Moving to Interview'
+ if (to === 'screening') return 'Under Review'
+ if (to === 'new' && from === 'rejected') return 'Application Reopened'
+ return `Status Updated`
+}
+
+function getStatusChangeDescription(to: string | undefined): string {
+ if (to === 'rejected') return 'The team has decided not to proceed with your application at this time.'
+ if (to === 'hired') return 'Congratulations! The team has extended a hire. Expect to hear from them shortly.'
+ if (to === 'offer') return 'Great news! An offer is being prepared for you.'
+ if (to === 'interview') return 'The team would like to interview you. Watch for scheduling details.'
+ if (to === 'screening') return 'A recruiter is now reviewing your qualifications.'
+ return 'Your application status has been updated.'
+}