feat(14-01): rewrite database foundation from SQLite to PostgreSQL
- Replace all 13 sqliteTable definitions with pgTable (pg-core) - Convert integer timestamps to native timestamp type with defaultNow() - Convert real columns to doublePrecision, integer used to boolean - Rewrite db connection to use postgres.js driver with DATABASE_URL - Rewrite migrate.ts to use postgres-js migrator targeting drizzle-pg/ - Convert seed.ts to async - Update drizzle.config.ts to postgresql dialect - Install postgres and @electric-sql/pglite, remove better-sqlite3
This commit is contained in:
8
bun.lock
8
bun.lock
@@ -5,6 +5,7 @@
|
|||||||
"": {
|
"": {
|
||||||
"name": "gearbox",
|
"name": "gearbox",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@electric-sql/pglite": "^0.4.3",
|
||||||
"@hono/zod-validator": "^0.7.6",
|
"@hono/zod-validator": "^0.7.6",
|
||||||
"@modelcontextprotocol/sdk": "^1.29.0",
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
||||||
"@tailwindcss/vite": "^4.2.1",
|
"@tailwindcss/vite": "^4.2.1",
|
||||||
@@ -15,6 +16,7 @@
|
|||||||
"framer-motion": "^12.38.0",
|
"framer-motion": "^12.38.0",
|
||||||
"hono": "^4.12.8",
|
"hono": "^4.12.8",
|
||||||
"lucide-react": "^0.577.0",
|
"lucide-react": "^0.577.0",
|
||||||
|
"postgres": "^3.4.8",
|
||||||
"react": "^19.2.4",
|
"react": "^19.2.4",
|
||||||
"react-dom": "^19.2.4",
|
"react-dom": "^19.2.4",
|
||||||
"recharts": "^3.8.0",
|
"recharts": "^3.8.0",
|
||||||
@@ -28,12 +30,10 @@
|
|||||||
"@tanstack/react-query-devtools": "^5.91.3",
|
"@tanstack/react-query-devtools": "^5.91.3",
|
||||||
"@tanstack/react-router-devtools": "^1.166.7",
|
"@tanstack/react-router-devtools": "^1.166.7",
|
||||||
"@tanstack/router-plugin": "^1.166.9",
|
"@tanstack/router-plugin": "^1.166.9",
|
||||||
"@types/better-sqlite3": "^7.6.13",
|
|
||||||
"@types/bun": "latest",
|
"@types/bun": "latest",
|
||||||
"@types/react": "^19.2.14",
|
"@types/react": "^19.2.14",
|
||||||
"@types/react-dom": "^19.2.3",
|
"@types/react-dom": "^19.2.3",
|
||||||
"@vitejs/plugin-react": "^6.0.1",
|
"@vitejs/plugin-react": "^6.0.1",
|
||||||
"better-sqlite3": "^12.8.0",
|
|
||||||
"concurrently": "^9.1.2",
|
"concurrently": "^9.1.2",
|
||||||
"drizzle-kit": "^0.31.9",
|
"drizzle-kit": "^0.31.9",
|
||||||
"vite": "^8.0.0",
|
"vite": "^8.0.0",
|
||||||
@@ -102,6 +102,8 @@
|
|||||||
|
|
||||||
"@drizzle-team/brocli": ["@drizzle-team/brocli@0.10.2", "", {}, "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w=="],
|
"@drizzle-team/brocli": ["@drizzle-team/brocli@0.10.2", "", {}, "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w=="],
|
||||||
|
|
||||||
|
"@electric-sql/pglite": ["@electric-sql/pglite@0.4.3", "", {}, "sha512-ichuWTgtd4mOM1G4SpyGJa5trT03lWbMypDV0fUXUCXg5hiHqVAz/bZyV68NqmkLB7WcYmj1RMJVSp8HV/v/ZQ=="],
|
||||||
|
|
||||||
"@emnapi/core": ["@emnapi/core@1.9.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.0", "tslib": "^2.4.0" } }, "sha512-0DQ98G9ZQZOxfUcQn1waV2yS8aWdZ6kJMbYCJB3oUBecjWYO1fqJ+a1DRfPF3O5JEkwqwP1A9QEN/9mYm2Yd0w=="],
|
"@emnapi/core": ["@emnapi/core@1.9.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.0", "tslib": "^2.4.0" } }, "sha512-0DQ98G9ZQZOxfUcQn1waV2yS8aWdZ6kJMbYCJB3oUBecjWYO1fqJ+a1DRfPF3O5JEkwqwP1A9QEN/9mYm2Yd0w=="],
|
||||||
|
|
||||||
"@emnapi/runtime": ["@emnapi/runtime@1.9.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-QN75eB0IH2ywSpRpNddCRfQIhmJYBCJ1x5Lb3IscKAL8bMnVAKnRg8dCoXbHzVLLH7P38N2Z3mtulB7W0J0FKw=="],
|
"@emnapi/runtime": ["@emnapi/runtime@1.9.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-QN75eB0IH2ywSpRpNddCRfQIhmJYBCJ1x5Lb3IscKAL8bMnVAKnRg8dCoXbHzVLLH7P38N2Z3mtulB7W0J0FKw=="],
|
||||||
@@ -684,6 +686,8 @@
|
|||||||
|
|
||||||
"postcss": ["postcss@8.5.8", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg=="],
|
"postcss": ["postcss@8.5.8", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg=="],
|
||||||
|
|
||||||
|
"postgres": ["postgres@3.4.8", "", {}, "sha512-d+JFcLM17njZaOLkv6SCev7uoLaBtfK86vMUXhW1Z4glPWh4jozno9APvW/XKFJ3CCxVoC7OL38BqRydtu5nGg=="],
|
||||||
|
|
||||||
"prebuild-install": ["prebuild-install@7.1.3", "", { "dependencies": { "detect-libc": "^2.0.0", "expand-template": "^2.0.3", "github-from-package": "0.0.0", "minimist": "^1.2.3", "mkdirp-classic": "^0.5.3", "napi-build-utils": "^2.0.0", "node-abi": "^3.3.0", "pump": "^3.0.0", "rc": "^1.2.7", "simple-get": "^4.0.0", "tar-fs": "^2.0.0", "tunnel-agent": "^0.6.0" }, "bin": { "prebuild-install": "bin.js" } }, "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug=="],
|
"prebuild-install": ["prebuild-install@7.1.3", "", { "dependencies": { "detect-libc": "^2.0.0", "expand-template": "^2.0.3", "github-from-package": "0.0.0", "minimist": "^1.2.3", "mkdirp-classic": "^0.5.3", "napi-build-utils": "^2.0.0", "node-abi": "^3.3.0", "pump": "^3.0.0", "rc": "^1.2.7", "simple-get": "^4.0.0", "tar-fs": "^2.0.0", "tunnel-agent": "^0.6.0" }, "bin": { "prebuild-install": "bin.js" } }, "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug=="],
|
||||||
|
|
||||||
"prettier": ["prettier@3.8.1", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg=="],
|
"prettier": ["prettier@3.8.1", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg=="],
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import { defineConfig } from "drizzle-kit";
|
import { defineConfig } from "drizzle-kit";
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
out: "./drizzle",
|
out: "./drizzle-pg",
|
||||||
schema: "./src/db/schema.ts",
|
schema: "./src/db/schema.ts",
|
||||||
dialect: "sqlite",
|
dialect: "postgresql",
|
||||||
dbCredentials: {
|
dbCredentials: {
|
||||||
url: process.env.DATABASE_PATH || "gearbox.db",
|
url:
|
||||||
|
process.env.DATABASE_URL ||
|
||||||
|
"postgresql://gearbox:gearbox@localhost:5432/gearbox",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -21,12 +21,10 @@
|
|||||||
"@tanstack/react-query-devtools": "^5.91.3",
|
"@tanstack/react-query-devtools": "^5.91.3",
|
||||||
"@tanstack/react-router-devtools": "^1.166.7",
|
"@tanstack/react-router-devtools": "^1.166.7",
|
||||||
"@tanstack/router-plugin": "^1.166.9",
|
"@tanstack/router-plugin": "^1.166.9",
|
||||||
"@types/better-sqlite3": "^7.6.13",
|
|
||||||
"@types/bun": "latest",
|
"@types/bun": "latest",
|
||||||
"@types/react": "^19.2.14",
|
"@types/react": "^19.2.14",
|
||||||
"@types/react-dom": "^19.2.3",
|
"@types/react-dom": "^19.2.3",
|
||||||
"@vitejs/plugin-react": "^6.0.1",
|
"@vitejs/plugin-react": "^6.0.1",
|
||||||
"better-sqlite3": "^12.8.0",
|
|
||||||
"concurrently": "^9.1.2",
|
"concurrently": "^9.1.2",
|
||||||
"drizzle-kit": "^0.31.9",
|
"drizzle-kit": "^0.31.9",
|
||||||
"vite": "^8.0.0"
|
"vite": "^8.0.0"
|
||||||
@@ -35,6 +33,7 @@
|
|||||||
"typescript": "^5.9.3"
|
"typescript": "^5.9.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@electric-sql/pglite": "^0.4.3",
|
||||||
"@hono/zod-validator": "^0.7.6",
|
"@hono/zod-validator": "^0.7.6",
|
||||||
"@modelcontextprotocol/sdk": "^1.29.0",
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
||||||
"@tailwindcss/vite": "^4.2.1",
|
"@tailwindcss/vite": "^4.2.1",
|
||||||
@@ -45,6 +44,7 @@
|
|||||||
"framer-motion": "^12.38.0",
|
"framer-motion": "^12.38.0",
|
||||||
"hono": "^4.12.8",
|
"hono": "^4.12.8",
|
||||||
"lucide-react": "^0.577.0",
|
"lucide-react": "^0.577.0",
|
||||||
|
"postgres": "^3.4.8",
|
||||||
"react": "^19.2.4",
|
"react": "^19.2.4",
|
||||||
"react-dom": "^19.2.4",
|
"react-dom": "^19.2.4",
|
||||||
"recharts": "^3.8.0",
|
"recharts": "^3.8.0",
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { Database } from "bun:sqlite";
|
import { drizzle } from "drizzle-orm/postgres-js";
|
||||||
import { drizzle } from "drizzle-orm/bun-sqlite";
|
import postgres from "postgres";
|
||||||
import * as schema from "./schema.ts";
|
import * as schema from "./schema.ts";
|
||||||
|
|
||||||
const sqlite = new Database(process.env.DATABASE_PATH || "gearbox.db");
|
const connectionString =
|
||||||
sqlite.run("PRAGMA journal_mode = WAL");
|
process.env.DATABASE_URL ||
|
||||||
sqlite.run("PRAGMA foreign_keys = ON");
|
"postgresql://gearbox:gearbox@localhost:5432/gearbox";
|
||||||
|
const queryClient = postgres(connectionString);
|
||||||
export const db = drizzle(sqlite, { schema });
|
export const db = drizzle(queryClient, { schema });
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
import { Database } from "bun:sqlite";
|
import { drizzle } from "drizzle-orm/postgres-js";
|
||||||
import { drizzle } from "drizzle-orm/bun-sqlite";
|
import { migrate } from "drizzle-orm/postgres-js/migrator";
|
||||||
import { migrate } from "drizzle-orm/bun-sqlite/migrator";
|
import postgres from "postgres";
|
||||||
|
|
||||||
const sqlite = new Database(process.env.DATABASE_PATH || "gearbox.db");
|
const connectionString =
|
||||||
sqlite.run("PRAGMA journal_mode = WAL");
|
process.env.DATABASE_URL ||
|
||||||
sqlite.run("PRAGMA foreign_keys = ON");
|
"postgresql://gearbox:gearbox@localhost:5432/gearbox";
|
||||||
|
const migrationClient = postgres(connectionString, { max: 1 });
|
||||||
|
const db = drizzle(migrationClient);
|
||||||
|
|
||||||
const db = drizzle(sqlite);
|
await migrate(db, { migrationsFolder: "./drizzle-pg" });
|
||||||
migrate(db, { migrationsFolder: "./drizzle" });
|
await migrationClient.end();
|
||||||
|
|
||||||
sqlite.close();
|
|
||||||
console.log("Migrations applied successfully");
|
console.log("Migrations applied successfully");
|
||||||
|
|||||||
128
src/db/schema.ts
128
src/db/schema.ts
@@ -1,18 +1,24 @@
|
|||||||
import { integer, real, sqliteTable, text } from "drizzle-orm/sqlite-core";
|
import {
|
||||||
|
boolean,
|
||||||
|
doublePrecision,
|
||||||
|
integer,
|
||||||
|
pgTable,
|
||||||
|
serial,
|
||||||
|
text,
|
||||||
|
timestamp,
|
||||||
|
} from "drizzle-orm/pg-core";
|
||||||
|
|
||||||
export const categories = sqliteTable("categories", {
|
export const categories = pgTable("categories", {
|
||||||
id: integer("id").primaryKey({ autoIncrement: true }),
|
id: serial("id").primaryKey(),
|
||||||
name: text("name").notNull().unique(),
|
name: text("name").notNull().unique(),
|
||||||
icon: text("icon").notNull().default("package"),
|
icon: text("icon").notNull().default("package"),
|
||||||
createdAt: integer("created_at", { mode: "timestamp" })
|
createdAt: timestamp("created_at").notNull().defaultNow(),
|
||||||
.notNull()
|
|
||||||
.$defaultFn(() => new Date()),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const items = sqliteTable("items", {
|
export const items = pgTable("items", {
|
||||||
id: integer("id").primaryKey({ autoIncrement: true }),
|
id: serial("id").primaryKey(),
|
||||||
name: text("name").notNull(),
|
name: text("name").notNull(),
|
||||||
weightGrams: real("weight_grams"),
|
weightGrams: doublePrecision("weight_grams"),
|
||||||
priceCents: integer("price_cents"),
|
priceCents: integer("price_cents"),
|
||||||
categoryId: integer("category_id")
|
categoryId: integer("category_id")
|
||||||
.notNull()
|
.notNull()
|
||||||
@@ -22,37 +28,29 @@ export const items = sqliteTable("items", {
|
|||||||
imageFilename: text("image_filename"),
|
imageFilename: text("image_filename"),
|
||||||
imageSourceUrl: text("image_source_url"),
|
imageSourceUrl: text("image_source_url"),
|
||||||
quantity: integer("quantity").notNull().default(1),
|
quantity: integer("quantity").notNull().default(1),
|
||||||
createdAt: integer("created_at", { mode: "timestamp" })
|
createdAt: timestamp("created_at").notNull().defaultNow(),
|
||||||
.notNull()
|
updatedAt: timestamp("updated_at").notNull().defaultNow(),
|
||||||
.$defaultFn(() => new Date()),
|
|
||||||
updatedAt: integer("updated_at", { mode: "timestamp" })
|
|
||||||
.notNull()
|
|
||||||
.$defaultFn(() => new Date()),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const threads = sqliteTable("threads", {
|
export const threads = pgTable("threads", {
|
||||||
id: integer("id").primaryKey({ autoIncrement: true }),
|
id: serial("id").primaryKey(),
|
||||||
name: text("name").notNull(),
|
name: text("name").notNull(),
|
||||||
status: text("status").notNull().default("active"),
|
status: text("status").notNull().default("active"),
|
||||||
resolvedCandidateId: integer("resolved_candidate_id"),
|
resolvedCandidateId: integer("resolved_candidate_id"),
|
||||||
categoryId: integer("category_id")
|
categoryId: integer("category_id")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => categories.id),
|
.references(() => categories.id),
|
||||||
createdAt: integer("created_at", { mode: "timestamp" })
|
createdAt: timestamp("created_at").notNull().defaultNow(),
|
||||||
.notNull()
|
updatedAt: timestamp("updated_at").notNull().defaultNow(),
|
||||||
.$defaultFn(() => new Date()),
|
|
||||||
updatedAt: integer("updated_at", { mode: "timestamp" })
|
|
||||||
.notNull()
|
|
||||||
.$defaultFn(() => new Date()),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const threadCandidates = sqliteTable("thread_candidates", {
|
export const threadCandidates = pgTable("thread_candidates", {
|
||||||
id: integer("id").primaryKey({ autoIncrement: true }),
|
id: serial("id").primaryKey(),
|
||||||
threadId: integer("thread_id")
|
threadId: integer("thread_id")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => threads.id, { onDelete: "cascade" }),
|
.references(() => threads.id, { onDelete: "cascade" }),
|
||||||
name: text("name").notNull(),
|
name: text("name").notNull(),
|
||||||
weightGrams: real("weight_grams"),
|
weightGrams: doublePrecision("weight_grams"),
|
||||||
priceCents: integer("price_cents"),
|
priceCents: integer("price_cents"),
|
||||||
categoryId: integer("category_id")
|
categoryId: integer("category_id")
|
||||||
.notNull()
|
.notNull()
|
||||||
@@ -64,28 +62,20 @@ export const threadCandidates = sqliteTable("thread_candidates", {
|
|||||||
status: text("status").notNull().default("researching"),
|
status: text("status").notNull().default("researching"),
|
||||||
pros: text("pros"),
|
pros: text("pros"),
|
||||||
cons: text("cons"),
|
cons: text("cons"),
|
||||||
sortOrder: real("sort_order").notNull().default(0),
|
sortOrder: doublePrecision("sort_order").notNull().default(0),
|
||||||
createdAt: integer("created_at", { mode: "timestamp" })
|
createdAt: timestamp("created_at").notNull().defaultNow(),
|
||||||
.notNull()
|
updatedAt: timestamp("updated_at").notNull().defaultNow(),
|
||||||
.$defaultFn(() => new Date()),
|
|
||||||
updatedAt: integer("updated_at", { mode: "timestamp" })
|
|
||||||
.notNull()
|
|
||||||
.$defaultFn(() => new Date()),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const setups = sqliteTable("setups", {
|
export const setups = pgTable("setups", {
|
||||||
id: integer("id").primaryKey({ autoIncrement: true }),
|
id: serial("id").primaryKey(),
|
||||||
name: text("name").notNull(),
|
name: text("name").notNull(),
|
||||||
createdAt: integer("created_at", { mode: "timestamp" })
|
createdAt: timestamp("created_at").notNull().defaultNow(),
|
||||||
.notNull()
|
updatedAt: timestamp("updated_at").notNull().defaultNow(),
|
||||||
.$defaultFn(() => new Date()),
|
|
||||||
updatedAt: integer("updated_at", { mode: "timestamp" })
|
|
||||||
.notNull()
|
|
||||||
.$defaultFn(() => new Date()),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const setupItems = sqliteTable("setup_items", {
|
export const setupItems = pgTable("setup_items", {
|
||||||
id: integer("id").primaryKey({ autoIncrement: true }),
|
id: serial("id").primaryKey(),
|
||||||
setupId: integer("setup_id")
|
setupId: integer("setup_id")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => setups.id, { onDelete: "cascade" }),
|
.references(() => setups.id, { onDelete: "cascade" }),
|
||||||
@@ -95,69 +85,59 @@ export const setupItems = sqliteTable("setup_items", {
|
|||||||
classification: text("classification").notNull().default("base"),
|
classification: text("classification").notNull().default("base"),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const settings = sqliteTable("settings", {
|
export const settings = pgTable("settings", {
|
||||||
key: text("key").primaryKey(),
|
key: text("key").primaryKey(),
|
||||||
value: text("value").notNull(),
|
value: text("value").notNull(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const users = sqliteTable("users", {
|
export const users = pgTable("users", {
|
||||||
id: integer("id").primaryKey({ autoIncrement: true }),
|
id: serial("id").primaryKey(),
|
||||||
username: text("username").notNull().unique(),
|
username: text("username").notNull().unique(),
|
||||||
passwordHash: text("password_hash").notNull(),
|
passwordHash: text("password_hash").notNull(),
|
||||||
createdAt: integer("created_at", { mode: "timestamp" })
|
createdAt: timestamp("created_at").notNull().defaultNow(),
|
||||||
.notNull()
|
|
||||||
.$defaultFn(() => new Date()),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const sessions = sqliteTable("sessions", {
|
export const sessions = pgTable("sessions", {
|
||||||
id: text("id").primaryKey(),
|
id: text("id").primaryKey(),
|
||||||
userId: integer("user_id")
|
userId: integer("user_id")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => users.id, { onDelete: "cascade" }),
|
.references(() => users.id, { onDelete: "cascade" }),
|
||||||
expiresAt: integer("expires_at", { mode: "timestamp" }).notNull(),
|
expiresAt: timestamp("expires_at").notNull(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const apiKeys = sqliteTable("api_keys", {
|
export const apiKeys = pgTable("api_keys", {
|
||||||
id: integer("id").primaryKey({ autoIncrement: true }),
|
id: serial("id").primaryKey(),
|
||||||
name: text("name").notNull(),
|
name: text("name").notNull(),
|
||||||
keyHash: text("key_hash").notNull(),
|
keyHash: text("key_hash").notNull(),
|
||||||
keyPrefix: text("key_prefix").notNull(),
|
keyPrefix: text("key_prefix").notNull(),
|
||||||
createdAt: integer("created_at", { mode: "timestamp" })
|
createdAt: timestamp("created_at").notNull().defaultNow(),
|
||||||
.notNull()
|
|
||||||
.$defaultFn(() => new Date()),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const oauthClients = sqliteTable("oauth_clients", {
|
export const oauthClients = pgTable("oauth_clients", {
|
||||||
id: integer("id").primaryKey({ autoIncrement: true }),
|
id: serial("id").primaryKey(),
|
||||||
clientId: text("client_id").notNull().unique(),
|
clientId: text("client_id").notNull().unique(),
|
||||||
clientName: text("client_name"),
|
clientName: text("client_name"),
|
||||||
redirectUris: text("redirect_uris").notNull(), // JSON array
|
redirectUris: text("redirect_uris").notNull(), // JSON array
|
||||||
createdAt: integer("created_at", { mode: "timestamp" })
|
createdAt: timestamp("created_at").notNull().defaultNow(),
|
||||||
.notNull()
|
|
||||||
.$defaultFn(() => new Date()),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const oauthCodes = sqliteTable("oauth_codes", {
|
export const oauthCodes = pgTable("oauth_codes", {
|
||||||
id: integer("id").primaryKey({ autoIncrement: true }),
|
id: serial("id").primaryKey(),
|
||||||
code: text("code").notNull().unique(),
|
code: text("code").notNull().unique(),
|
||||||
clientId: text("client_id").notNull(),
|
clientId: text("client_id").notNull(),
|
||||||
codeChallenge: text("code_challenge").notNull(),
|
codeChallenge: text("code_challenge").notNull(),
|
||||||
codeChallengeMethod: text("code_challenge_method").notNull().default("S256"),
|
codeChallengeMethod: text("code_challenge_method").notNull().default("S256"),
|
||||||
redirectUri: text("redirect_uri").notNull(),
|
redirectUri: text("redirect_uri").notNull(),
|
||||||
expiresAt: integer("expires_at", { mode: "timestamp" }).notNull(),
|
expiresAt: timestamp("expires_at").notNull(),
|
||||||
used: integer("used").notNull().default(0),
|
used: boolean("used").notNull().default(false),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const oauthTokens = sqliteTable("oauth_tokens", {
|
export const oauthTokens = pgTable("oauth_tokens", {
|
||||||
id: integer("id").primaryKey({ autoIncrement: true }),
|
id: serial("id").primaryKey(),
|
||||||
accessTokenHash: text("access_token_hash").notNull().unique(),
|
accessTokenHash: text("access_token_hash").notNull().unique(),
|
||||||
refreshTokenHash: text("refresh_token_hash").notNull().unique(),
|
refreshTokenHash: text("refresh_token_hash").notNull().unique(),
|
||||||
clientId: text("client_id").notNull(),
|
clientId: text("client_id").notNull(),
|
||||||
expiresAt: integer("expires_at", { mode: "timestamp" }).notNull(), // access token expiry
|
expiresAt: timestamp("expires_at").notNull(), // access token expiry
|
||||||
refreshExpiresAt: integer("refresh_expires_at", {
|
refreshExpiresAt: timestamp("refresh_expires_at").notNull(), // refresh token expiry
|
||||||
mode: "timestamp",
|
createdAt: timestamp("created_at").notNull().defaultNow(),
|
||||||
}).notNull(), // refresh token expiry
|
|
||||||
createdAt: integer("created_at", { mode: "timestamp" })
|
|
||||||
.notNull()
|
|
||||||
.$defaultFn(() => new Date()),
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
import { db } from "./index.ts";
|
import { db } from "./index.ts";
|
||||||
import { categories } from "./schema.ts";
|
import { categories } from "./schema.ts";
|
||||||
|
|
||||||
export function seedDefaults() {
|
export async function seedDefaults() {
|
||||||
const existing = db.select().from(categories).all();
|
const existing = await db.select().from(categories);
|
||||||
if (existing.length === 0) {
|
if (existing.length === 0) {
|
||||||
db.insert(categories)
|
await db.insert(categories).values({
|
||||||
.values({
|
name: "Uncategorized",
|
||||||
name: "Uncategorized",
|
icon: "package",
|
||||||
icon: "package",
|
});
|
||||||
})
|
|
||||||
.run();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user