feat(01-01): define DB schema, configure Drizzle Kit, write seed script
- Created src/lib/db/schema.ts with users table (id, email, password_hash, created_at) - Created src/lib/db/index.ts exporting db singleton via Drizzle + Neon HTTP driver - Created drizzle.config.ts pointing to schema.ts with postgresql dialect - Created scripts/seed.ts reading AGENT_EMAIL/AGENT_PASSWORD to create hashed user row - Generated drizzle/0000_milky_black_cat.sql migration (committed per user decision) - db:migrate and db:seed pending user Neon database setup
This commit is contained in:
11
teressa-copeland-homes/drizzle.config.ts
Normal file
11
teressa-copeland-homes/drizzle.config.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import "dotenv/config";
|
||||||
|
import { defineConfig } from "drizzle-kit";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
schema: "./src/lib/db/schema.ts",
|
||||||
|
out: "./drizzle",
|
||||||
|
dialect: "postgresql",
|
||||||
|
dbCredentials: {
|
||||||
|
url: process.env.DATABASE_URL!,
|
||||||
|
},
|
||||||
|
});
|
||||||
7
teressa-copeland-homes/drizzle/0000_milky_black_cat.sql
Normal file
7
teressa-copeland-homes/drizzle/0000_milky_black_cat.sql
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
CREATE TABLE "users" (
|
||||||
|
"id" text PRIMARY KEY NOT NULL,
|
||||||
|
"email" text NOT NULL,
|
||||||
|
"password_hash" text NOT NULL,
|
||||||
|
"created_at" timestamp DEFAULT now() NOT NULL,
|
||||||
|
CONSTRAINT "users_email_unique" UNIQUE("email")
|
||||||
|
);
|
||||||
65
teressa-copeland-homes/drizzle/meta/0000_snapshot.json
Normal file
65
teressa-copeland-homes/drizzle/meta/0000_snapshot.json
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
{
|
||||||
|
"id": "67889008-a13f-4b7e-9a7e-87d0e0ca3fd4",
|
||||||
|
"prevId": "00000000-0000-0000-0000-000000000000",
|
||||||
|
"version": "7",
|
||||||
|
"dialect": "postgresql",
|
||||||
|
"tables": {
|
||||||
|
"public.users": {
|
||||||
|
"name": "users",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"name": "email",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"password_hash": {
|
||||||
|
"name": "password_hash",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {
|
||||||
|
"users_email_unique": {
|
||||||
|
"name": "users_email_unique",
|
||||||
|
"nullsNotDistinct": false,
|
||||||
|
"columns": [
|
||||||
|
"email"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"enums": {},
|
||||||
|
"schemas": {},
|
||||||
|
"sequences": {},
|
||||||
|
"roles": {},
|
||||||
|
"policies": {},
|
||||||
|
"views": {},
|
||||||
|
"_meta": {
|
||||||
|
"columns": {},
|
||||||
|
"schemas": {},
|
||||||
|
"tables": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
13
teressa-copeland-homes/drizzle/meta/_journal.json
Normal file
13
teressa-copeland-homes/drizzle/meta/_journal.json
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"version": "7",
|
||||||
|
"dialect": "postgresql",
|
||||||
|
"entries": [
|
||||||
|
{
|
||||||
|
"idx": 0,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1773948629637,
|
||||||
|
"tag": "0000_milky_black_cat",
|
||||||
|
"breakpoints": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
29
teressa-copeland-homes/scripts/seed.ts
Normal file
29
teressa-copeland-homes/scripts/seed.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import "dotenv/config";
|
||||||
|
import { drizzle } from "drizzle-orm/neon-http";
|
||||||
|
import { neon } from "@neondatabase/serverless";
|
||||||
|
import { users } from "../src/lib/db/schema";
|
||||||
|
import bcrypt from "bcryptjs";
|
||||||
|
|
||||||
|
const sql = neon(process.env.DATABASE_URL!);
|
||||||
|
const db = drizzle({ client: sql });
|
||||||
|
|
||||||
|
async function seed() {
|
||||||
|
const email = process.env.AGENT_EMAIL;
|
||||||
|
const password = process.env.AGENT_PASSWORD;
|
||||||
|
|
||||||
|
if (!email || !password) {
|
||||||
|
throw new Error("AGENT_EMAIL and AGENT_PASSWORD env vars are required");
|
||||||
|
}
|
||||||
|
|
||||||
|
const passwordHash = await bcrypt.hash(password, 12);
|
||||||
|
|
||||||
|
await db.insert(users).values({ email, passwordHash }).onConflictDoNothing();
|
||||||
|
|
||||||
|
console.log(`Seeded agent account: ${email}`);
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
seed().catch((err) => {
|
||||||
|
console.error(err);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
6
teressa-copeland-homes/src/lib/db/index.ts
Normal file
6
teressa-copeland-homes/src/lib/db/index.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { drizzle } from "drizzle-orm/neon-http";
|
||||||
|
import { neon } from "@neondatabase/serverless";
|
||||||
|
import * as schema from "./schema";
|
||||||
|
|
||||||
|
const sql = neon(process.env.DATABASE_URL!);
|
||||||
|
export const db = drizzle({ client: sql, schema });
|
||||||
8
teressa-copeland-homes/src/lib/db/schema.ts
Normal file
8
teressa-copeland-homes/src/lib/db/schema.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { pgTable, text, timestamp } from "drizzle-orm/pg-core";
|
||||||
|
|
||||||
|
export const users = pgTable("users", {
|
||||||
|
id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
|
||||||
|
email: text("email").notNull().unique(),
|
||||||
|
passwordHash: text("password_hash").notNull(),
|
||||||
|
createdAt: timestamp("created_at").defaultNow().notNull(),
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user