# Design: Supabase/Prisma/Docker Removal — Full Convex Cutover

**Date:** 2026-03-08
**Approach:** B (Convex + Upstash Redis)
**Estimated effort:** 2-3 weeks

## Context

All 20 data-source domains default to Convex. Prisma still receives dual-writes. Supabase handles 3 storage buckets and some direct table queries. Docker runs Postgres, Redis, and Mailpit for local dev. This design eliminates Supabase, Prisma, and Docker (dev/CI) while keeping BullMQ on Upstash Redis and the production Dockerfile.

## Section 1: Convex Gap Fixes (Pre-requisites)

Before removing anything, fix the 3 domains that aren't fully wired:

**1a. Nurture domain** — Create `convex/nurture/queries.ts` and `convex/nurture/mutations.ts` covering the 4 nurture tables (sequences, steps, states, emails). ~50-80 functions matching the schema in `convex/schema/nurture.ts`.

**1b. Expenses domain** — Wire the remaining ~8 expense API routes to use Convex (functions already exist in `convex/expenses/`).

**1c. Customers schema** — Move customers table definition from `convex/schema/bookings.ts` to `convex/schema/customers.ts`. Update imports in `convex/schema.ts`.

**1d. Blog public pages** — Update `lib/blog.ts` to query Convex instead of Supabase directly. Staff API routes already use Convex; this closes the gap for public-facing blog pages.

**1e. Remaining unwired API routes** — Wire the ~47% of routes still calling Prisma directly to use Convex.

## Section 2: Convex Storage Migration

Replace all 3 Supabase storage buckets + 2 local filesystem upload paths with Convex file storage.

**2a. Convex storage infrastructure** — Create shared storage actions in `convex/storage/`:

- `generateUploadUrl` — Convex upload flow
- `getFileUrl` — Convert storage ID to serving URL
- `deleteFile` — Remove file by storage ID
- File metadata table: original filename, mime type, size, uploader, associated record

**2b. Contact attachments** (`contact-attachments` bucket) — Update `/app/api/contact/submit/route.ts`. Keep validation (MIME sniffing, threat scanning, 3 files max, 10MB each). Store Convex storage IDs.

**2c. Job CVs** (`job-cvs` bucket) — Update `/app/api/apply-job/route.ts` (upload) and `/app/api/staff/applications/[id]/download-cv/route.ts` (download). PDF-only, 10MB max. Replace `cv_bucket`/`cv_path` with Convex storage ID.

**2d. Blog images** (`blog-images` bucket) — Update `lib/services/blog-management.service.ts`. Replace `getPublicUrl()` with Convex serving URL.

**2e. User avatars** (`public/uploads/avatars/`) — Update `/app/api/user/avatar/route.ts` to use Convex storage. Directory currently empty.

**2f. Email attachments** (`public/uploads/attachments/`) — Update `/app/api/upload/attachments/route.ts`. Currently empty.

**2g. Existing file data migration** — One-time script: download from Supabase, re-upload to Convex, update DB records with new storage IDs.

## Section 3: Redis to Convex Migration

Move 4 Redis features to Convex. Keep BullMQ on Upstash Redis.

**3a. OTP storage** — Replace `lib/otp-storage.ts` Redis ops with Convex `otps` table mutations. Add scheduled function to purge expired OTPs hourly.

**3b. Secure tokens** — Replace `lib/secure-tokens.ts` Redis tracking with Convex `otps` table using `tokenId` field and `consumed` boolean.

**3c. Rate limiting** — Replace `lib/rate-limiter.ts` and `lib/rate-limit.ts` with Convex `rateLimitTracking` table. Sliding window counter via Convex query. Scheduled cleanup hourly. ~50ms latency acceptable for auth endpoints.

**3d. Generic caching** — Replace `lib/cache.ts` with in-memory LRU cache (`lru-cache` package).

**3e. Health checks** — Delete Redis health check from `lib/health-checks.ts`. Replace with Convex connectivity check. Remove `/api/health/redis`.

**3f. BullMQ on Upstash** — Keep `lib/queue/bullmq.ts`. Switch from local IORedis to Upstash Redis REST. No Docker Redis needed.

**3g. Dependency cleanup** — Remove `ioredis`. Keep `@upstash/redis`. Remove `@upstash/ratelimit`.

## Section 4: Remove Migration Scaffolding

**4a. Dual-database sync** — Delete:

- `lib/dual-database.ts` (1,482 lines)
- `lib/sync-queue.ts` (272 lines)
- `lib/database-sync/` directory (1,960 lines)

**4b. Data source router** — Delete `lib/data-source.ts`. Remove all `getDataSource()`/`isConvex()`/`isPrisma()` calls from every API route.

**4c. Prisma** — Delete:

- `prisma/schema.prisma` (3,956 lines, 133 models)
- `prisma/migrations/` directory
- `prisma/seeds/` directory
- `lib/generated/prisma/` directory
- Remove `prisma` and `@prisma/client` from `package.json`

**4d. Supabase** — Delete:

- `lib/supabase.ts` (236 lines)
- `supabase/` directory (config, 57 migrations, seed)
- Remove `@supabase/supabase-js` from `package.json`
- Remove all `SUPABASE_*` env vars

**4e. Import cleanup** — Grep and fix all imports from deleted modules.

**4f. Test cleanup** — Delete or rewrite tests for dual-db sync, Prisma, Supabase, data-source routing. Keep business logic tests, repoint to Convex.

## Section 5: Remove Docker from Dev and CI

**5a. Delete dev Docker files:**

- `Dockerfile.dev`, `Dockerfile.simple`
- `docker-compose.yml`, `docker-compose.infra.yml`, `docker-compose.override.yml`
- `scripts/docker-infra.sh`, `scripts/docker-status.sh`, `scripts/docker-logging.sh`

**5b. Keep production Dockerfile** — Update to remove Prisma/Supabase references. Add Convex and Clerk env vars.

**5c. Update npm scripts** — Remove 26 `docker:*` scripts. Add:

- `"dev": "concurrently \"next dev -p 9000\" \"convex dev\""`
- `"dev:next": "next dev -p 9000"`
- `"dev:convex": "convex dev"`

**5d. Update CI workflows:**

- Delete `database-tests.yml` (Postgres + Supabase). Replace with Convex-backed tests.
- Keep `test.yml`, `dependency-check.yml`, `release-checks.yml` — update to remove Prisma/Supabase checks.

**5e. Rewrite env validation** — `lib/env-validation.ts`:

- Remove: `DATABASE_URL`, `SUPABASE_*`, `REDIS_URL`
- Add required: `CONVEX_DEPLOYMENT`, `NEXT_PUBLIC_CONVEX_URL`, `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY`, `CLERK_SECRET_KEY`
- Add recommended: `UPSTASH_REDIS_REST_URL`

**5f. Update env files:**

- Delete `.env.docker`, `.env.docker.example`
- Rewrite `.env.example` for Clerk + Convex + Upstash only.

**5g. Temporal preserved** — `lib/temporal.ts` and `/api/staff/workflows/route.ts` stay. Future connection to Temporal Cloud.

## Deletion Inventory

| What                    | Lines        | Files                             |
| ----------------------- | ------------ | --------------------------------- |
| `lib/dual-database.ts`  | 1,482        | 1                                 |
| `lib/sync-queue.ts`     | 272          | 1                                 |
| `lib/database-sync/`    | 1,960        | 7                                 |
| `lib/data-source.ts`    | 62           | 1                                 |
| `lib/supabase.ts`       | 236          | 1                                 |
| `prisma/`               | 3,956+       | schema + migrations + seeds       |
| `lib/generated/prisma/` | ~2,400       | 5+ generated files                |
| `supabase/`             | ~3,000       | config + 57 migrations            |
| Docker dev files        | ~500         | 5 Dockerfiles + compose + scripts |
| Docker npm scripts      | 26 entries   | package.json                      |
| Redis code (non-BullMQ) | ~480         | 5 files                           |
| **Total removed**       | **~14,000+** | **~80+ files**                    |

## Dependencies Removed

- `prisma`, `@prisma/client`
- `@supabase/supabase-js`
- `ioredis`
- `@upstash/ratelimit`

## Dependencies Added

- `lru-cache` (in-memory caching)
- `concurrently` (dev script)

## Dependencies Kept

- `@upstash/redis` (BullMQ connection)
- `bullmq` (job queues)
