ordinarthur 9c93e74318 replace Supabase with Postgres + Drizzle ORM
- Drop @supabase/supabase-js entirely; add drizzle-orm + postgres (porsager) driver
- New packages/db: schema (pgSchema ordinarthur_os), client factory, migrate runner, drizzle-kit config
- SQL migrations: 0000_init (pgcrypto + schema), 0001_jobs (jobs + job_search_criteria, no RLS)
- Rewrite apps/api db module with DI symbols DB/DB_HANDLE + @InjectDb() decorator
- Rewrite jobs.service.ts with Drizzle queries (upsert via onConflictDoUpdate, arrayOverlaps for stack filter)
- Replace SUPABASE_* env vars with DATABASE_URL in env config + .env.example
- Add docker-compose.yml (Postgres 16-alpine, dev only)
- Add deploy/k8s/postgres.yaml (StatefulSet + PVC), migrate.job.yaml, updated secrets.template.yaml
- Update all docs (README, PLAN, ARCHITECTURE, CLAUDE.md, AGENTS.md, packages/db/README.md)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 10:15:34 +02:00

52 lines
1.9 KiB
SQL

-- 0001_jobs.sql — Phase 1
-- Tables jobs + job_search_criteria.
CREATE TABLE IF NOT EXISTS "ordinarthur_os"."jobs" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"source" text NOT NULL,
"source_url" text NOT NULL,
"title" text NOT NULL,
"company" text,
"description" text,
"location" text,
"remote_type" text,
"salary_min" integer,
"salary_max" integer,
"stack" text[] DEFAULT '{}'::text[] NOT NULL,
"apply_url" text,
"first_seen_at" timestamp with time zone DEFAULT now() NOT NULL,
"last_seen_at" timestamp with time zone DEFAULT now() NOT NULL,
"archived" boolean DEFAULT false NOT NULL,
"starred" boolean DEFAULT false NOT NULL,
"applied_at" timestamp with time zone,
"notes" text,
CONSTRAINT "jobs_source_url_unique" UNIQUE("source_url"),
CONSTRAINT "jobs_remote_type_check"
CHECK ("remote_type" IS NULL OR "remote_type" IN ('remote','hybrid','onsite'))
);
CREATE INDEX IF NOT EXISTS "jobs_last_seen_idx"
ON "ordinarthur_os"."jobs" ("last_seen_at" DESC);
CREATE INDEX IF NOT EXISTS "jobs_archived_idx"
ON "ordinarthur_os"."jobs" ("archived");
CREATE INDEX IF NOT EXISTS "jobs_remote_type_idx"
ON "ordinarthur_os"."jobs" ("remote_type");
CREATE INDEX IF NOT EXISTS "jobs_stack_gin"
ON "ordinarthur_os"."jobs" USING gin ("stack");
CREATE TABLE IF NOT EXISTS "ordinarthur_os"."job_search_criteria" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"name" text,
"titles" text[] DEFAULT '{}'::text[] NOT NULL,
"locations" text[] DEFAULT '{}'::text[] NOT NULL,
"stack" text[] DEFAULT '{}'::text[] NOT NULL,
"remote_types" text[] DEFAULT '{}'::text[] NOT NULL,
"salary_min" integer,
"active" boolean DEFAULT true NOT NULL,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
);
CREATE INDEX IF NOT EXISTS "job_criteria_active_idx"
ON "ordinarthur_os"."job_search_criteria" ("active");