From f9bd544dba1057655fee9343adf427fedb182215 Mon Sep 17 00:00:00 2001 From: cte Date: Sun, 11 May 2025 23:38:44 -0700 Subject: [PATCH] Clean up database stuff --- .env.development | 1 + .env.test | 1 + .husky/pre-commit | 4 +- README.md | 6 ++ drizzle.config.ts | 6 +- migrations/0000_init-db.sql | 21 ------ migrations/0000_misty_leo.sql | 12 +++ migrations/meta/0000_snapshot.json | 58 ++------------- migrations/meta/_journal.json | 4 +- package.json | 9 +-- pnpm-lock.yaml | 113 +++++++++++++++++++++++------ src/db/index.ts | 50 +------------ src/db/schema.ts | 35 ++------- 13 files changed, 133 insertions(+), 187 deletions(-) create mode 100644 .env.development create mode 100644 .env.test delete mode 100644 migrations/0000_init-db.sql create mode 100644 migrations/0000_misty_leo.sql diff --git a/.env.development b/.env.development new file mode 100644 index 0000000000..74cabaee61 --- /dev/null +++ b/.env.development @@ -0,0 +1 @@ +DATABASE_URL=postgres://postgres:password@localhost:5432/roo_code_development diff --git a/.env.test b/.env.test new file mode 100644 index 0000000000..f0a4a83413 --- /dev/null +++ b/.env.test @@ -0,0 +1 @@ +DATABASE_URL=postgres://postgres:password@localhost:5432/roo_code_test diff --git a/.husky/pre-commit b/.husky/pre-commit index 1ab521a7eb..09dd6bf4f1 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,3 +1 @@ -#!/bin/sh -# Disable concurent to run `check-types` after ESLint in lint-staged -cd "$(dirname "$0")/.." && npx --no lint-staged --concurrent false +pnpx lint-staged diff --git a/README.md b/README.md index 2004b5f6fd..6e94a786d2 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,12 @@ You can now install the required packages with: pnpm install ``` +Make sure your database is migrated: + +```sh +pnpm db:migrate +``` + If everything is working as expected you should be able to run any of the following without errors: ```sh diff --git a/drizzle.config.ts b/drizzle.config.ts index b84114f2ca..0f2ac48521 100644 --- a/drizzle.config.ts +++ b/drizzle.config.ts @@ -2,11 +2,9 @@ import { defineConfig } from 'drizzle-kit'; export default defineConfig({ out: './migrations', - schema: './src/models/Schema.ts', + schema: './src/db/schema.ts', dialect: 'postgresql', - dbCredentials: { - url: process.env.DATABASE_URL ?? '', - }, + dbCredentials: { url: process.env.DATABASE_URL! }, verbose: true, strict: true, }); diff --git a/migrations/0000_init-db.sql b/migrations/0000_init-db.sql deleted file mode 100644 index b7d674a0b1..0000000000 --- a/migrations/0000_init-db.sql +++ /dev/null @@ -1,21 +0,0 @@ -CREATE TABLE IF NOT EXISTS "organization" ( - "id" text PRIMARY KEY NOT NULL, - "stripe_customer_id" text, - "stripe_subscription_id" text, - "stripe_subscription_price_id" text, - "stripe_subscription_status" text, - "stripe_subscription_current_period_end" bigint, - "updated_at" timestamp DEFAULT now() NOT NULL, - "created_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE IF NOT EXISTS "todo" ( - "id" serial PRIMARY KEY NOT NULL, - "owner_id" text NOT NULL, - "title" text NOT NULL, - "message" text NOT NULL, - "updated_at" timestamp DEFAULT now() NOT NULL, - "created_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE UNIQUE INDEX IF NOT EXISTS "stripe_customer_id_idx" ON "organization" USING btree ("stripe_customer_id"); \ No newline at end of file diff --git a/migrations/0000_misty_leo.sql b/migrations/0000_misty_leo.sql new file mode 100644 index 0000000000..8854258552 --- /dev/null +++ b/migrations/0000_misty_leo.sql @@ -0,0 +1,12 @@ +CREATE TABLE "organization" ( + "id" text PRIMARY KEY NOT NULL, + "stripe_customer_id" text, + "stripe_subscription_id" text, + "stripe_subscription_price_id" text, + "stripe_subscription_status" text, + "stripe_subscription_current_period_end" bigint, + "updated_at" timestamp DEFAULT now() NOT NULL, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE UNIQUE INDEX "stripe_customer_id_idx" ON "organization" USING btree ("stripe_customer_id"); \ No newline at end of file diff --git a/migrations/meta/0000_snapshot.json b/migrations/meta/0000_snapshot.json index 54ca06164d..8d2c1bea6f 100644 --- a/migrations/meta/0000_snapshot.json +++ b/migrations/meta/0000_snapshot.json @@ -1,5 +1,5 @@ { - "id": "400db9e7-780a-44b8-b0be-2db504f59104", + "id": "013accf9-070a-47cd-9b49-1318a1c8a237", "prevId": "00000000-0000-0000-0000-000000000000", "version": "7", "dialect": "postgresql", @@ -78,60 +78,18 @@ }, "foreignKeys": {}, "compositePrimaryKeys": {}, - "uniqueConstraints": {} - }, - "public.todo": { - "name": "todo", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "serial", - "primaryKey": true, - "notNull": true - }, - "owner_id": { - "name": "owner_id", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "title": { - "name": "title", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "message": { - "name": "message", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "updated_at": { - "name": "updated_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - }, - "created_at": { - "name": "created_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - } - }, - "indexes": {}, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": {} + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false } }, "enums": {}, "schemas": {}, "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, "_meta": { "columns": {}, "schemas": {}, diff --git a/migrations/meta/_journal.json b/migrations/meta/_journal.json index 90627d8396..c6f820ea20 100644 --- a/migrations/meta/_journal.json +++ b/migrations/meta/_journal.json @@ -5,8 +5,8 @@ { "idx": 0, "version": "7", - "when": 1726784321202, - "tag": "0000_init-db", + "when": 1747031111132, + "tag": "0000_misty_leo", "breakpoints": true } ] diff --git a/package.json b/package.json index 7a273b4acd..6d06549e35 100644 --- a/package.json +++ b/package.json @@ -13,9 +13,9 @@ "start": "next start", "build-stats": "cross-env ANALYZE=true pnpm run build", "clean": "rimraf .next out coverage", - "db:generate": "drizzle-kit generate", - "db:migrate": "dotenv -c production -- drizzle-kit migrate", - "db:studio": "dotenv -c production -- drizzle-kit studio", + "db:generate": "dotenvx run -f .env.development -- drizzle-kit generate", + "db:migrate": "dotenvx run -f .env.development -- drizzle-kit migrate", + "db:studio": "dotenvx run -f .env.development -- drizzle-kit studio", "storybook": "storybook dev -p 6006", "storybook:build": "storybook build", "storybook:serve": "http-server storybook-static --port 6006 --silent", @@ -27,7 +27,6 @@ "@clerk/localizations": "^3.14.2", "@clerk/nextjs": "^6.18.3", "@clerk/themes": "^2.1.36", - "@electric-sql/pglite": "^0.3.0", "@logtail/pino": "^0.5.2", "@radix-ui/react-dropdown-menu": "^2.1.14", "@radix-ui/react-icons": "^1.3.2", @@ -57,6 +56,7 @@ }, "devDependencies": { "@clerk/testing": "^1.3.11", + "@dotenvx/dotenvx": "^1.43.0", "@eslint/js": "^9.26.0", "@next/bundle-analyzer": "^15.3.2", "@next/eslint-plugin-next": "^15.3.2", @@ -83,7 +83,6 @@ "@vitest/expect": "^3.1.3", "autoprefixer": "^10.4.20", "cross-env": "^7.0.3", - "dotenv-cli": "^8.0.0", "drizzle-kit": "^0.31.1", "eslint": "^9.26.0", "eslint-config-prettier": "^10.1.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e0bf6bf447..92e5cb2825 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,9 +17,6 @@ importers: '@clerk/themes': specifier: ^2.1.36 version: 2.2.43 - '@electric-sql/pglite': - specifier: ^0.3.0 - version: 0.3.0 '@logtail/pino': specifier: ^0.5.2 version: 0.5.4(pino@9.6.0) @@ -102,6 +99,9 @@ importers: '@clerk/testing': specifier: ^1.3.11 version: 1.6.7(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@dotenvx/dotenvx': + specifier: ^1.43.0 + version: 1.43.0 '@eslint/js': specifier: ^9.26.0 version: 9.26.0 @@ -180,9 +180,6 @@ importers: cross-env: specifier: ^7.0.3 version: 7.0.3 - dotenv-cli: - specifier: ^8.0.0 - version: 8.0.0 drizzle-kit: specifier: ^0.31.1 version: 0.31.1 @@ -1028,9 +1025,19 @@ packages: resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==} engines: {node: '>=10.0.0'} + '@dotenvx/dotenvx@1.43.0': + resolution: {integrity: sha512-Z8XjM75aWZ/ekUzBjlr/OqQsLWtJY4nVtruxopAt+FlYHfY0/gKl85nD16aEqbTkU53kJcm5psID0L2/sQMmuw==} + hasBin: true + '@drizzle-team/brocli@0.10.2': resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==} + '@ecies/ciphers@0.2.3': + resolution: {integrity: sha512-tapn6XhOueMwht3E2UzY0ZZjYokdaw9XtL9kEyjhQ/Fb9vL9xTFbOaI+fV0AWvTpYu4BNloC6getKW6NtSg4mA==} + engines: {bun: '>=1', deno: '>=2', node: '>=16'} + peerDependencies: + '@noble/ciphers': ^1.0.0 + '@electric-sql/pglite@0.3.0': resolution: {integrity: sha512-ou1PA24kStSNpQVGdzDg1FGRzO7AQ3O6OX5FryupMdHkSfSswG2ZVXjCzzJKK88KLk+Q4QinKjZIUtmMeec5rg==} @@ -1850,6 +1857,18 @@ packages: cpu: [x64] os: [win32] + '@noble/ciphers@1.3.0': + resolution: {integrity: sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==} + engines: {node: ^14.21.3 || >=16} + + '@noble/curves@1.9.0': + resolution: {integrity: sha512-7YDlXiNMdO1YZeH6t/kvopHHbIZzlxrCV9WLqCY6QhcXOoXiNCMDqJIglZ9Yjx5+w7Dz30TITFrlTjnRg7sKEg==} + engines: {node: ^14.21.3 || >=16} + + '@noble/hashes@1.8.0': + resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} + engines: {node: ^14.21.3 || >=16} + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -4311,6 +4330,10 @@ packages: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} + commander@11.1.0: + resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} + engines: {node: '>=16'} + commander@12.1.0: resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} engines: {node: '>=18'} @@ -4662,14 +4685,6 @@ packages: dot-case@3.0.4: resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} - dotenv-cli@8.0.0: - resolution: {integrity: sha512-aLqYbK7xKOiTMIRf1lDPbI+Y+Ip/wo5k3eyp6ePysVaSqbyxjyK3dK35BTxG+rmd7djf5q2UPs4noPNH+cj0Qw==} - hasBin: true - - dotenv-expand@10.0.0: - resolution: {integrity: sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==} - engines: {node: '>=12'} - dotenv@16.0.3: resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==} engines: {node: '>=12'} @@ -4785,6 +4800,10 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + eciesjs@0.4.14: + resolution: {integrity: sha512-eJAgf9pdv214Hn98FlUzclRMYWF7WfoLlkS9nWMTm1qcCwn6Ad4EGD9lr9HXMBfSrZhYQujRE+p0adPRkctC6A==} + engines: {bun: '>=1', deno: '>=2', node: '>=16'} + ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} @@ -5778,6 +5797,10 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + isexe@3.1.1: + resolution: {integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==} + engines: {node: '>=16'} + istanbul-lib-coverage@3.2.2: resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} engines: {node: '>=8'} @@ -6566,6 +6589,10 @@ packages: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} + object-treeify@1.1.33: + resolution: {integrity: sha512-EFVjAYfzWqWsBMRHPMAXLCDIJnpMhdWAqR7xG6M6a2cs6PMFpl/+Z20w9zDW4vkxOFfddegBKq9Rehd0bxWE7A==} + engines: {node: '>= 10'} + object.assign@4.1.7: resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} engines: {node: '>= 0.4'} @@ -8447,6 +8474,11 @@ packages: engines: {node: '>= 8'} hasBin: true + which@4.0.0: + resolution: {integrity: sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==} + engines: {node: ^16.13.0 || >=18.0.0} + hasBin: true + why-is-node-running@2.3.0: resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} engines: {node: '>=8'} @@ -9516,9 +9548,26 @@ snapshots: '@discoveryjs/json-ext@0.5.7': {} + '@dotenvx/dotenvx@1.43.0': + dependencies: + commander: 11.1.0 + dotenv: 16.5.0 + eciesjs: 0.4.14 + execa: 5.1.1 + fdir: 6.4.4(picomatch@4.0.2) + ignore: 5.3.2 + object-treeify: 1.1.33 + picomatch: 4.0.2 + which: 4.0.0 + '@drizzle-team/brocli@0.10.2': {} - '@electric-sql/pglite@0.3.0': {} + '@ecies/ciphers@0.2.3(@noble/ciphers@1.3.0)': + dependencies: + '@noble/ciphers': 1.3.0 + + '@electric-sql/pglite@0.3.0': + optional: true '@emnapi/runtime@1.4.3': dependencies: @@ -10264,6 +10313,14 @@ snapshots: '@next/swc-win32-x64-msvc@15.3.2': optional: true + '@noble/ciphers@1.3.0': {} + + '@noble/curves@1.9.0': + dependencies: + '@noble/hashes': 1.8.0 + + '@noble/hashes@1.8.0': {} + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -13240,6 +13297,8 @@ snapshots: dependencies: delayed-stream: 1.0.0 + commander@11.1.0: {} + commander@12.1.0: {} commander@13.1.0: {} @@ -13595,15 +13654,6 @@ snapshots: no-case: 3.0.4 tslib: 2.8.1 - dotenv-cli@8.0.0: - dependencies: - cross-spawn: 7.0.6 - dotenv: 16.5.0 - dotenv-expand: 10.0.0 - minimist: 1.2.8 - - dotenv-expand@10.0.0: {} - dotenv@16.0.3: {} dotenv@16.4.7: {} @@ -13637,6 +13687,13 @@ snapshots: eastasianwidth@0.2.0: {} + eciesjs@0.4.14: + dependencies: + '@ecies/ciphers': 0.2.3(@noble/ciphers@1.3.0) + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.0 + '@noble/hashes': 1.8.0 + ee-first@1.1.1: {} electron-to-chromium@1.5.151: {} @@ -14847,6 +14904,8 @@ snapshots: isexe@2.0.0: {} + isexe@3.1.1: {} + istanbul-lib-coverage@3.2.2: {} istanbul-lib-hook@3.0.0: @@ -15877,6 +15936,8 @@ snapshots: object-keys@1.1.1: {} + object-treeify@1.1.33: {} + object.assign@4.1.7: dependencies: call-bind: 1.0.8 @@ -17996,6 +18057,10 @@ snapshots: dependencies: isexe: 2.0.0 + which@4.0.0: + dependencies: + isexe: 3.1.1 + why-is-node-running@2.3.0: dependencies: siginfo: 2.0.0 diff --git a/src/db/index.ts b/src/db/index.ts index 4448e3f3c5..d367f97e98 100644 --- a/src/db/index.ts +++ b/src/db/index.ts @@ -1,51 +1,5 @@ -import path from 'node:path'; - -import { PGlite } from '@electric-sql/pglite'; -import { drizzle as drizzlePg } from 'drizzle-orm/node-postgres'; -import { migrate as migratePg } from 'drizzle-orm/node-postgres/migrator'; -import { - drizzle as drizzlePglite, - type PgliteDatabase, -} from 'drizzle-orm/pglite'; -import { migrate as migratePglite } from 'drizzle-orm/pglite/migrator'; -import { PHASE_PRODUCTION_BUILD } from 'next/dist/shared/lib/constants'; -import { Client } from 'pg'; - -import * as schema from '@/db/schema'; - import { Env } from '../lib/server/Env'; -let client; -let drizzle; +import { drizzle } from 'drizzle-orm/node-postgres'; -if (process.env.NEXT_PHASE !== PHASE_PRODUCTION_BUILD && Env.DATABASE_URL) { - client = new Client({ - connectionString: Env.DATABASE_URL, - }); - await client.connect(); - - drizzle = drizzlePg(client, { schema }); - await migratePg(drizzle, { - migrationsFolder: path.join(process.cwd(), 'migrations'), - }); -} else { - // Stores the db connection in the global scope to prevent multiple instances due to hot reloading with Next.js - const global = globalThis as unknown as { - client: PGlite; - drizzle: PgliteDatabase; - }; - - if (!global.client) { - global.client = new PGlite(); - await global.client.waitReady; - - global.drizzle = drizzlePglite(global.client, { schema }); - } - - drizzle = global.drizzle; - await migratePglite(global.drizzle, { - migrationsFolder: path.join(process.cwd(), 'migrations'), - }); -} - -export const db = drizzle; +export const db = drizzle(Env.DATABASE_URL!); diff --git a/src/db/schema.ts b/src/db/schema.ts index c1c3755fb1..94ca4c5e2a 100644 --- a/src/db/schema.ts +++ b/src/db/schema.ts @@ -1,22 +1,11 @@ import { bigint, pgTable, - serial, text, timestamp, uniqueIndex, } from 'drizzle-orm/pg-core'; -// This file defines the structure of your database tables using the Drizzle ORM. - -// To modify the database schema: -// 1. Update this file with your desired changes. -// 2. Generate a new migration by running: `npm run db:generate` - -// The generated migration file will reflect your schema changes. -// The migration is automatically applied during the next database interaction, -// so there's no need to run it manually or restart the Next.js server. - export const organizationSchema = pgTable( 'organization', { @@ -35,23 +24,9 @@ export const organizationSchema = pgTable( .notNull(), createdAt: timestamp('created_at', { mode: 'date' }).defaultNow().notNull(), }, - (table) => { - return { - stripeCustomerIdIdx: uniqueIndex('stripe_customer_id_idx').on( - table.stripeCustomerId, - ), - }; - }, + (table) => ({ + stripeCustomerIdIdx: uniqueIndex('stripe_customer_id_idx').on( + table.stripeCustomerId, + ), + }), ); - -export const todoSchema = pgTable('todo', { - id: serial('id').primaryKey(), - ownerId: text('owner_id').notNull(), - title: text('title').notNull(), - message: text('message').notNull(), - updatedAt: timestamp('updated_at', { mode: 'date' }) - .defaultNow() - .$onUpdate(() => new Date()) - .notNull(), - createdAt: timestamp('created_at', { mode: 'date' }).defaultNow().notNull(), -});