diff --git a/apps/api/src/database/db.ts b/apps/api/src/database/db.ts index e69de29..98e9934 100644 --- a/apps/api/src/database/db.ts +++ b/apps/api/src/database/db.ts @@ -0,0 +1,12 @@ +import { Kysely, PostgresDialect } from 'kysely'; +import { type DB } from 'kysely-codegen'; +import { Pool } from "pg"; +import { env } from '../util'; + +export const db = new Kysely({ + dialect: new PostgresDialect({ + pool: new Pool({ + connectionString: env.DATABASE_URL, + }), + }) +}); diff --git a/apps/api/src/database/seed.ts b/apps/api/src/database/seed.ts index 72886df..ffad2ee 100644 --- a/apps/api/src/database/seed.ts +++ b/apps/api/src/database/seed.ts @@ -1,12 +1,28 @@ -import { Kysely, PostgresDialect } from 'kysely'; -import { type DB } from 'kysely-codegen'; -import { Pool } from "pg"; -import { env } from '../index'; +import { CreateTableBuilder, sql } from "kysely"; +import { db } from "./db"; +import { log } from "../util"; -const db = new Kysely({ - dialect: new PostgresDialect({ - pool: new Pool({ - connectionString: env.DATABASE_URL, - }), - }) -}); +// ===================== +// SYNC RUNS +// ===================== +{ + log.info({ name: "sync_runs" }, "Creating table"); + await db.schema.dropTable("sync_runs").ifExists().execute() + await db.schema.createTable("sync_runs") + .$call(addDefaultColumns) + .addColumn("completed_at", "timestamptz") + .execute() +} + +function addDefaultColumns(ctb: CreateTableBuilder) { + return ctb + .addColumn("id", "bigint", (cb) => cb + .generatedAlwaysAsIdentity() + .primaryKey() + ) + .addColumn("created_at", "timestamptz", (cb) => cb + .notNull() + .defaultTo(sql`now()`) + .unique() + ) +}; diff --git a/apps/api/src/index.ts b/apps/api/src/index.ts index ceee255..bb898d6 100644 --- a/apps/api/src/index.ts +++ b/apps/api/src/index.ts @@ -1,28 +1,7 @@ import cluster from 'node:cluster' import os from 'node:os' import process from 'node:process' -import { pino } from 'pino'; - -export const log = pino({ - level: Bun.env.LOG_LEVEL ?? "info", - transport: Bun.env.NODE_ENV != "production" - ? { target: "pino-pretty" } - : undefined, -}); - -export const env = { - PRINTFUL_AUTH: requireEnv("PRINTFUL_AUTH"), - PRINTFUL_STORE_ID: requireEnv("PRINTFUL_STORE_ID"), - WEBFLOW_SITE_ID: requireEnv("WEBFLOW_SITE_ID"), - WEBFLOW_COLLECTION_ID: requireEnv("WEBFLOW_COLLECTION_ID"), - WEBFLOW_AUTH: requireEnv("WEBFLOW_AUTH"), - WEBFLOW_WEBHOOK_SECRET: requireEnv("WEBFLOW_WEBHOOK_SECRET"), - AUTH_SECRET: requireEnv("AUTH_SECRET"), - ADMIN_PASSWORD: requireEnv("ADMIN_PASSWORD"), - DATABASE_URL: requireEnv("DATABASE_URL"), - NODE_ENV: optionEnv("NODE_ENV", "development"), - LOG_LEVEL: optionEnv("LOG_LEVEL", "info"), -}; +import { log } from "./util"; if (cluster.isPrimary) { for (let i = 0; i < os.availableParallelism(); i++) @@ -31,21 +10,3 @@ if (cluster.isPrimary) { await import('./server') log.info(`Worker ${process.pid} started`) } - -function requireEnv(key: string): string { - const val = Bun.env[key]; - if (!val) { - log.error({ name: key }, "Missing required environment variable"); - throw new Error(`Missing required environment variable: ${key}`) - }; - return val; -} - -function optionEnv(key: string, fallback: string = ""): string { - const val = Bun.env[key]; - if (!val) { - log.warn({ name: key }, `Missing optional environmental variable, falling back to "${fallback}"`) - return fallback - }; - return val; -} diff --git a/apps/api/src/server.ts b/apps/api/src/server.ts index b9787a7..26a0c46 100644 --- a/apps/api/src/server.ts +++ b/apps/api/src/server.ts @@ -20,7 +20,7 @@ import { Webflow, } from "@blade-and-brawn/commerce"; import zipcodesUs from "zipcodes-us"; -import { env, log } from "./index"; +import { env, log } from "./util"; import serverTiming from "@elysia/server-timing"; import jwt from "@elysia/jwt"; diff --git a/apps/api/src/util.ts b/apps/api/src/util.ts new file mode 100644 index 0000000..b3cc7df --- /dev/null +++ b/apps/api/src/util.ts @@ -0,0 +1,40 @@ +import { pino } from 'pino'; + +export const log = pino({ + level: Bun.env.LOG_LEVEL ?? "info", + transport: Bun.env.NODE_ENV != "production" + ? { target: "pino-pretty" } + : undefined, +}); + +export const env = { + PRINTFUL_AUTH: requireEnv("PRINTFUL_AUTH"), + PRINTFUL_STORE_ID: requireEnv("PRINTFUL_STORE_ID"), + WEBFLOW_SITE_ID: requireEnv("WEBFLOW_SITE_ID"), + WEBFLOW_COLLECTION_ID: requireEnv("WEBFLOW_COLLECTION_ID"), + WEBFLOW_AUTH: requireEnv("WEBFLOW_AUTH"), + WEBFLOW_WEBHOOK_SECRET: requireEnv("WEBFLOW_WEBHOOK_SECRET"), + AUTH_SECRET: requireEnv("AUTH_SECRET"), + ADMIN_PASSWORD: requireEnv("ADMIN_PASSWORD"), + DATABASE_URL: requireEnv("DATABASE_URL"), + NODE_ENV: optionEnv("NODE_ENV", "development"), + LOG_LEVEL: optionEnv("LOG_LEVEL", "info"), +}; + +function requireEnv(key: string): string { + const val = Bun.env[key]; + if (!val) { + log.error({ name: key }, "Missing required environment variable"); + throw new Error(`Missing required environment variable: ${key}`) + }; + return val; +} + +function optionEnv(key: string, fallback: string = ""): string { + const val = Bun.env[key]; + if (!val) { + log.warn({ name: key }, `Missing optional environmental variable, falling back to "${fallback}"`) + return fallback + }; + return val; +}