diff --git a/drizzle.config.ts b/drizzle.config.ts new file mode 100644 index 0000000..6a04468 --- /dev/null +++ b/drizzle.config.ts @@ -0,0 +1,13 @@ +import type { Config } from "drizzle-kit"; + +export default { + schema: "./src/app/db/schema/users.ts", + out: "./src/app/db/migrations", + // dbCredentials: { + // host: String(process.env.DB_HOST), + // port: Number(process.env.DB_PORT), + // user: process.env.DB_USERNAME, + // password: process.env.DB_PASSWORD, + // database: String(process.env.DB_NAME), + // }, +} satisfies Config; diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json new file mode 100644 index 0000000..5dcbf6a --- /dev/null +++ b/drizzle/meta/_journal.json @@ -0,0 +1 @@ +{"version":"5","dialect":"pg","entries":[]} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index f1cc5d3..4897475 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,7 @@ "autoprefixer": "10.4.14", "class-variance-authority": "^0.6.0", "clsx": "^1.2.1", - "drizzle-orm": "^0.27.0", + "drizzle-orm": "^0.27.1", "eslint": "8.41.0", "eslint-config-next": "13.4.3", "lucide-react": "^0.233.0", @@ -38,6 +38,7 @@ }, "devDependencies": { "@types/pg": "^8.10.2", + "dotenv": "^16.3.1", "drizzle-kit": "^0.19.3" } }, @@ -2682,6 +2683,18 @@ "node": ">=6.0.0" } }, + "node_modules/dotenv": { + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=1" + } + }, "node_modules/dreamopt": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/dreamopt/-/dreamopt-0.8.0.tgz", @@ -2794,9 +2807,9 @@ } }, "node_modules/drizzle-orm": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.27.0.tgz", - "integrity": "sha512-LGiJ0icB+wQwgbSCOvAjONY8Ec6G/EDzQQP5PmUaQYeI9OqgpVKHC2T1fFIbvk5dabWsbokJ5NOciVAxriStig==", + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.27.1.tgz", + "integrity": "sha512-SLZQBzGusKuB6fqHlRrWrNPFQHef1bS/VYPvJH4XfNPOHx/Sgs++UKKctgKDn7bwqwRBKs0rbvvJ561drCOo/g==", "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=3", diff --git a/package.json b/package.json index 80390f1..b1cdd2e 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,9 @@ "dev": "next dev", "build": "next build", "start": "next start", - "lint": "next lint" + "lint": "next lint", + "migration:generate": "drizzle-kit generate:pg --config=drizzle.config.ts", + "migration:push": "node -r esbuild-register src/app/db/migrate.ts" }, "dependencies": { "@headlessui/react": "^1.7.14", @@ -21,7 +23,7 @@ "autoprefixer": "10.4.14", "class-variance-authority": "^0.6.0", "clsx": "^1.2.1", - "drizzle-orm": "^0.27.0", + "drizzle-orm": "^0.27.1", "eslint": "8.41.0", "eslint-config-next": "13.4.3", "lucide-react": "^0.233.0", @@ -39,6 +41,7 @@ }, "devDependencies": { "@types/pg": "^8.10.2", + "dotenv": "^16.3.1", "drizzle-kit": "^0.19.3" } } diff --git a/src/app/db/db.ts b/src/app/db/db.ts index bc7b8de..ac4c851 100644 --- a/src/app/db/db.ts +++ b/src/app/db/db.ts @@ -11,4 +11,4 @@ const pool = new Pool({ database: process.env.DB_NAME, }); -const db = drizzle(pool); +export const db = drizzle(pool); diff --git a/src/app/db/migrate.ts b/src/app/db/migrate.ts new file mode 100644 index 0000000..1ba0156 --- /dev/null +++ b/src/app/db/migrate.ts @@ -0,0 +1,27 @@ +import { migrate } from "drizzle-orm/node-postgres/migrator"; +import { drizzle } from "drizzle-orm/node-postgres"; +import { Pool } from "pg"; +import dotenv from "dotenv"; + +dotenv.config({ path: ".env.local" }); + +const pool = new Pool({ + host: process.env.DB_HOST, + port: Number(process.env.DB_PORT), + user: process.env.DB_USERNAME, + password: process.env.DB_PASSWORD, + database: process.env.DB_NAME, +}); + +const db = drizzle(pool); + +// this will automatically run needed migrations on the database +migrate(db, { migrationsFolder: "./src/app/db/migrations" }) + .then(() => { + console.log("Migrations complete!"); + process.exit(0); + }) + .catch((err) => { + console.error("Migrations failed!", err); + process.exit(1); + }); diff --git a/src/app/db/migrations/0000_magenta_clea.sql b/src/app/db/migrations/0000_magenta_clea.sql new file mode 100644 index 0000000..51c55cb --- /dev/null +++ b/src/app/db/migrations/0000_magenta_clea.sql @@ -0,0 +1,9 @@ +CREATE TABLE IF NOT EXISTS "user" ( + "id" serial PRIMARY KEY NOT NULL, + "name" text, + "email" text, + "oauth_id" text, + "oauth_provider" text, + "created_at" timestamp, + "updated_at" timestamp +); diff --git a/src/app/db/migrations/meta/0000_snapshot.json b/src/app/db/migrations/meta/0000_snapshot.json new file mode 100644 index 0000000..8c61663 --- /dev/null +++ b/src/app/db/migrations/meta/0000_snapshot.json @@ -0,0 +1,66 @@ +{ + "version": "5", + "dialect": "pg", + "id": "9cd7955c-6b58-415a-baab-bab2d5e5ff12", + "prevId": "00000000-0000-0000-0000-000000000000", + "tables": { + "user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "oauth_id": { + "name": "oauth_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "oauth_provider": { + "name": "oauth_provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {} + } + }, + "enums": {}, + "schemas": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + } +} \ No newline at end of file diff --git a/src/app/db/migrations/meta/_journal.json b/src/app/db/migrations/meta/_journal.json new file mode 100644 index 0000000..43e1270 --- /dev/null +++ b/src/app/db/migrations/meta/_journal.json @@ -0,0 +1,13 @@ +{ + "version": "5", + "dialect": "pg", + "entries": [ + { + "idx": 0, + "version": "5", + "when": 1689006611034, + "tag": "0000_magenta_clea", + "breakpoints": true + } + ] +} \ No newline at end of file diff --git a/src/app/db/schema/users.ts b/src/app/db/schema/users.ts index e69de29..a7f1326 100644 --- a/src/app/db/schema/users.ts +++ b/src/app/db/schema/users.ts @@ -0,0 +1,12 @@ +import { serial, text, timestamp, pgTable } from "drizzle-orm/pg-core"; + +export const user = pgTable("user", { + id: serial("id").primaryKey(), + name: text("name"), + email: text("email"), + oauthId: text("oauth_id"), + oauthProvider: text("oauth_provider").$type<"google">(), + // role: text("role").$type<"admin" | "write" | "read">(), + createdAt: timestamp("created_at"), + updatedAt: timestamp("updated_at"), +}); diff --git a/tsconfig.json b/tsconfig.json index d71946f..bda1072 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,6 +2,7 @@ "compilerOptions": { "allowSyntheticDefaultImports": true, "target": "es5", + // "target": "ESNext", "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, @@ -24,6 +25,12 @@ "@/*": ["./src/*"] } }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + "src/app/db/schema/users.ts" + ], "exclude": ["node_modules"] }