diff --git a/.env.example b/.env.example index 924c45a..0b8fcc9 100644 --- a/.env.example +++ b/.env.example @@ -2,3 +2,4 @@ SLACK_BOT_TOKEN=xoxb- SLACK_APP_TOKEN=xapp- GITHUB_TOKEN=github_pat_ DATABASE_URL=postgres://root:password@localhost:5432/app +# DATABASE_POOL_MAX=10 diff --git a/installation.md b/installation.md index 2eb15cc..9c4fc9a 100644 --- a/installation.md +++ b/installation.md @@ -46,6 +46,7 @@ DATABASE_URL=postgres://user:password@localhost/releases_db # Optional # POLL_CRON=0 * * * * # How often to check releases (default: every hour) # DIGEST_CRON=30 7 * * * # When to send the periodic digest in UTC (default: 07:30 UTC) +# DATABASE_POOL_MAX=10 # Max connections in the DB connection pool (default: 10) ``` All tables are created in the `main` PostgreSQL schema. To use a different schema, change the hardcoded value in `src/db/schema.ts` and `drizzle.config.ts`, then regenerate and re-apply migrations. diff --git a/src/db/client.ts b/src/db/client.ts index 0c40efc..bcee9a1 100644 --- a/src/db/client.ts +++ b/src/db/client.ts @@ -1,7 +1,9 @@ import { drizzle } from 'drizzle-orm/bun-sql' +import { positiveInt, requiredString } from '@utils/env' -if (!process.env.DATABASE_URL) { - throw new Error('DATABASE_URL is required') -} - -export const db = drizzle(process.env.DATABASE_URL) +export const db = drizzle({ + connection: { + url: requiredString('DATABASE_URL'), + max: positiveInt('DATABASE_POOL_MAX', 10), + }, +}) diff --git a/src/env.d.ts b/src/env.d.ts index a779b2a..f0639e0 100644 --- a/src/env.d.ts +++ b/src/env.d.ts @@ -9,6 +9,7 @@ declare module 'bun' { // Database DATABASE_URL: string + DATABASE_POOL_MAX?: string // Jobs POLL_CRON?: string DIGEST_CRON?: string diff --git a/src/slack/app.ts b/src/slack/app.ts index d331338..9f711de 100644 --- a/src/slack/app.ts +++ b/src/slack/app.ts @@ -1,7 +1,8 @@ import { App } from '@slack/bolt' +import { requiredString } from '@utils/env.ts' export const app = new App({ - token: process.env.SLACK_BOT_TOKEN, - appToken: process.env.SLACK_APP_TOKEN, + token: requiredString('SLACK_BOT_TOKEN'), + appToken: requiredString('SLACK_APP_TOKEN'), socketMode: true, }) diff --git a/src/utils/env.ts b/src/utils/env.ts new file mode 100644 index 0000000..0285014 --- /dev/null +++ b/src/utils/env.ts @@ -0,0 +1,24 @@ +type EnvVarName = keyof typeof process.env + +export function requiredString(name: EnvVarName): string { + const value = process.env[name] + if (!value) { + throw new Error(`${name} is required`) + } + + return value +} + +export function positiveInt(name: EnvVarName, defaultValue: number): number { + const raw = process.env[name] + if (!raw) { + return defaultValue + } + + const value = Number(raw) + if (!Number.isInteger(value) || value < 1) { + throw new Error(`${name} must be a positive integer`) + } + + return value +} diff --git a/tsconfig.json b/tsconfig.json index 4d253b5..1246e71 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,11 +13,12 @@ "noEmit": true, "skipLibCheck": true, "paths": { - "@db/*": ["./src/db/*"], - "@forges/*": ["./src/forges/*"], - "@bot/*": ["./src/slack/*"], - "@jobs/*": ["./src/jobs/*"], - "@classify/*": ["./src/classify/*"] + "@db/*": ["./src/db/*"], + "@forges/*": ["./src/forges/*"], + "@bot/*": ["./src/slack/*"], + "@jobs/*": ["./src/jobs/*"], + "@classify/*": ["./src/classify/*"], + "@utils/*": ["./src/utils/*"] } } }