Clean up database stuff

This commit is contained in:
cte 2025-05-11 23:38:44 -07:00
parent 54fe3af0f0
commit f9bd544dba
13 changed files with 133 additions and 187 deletions

1
.env.development Normal file
View file

@ -0,0 +1 @@
DATABASE_URL=postgres://postgres:password@localhost:5432/roo_code_development

1
.env.test Normal file
View file

@ -0,0 +1 @@
DATABASE_URL=postgres://postgres:password@localhost:5432/roo_code_test

View file

@ -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

View file

@ -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

View file

@ -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,
});

View file

@ -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");

View file

@ -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");

View file

@ -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": {},

View file

@ -5,8 +5,8 @@
{
"idx": 0,
"version": "7",
"when": 1726784321202,
"tag": "0000_init-db",
"when": 1747031111132,
"tag": "0000_misty_leo",
"breakpoints": true
}
]

View file

@ -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",

113
pnpm-lock.yaml generated
View file

@ -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

View file

@ -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<typeof schema>;
};
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!);

View file

@ -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(),
});