feat(api): database migrations on startup

This commit is contained in:
Lars Hampe 2024-10-01 20:39:12 +02:00
parent d2ee997ac7
commit c31900d94a
7 changed files with 502 additions and 3 deletions

@ -1,3 +1,5 @@
import { migrateDatabase } from '@boring.tools/database'
declare module 'bun' {
interface Env {
POSTGRES_URL: string
@ -27,4 +29,6 @@ export const startup = () => {
process.exit(0)
}
})
migrateDatabase()
}

@ -1,12 +1,13 @@
import { drizzle } from 'drizzle-orm/node-postgres'
import { Client } from 'pg'
export * from './migration'
import * as schema from './schema'
export * from './schema'
const POSTGRES_URL = import.meta.env.POSTGRES_URL ?? process.env.POSTGRES_URL
const client = new Client({ connectionString: POSTGRES_URL })
export const client = new Client({ connectionString: POSTGRES_URL })
await client.connect()
export const db = drizzle(client, { schema })

@ -0,0 +1,8 @@
import path from 'node:path'
import { migrate } from 'drizzle-orm/postgres-js/migrator'
import { client, db } from './'
export const migrateDatabase = async () => {
await migrate(db, { migrationsFolder: path.join(__dirname, 'migrations') })
await client.end()
}

@ -0,0 +1,86 @@
DO $$ BEGIN
CREATE TYPE "public"."status" AS ENUM('draft', 'review', 'published');
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "user" (
"id" varchar(32) PRIMARY KEY NOT NULL,
"name" text,
"email" text NOT NULL,
CONSTRAINT "user_email_unique" UNIQUE("email")
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "access_token" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"userId" varchar(32),
"token" text NOT NULL,
"name" text NOT NULL,
"createdAt" timestamp DEFAULT now() NOT NULL,
"lastUsedOn" timestamp
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "changelog" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"createdAt" timestamp DEFAULT now(),
"updatedAt" timestamp,
"userId" varchar(32),
"title" varchar(256),
"description" text,
"isSemver" boolean DEFAULT true
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "changelog_commit" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"createdAt" timestamp,
"changelogId" uuid,
"versionId" uuid,
"shortHash" varchar(8) NOT NULL,
"author" json,
"body" text,
"message" text NOT NULL
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "changelog_version" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"createdAt" timestamp DEFAULT now(),
"updatedAt" timestamp,
"releasedAt" timestamp,
"changelogId" uuid NOT NULL,
"version" varchar(32) NOT NULL,
"markdown" text NOT NULL,
"status" "status" DEFAULT 'draft' NOT NULL,
"shortHash" varchar(8) NOT NULL
);
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "access_token" ADD CONSTRAINT "access_token_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "changelog" ADD CONSTRAINT "changelog_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "changelog_commit" ADD CONSTRAINT "changelog_commit_changelogId_changelog_id_fk" FOREIGN KEY ("changelogId") REFERENCES "public"."changelog"("id") ON DELETE cascade ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "changelog_commit" ADD CONSTRAINT "changelog_commit_versionId_changelog_version_id_fk" FOREIGN KEY ("versionId") REFERENCES "public"."changelog_version"("id") ON DELETE cascade ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "changelog_version" ADD CONSTRAINT "changelog_version_changelogId_changelog_id_fk" FOREIGN KEY ("changelogId") REFERENCES "public"."changelog"("id") ON DELETE cascade ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
CREATE UNIQUE INDEX IF NOT EXISTS "unique" ON "changelog_commit" USING btree ("changelogId","shortHash");

@ -0,0 +1,383 @@
{
"id": "33a139be-9cb3-4dfd-a14c-c196e17b053d",
"prevId": "00000000-0000-0000-0000-000000000000",
"version": "7",
"dialect": "postgresql",
"tables": {
"public.user": {
"name": "user",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(32)",
"primaryKey": true,
"notNull": true
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"user_email_unique": {
"name": "user_email_unique",
"nullsNotDistinct": false,
"columns": [
"email"
]
}
}
},
"public.access_token": {
"name": "access_token",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"userId": {
"name": "userId",
"type": "varchar(32)",
"primaryKey": false,
"notNull": false
},
"token": {
"name": "token",
"type": "text",
"primaryKey": false,
"notNull": true
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true
},
"createdAt": {
"name": "createdAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"lastUsedOn": {
"name": "lastUsedOn",
"type": "timestamp",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"access_token_userId_user_id_fk": {
"name": "access_token_userId_user_id_fk",
"tableFrom": "access_token",
"tableTo": "user",
"columnsFrom": [
"userId"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.changelog": {
"name": "changelog",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"createdAt": {
"name": "createdAt",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"default": "now()"
},
"updatedAt": {
"name": "updatedAt",
"type": "timestamp",
"primaryKey": false,
"notNull": false
},
"userId": {
"name": "userId",
"type": "varchar(32)",
"primaryKey": false,
"notNull": false
},
"title": {
"name": "title",
"type": "varchar(256)",
"primaryKey": false,
"notNull": false
},
"description": {
"name": "description",
"type": "text",
"primaryKey": false,
"notNull": false
},
"isSemver": {
"name": "isSemver",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"default": true
}
},
"indexes": {},
"foreignKeys": {
"changelog_userId_user_id_fk": {
"name": "changelog_userId_user_id_fk",
"tableFrom": "changelog",
"tableTo": "user",
"columnsFrom": [
"userId"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.changelog_commit": {
"name": "changelog_commit",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"createdAt": {
"name": "createdAt",
"type": "timestamp",
"primaryKey": false,
"notNull": false
},
"changelogId": {
"name": "changelogId",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"versionId": {
"name": "versionId",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"shortHash": {
"name": "shortHash",
"type": "varchar(8)",
"primaryKey": false,
"notNull": true
},
"author": {
"name": "author",
"type": "json",
"primaryKey": false,
"notNull": false
},
"body": {
"name": "body",
"type": "text",
"primaryKey": false,
"notNull": false
},
"message": {
"name": "message",
"type": "text",
"primaryKey": false,
"notNull": true
}
},
"indexes": {
"unique": {
"name": "unique",
"columns": [
{
"expression": "changelogId",
"isExpression": false,
"asc": true,
"nulls": "last"
},
{
"expression": "shortHash",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": true,
"concurrently": false,
"method": "btree",
"with": {}
}
},
"foreignKeys": {
"changelog_commit_changelogId_changelog_id_fk": {
"name": "changelog_commit_changelogId_changelog_id_fk",
"tableFrom": "changelog_commit",
"tableTo": "changelog",
"columnsFrom": [
"changelogId"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
},
"changelog_commit_versionId_changelog_version_id_fk": {
"name": "changelog_commit_versionId_changelog_version_id_fk",
"tableFrom": "changelog_commit",
"tableTo": "changelog_version",
"columnsFrom": [
"versionId"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"public.changelog_version": {
"name": "changelog_version",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"createdAt": {
"name": "createdAt",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"default": "now()"
},
"updatedAt": {
"name": "updatedAt",
"type": "timestamp",
"primaryKey": false,
"notNull": false
},
"releasedAt": {
"name": "releasedAt",
"type": "timestamp",
"primaryKey": false,
"notNull": false
},
"changelogId": {
"name": "changelogId",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"version": {
"name": "version",
"type": "varchar(32)",
"primaryKey": false,
"notNull": true
},
"markdown": {
"name": "markdown",
"type": "text",
"primaryKey": false,
"notNull": true
},
"status": {
"name": "status",
"type": "status",
"typeSchema": "public",
"primaryKey": false,
"notNull": true,
"default": "'draft'"
},
"shortHash": {
"name": "shortHash",
"type": "varchar(8)",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {
"changelog_version_changelogId_changelog_id_fk": {
"name": "changelog_version_changelogId_changelog_id_fk",
"tableFrom": "changelog_version",
"tableTo": "changelog",
"columnsFrom": [
"changelogId"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
}
},
"enums": {
"public.status": {
"name": "status",
"schema": "public",
"values": [
"draft",
"review",
"published"
]
}
},
"schemas": {},
"sequences": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}

@ -0,0 +1,13 @@
{
"version": "7",
"dialect": "postgresql",
"entries": [
{
"idx": 0,
"version": "7",
"when": 1727807735760,
"tag": "0000_crazy_mindworm",
"breakpoints": true
}
]
}

@ -17,7 +17,7 @@ export const changelog = pgTable('changelog', {
createdAt: timestamp('createdAt').defaultNow(),
updatedAt: timestamp('updatedAt'),
userId: text('userId').references(() => user.id, {
userId: varchar('userId', { length: 32 }).references(() => user.id, {
onDelete: 'cascade',
}),
@ -26,9 +26,13 @@ export const changelog = pgTable('changelog', {
isSemver: boolean('isSemver').default(true),
})
export const changelog_relation = relations(changelog, ({ many }) => ({
export const changelog_relation = relations(changelog, ({ many, one }) => ({
versions: many(changelog_version),
commits: many(changelog_commit),
user: one(user, {
fields: [changelog.userId],
references: [user.id],
}),
}))
export const changelog_version_status = pgEnum('status', [