101 lines
3.9 KiB
TypeScript
101 lines
3.9 KiB
TypeScript
/**
|
|
* Database schema using Drizzle ORM with SQLite.
|
|
*/
|
|
|
|
import { sqliteTable, text, integer, real } from 'drizzle-orm/sqlite-core';
|
|
import { sql } from 'drizzle-orm';
|
|
|
|
export const jobs = sqliteTable('jobs', {
|
|
id: text('id').primaryKey(),
|
|
|
|
// From crawler
|
|
source: text('source', { enum: ['gradcracker', 'indeed', 'linkedin', 'ukvisajobs'] }).notNull().default('gradcracker'),
|
|
sourceJobId: text('source_job_id'),
|
|
jobUrlDirect: text('job_url_direct'),
|
|
datePosted: text('date_posted'),
|
|
title: text('title').notNull(),
|
|
employer: text('employer').notNull(),
|
|
employerUrl: text('employer_url'),
|
|
jobUrl: text('job_url').notNull().unique(),
|
|
applicationLink: text('application_link'),
|
|
disciplines: text('disciplines'),
|
|
deadline: text('deadline'),
|
|
salary: text('salary'),
|
|
location: text('location'),
|
|
degreeRequired: text('degree_required'),
|
|
starting: text('starting'),
|
|
jobDescription: text('job_description'),
|
|
|
|
// JobSpy fields (nullable for other sources)
|
|
jobType: text('job_type'),
|
|
salarySource: text('salary_source'),
|
|
salaryInterval: text('salary_interval'),
|
|
salaryMinAmount: real('salary_min_amount'),
|
|
salaryMaxAmount: real('salary_max_amount'),
|
|
salaryCurrency: text('salary_currency'),
|
|
isRemote: integer('is_remote', { mode: 'boolean' }),
|
|
jobLevel: text('job_level'),
|
|
jobFunction: text('job_function'),
|
|
listingType: text('listing_type'),
|
|
emails: text('emails'),
|
|
companyIndustry: text('company_industry'),
|
|
companyLogo: text('company_logo'),
|
|
companyUrlDirect: text('company_url_direct'),
|
|
companyAddresses: text('company_addresses'),
|
|
companyNumEmployees: text('company_num_employees'),
|
|
companyRevenue: text('company_revenue'),
|
|
companyDescription: text('company_description'),
|
|
skills: text('skills'),
|
|
experienceRange: text('experience_range'),
|
|
companyRating: real('company_rating'),
|
|
companyReviewsCount: integer('company_reviews_count'),
|
|
vacancyCount: integer('vacancy_count'),
|
|
workFromHomeType: text('work_from_home_type'),
|
|
|
|
// Orchestrator enrichments
|
|
status: text('status', {
|
|
enum: ['discovered', 'processing', 'ready', 'applied', 'rejected', 'expired']
|
|
}).notNull().default('discovered'),
|
|
suitabilityScore: real('suitability_score'),
|
|
suitabilityReason: text('suitability_reason'),
|
|
tailoredSummary: text('tailored_summary'),
|
|
tailoredHeadline: text('tailored_headline'),
|
|
tailoredSkills: text('tailored_skills'),
|
|
selectedProjectIds: text('selected_project_ids'),
|
|
pdfPath: text('pdf_path'),
|
|
notionPageId: text('notion_page_id'),
|
|
|
|
// Timestamps
|
|
discoveredAt: text('discovered_at').notNull().default(sql`(datetime('now'))`),
|
|
processedAt: text('processed_at'),
|
|
appliedAt: text('applied_at'),
|
|
createdAt: text('created_at').notNull().default(sql`(datetime('now'))`),
|
|
updatedAt: text('updated_at').notNull().default(sql`(datetime('now'))`),
|
|
});
|
|
|
|
export const pipelineRuns = sqliteTable('pipeline_runs', {
|
|
id: text('id').primaryKey(),
|
|
startedAt: text('started_at').notNull().default(sql`(datetime('now'))`),
|
|
completedAt: text('completed_at'),
|
|
status: text('status', {
|
|
enum: ['running', 'completed', 'failed']
|
|
}).notNull().default('running'),
|
|
jobsDiscovered: integer('jobs_discovered').notNull().default(0),
|
|
jobsProcessed: integer('jobs_processed').notNull().default(0),
|
|
errorMessage: text('error_message'),
|
|
});
|
|
|
|
export const settings = sqliteTable('settings', {
|
|
key: text('key').primaryKey(),
|
|
value: text('value').notNull(),
|
|
createdAt: text('created_at').notNull().default(sql`(datetime('now'))`),
|
|
updatedAt: text('updated_at').notNull().default(sql`(datetime('now'))`),
|
|
});
|
|
|
|
export type JobRow = typeof jobs.$inferSelect;
|
|
export type NewJobRow = typeof jobs.$inferInsert;
|
|
export type PipelineRunRow = typeof pipelineRuns.$inferSelect;
|
|
export type NewPipelineRunRow = typeof pipelineRuns.$inferInsert;
|
|
export type SettingsRow = typeof settings.$inferSelect;
|
|
export type NewSettingsRow = typeof settings.$inferInsert;
|