feat: migrate from PocketBase to PostgreSQL with Drizzle ORM

- Add @project-e/db package with Drizzle schema and migrations
- Replace PocketBase client with PostgreSQL-based database client
- Migrate auth from custom to NextAuth.js
- Add Docker Compose with PostgreSQL container
- Update worker to use new database client
- Remove PocketBase-specific files and migrations
- Add drizzle config and initial migration
This commit is contained in:
2026-07-24 07:08:29 -04:00
parent 6c438eab32
commit 73335484f8
42 changed files with 2895 additions and 2796 deletions
+14
View File
@@ -0,0 +1,14 @@
{
"name": "@project-e/db",
"version": "0.1.0",
"private": true,
"type": "module",
"exports": {
".": "./src/index.ts",
"./schema": "./src/schema.ts"
},
"dependencies": {
"drizzle-orm": "^0.45.1",
"postgres": "^3.4.8"
}
}
+13
View File
@@ -0,0 +1,13 @@
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
import * as schema from './schema';
const databaseUrl = process.env.DATABASE_URL;
if (!databaseUrl) {
throw new Error('DATABASE_URL is required.');
}
export const sql = postgres(databaseUrl, { max: 10 });
export const db = drizzle(sql, { schema });
export * from './schema';
+24
View File
@@ -0,0 +1,24 @@
import { index, jsonb, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: uuid('id').defaultRandom().primaryKey(),
email: text('email').notNull().unique(),
name: text('name').notNull(),
passwordHash: text('password_hash').notNull(),
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull(),
});
// Collection data is intentionally stored as JSONB. The application has flexible
// per-collection fields, and this preserves that shape while PostgreSQL owns storage.
export const records = pgTable(
'records',
{
id: uuid('id').defaultRandom().primaryKey(),
collection: text('collection').notNull(),
data: jsonb('data').$type<Record<string, unknown>>().notNull().default({}),
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull(),
},
(table) => [index('records_collection_created_at_idx').on(table.collection, table.createdAt)]
);