feat(db): add rent_payment_destinations column and multi-schema support

- Add migration 0020 for rent_payment_destinations jsonb column
- Add DB_SCHEMA env var support for multi-schema deployments
- Create custom migrate.ts script with proper search_path handling
- Update drizzle.config.ts and client.ts to use DB_SCHEMA
- Add db_schema variable to Terraform with dev=test/prod=public defaults
- Update CD workflow to set DB_SCHEMA based on branch
This commit is contained in:
2026-03-15 20:25:31 +04:00
parent f4fe4470f7
commit 0747973c8f
12 changed files with 3522 additions and 14 deletions

View File

@@ -5,6 +5,7 @@ PORT=3000
# Database
DATABASE_URL=postgres://postgres:postgres@127.0.0.1:54322/postgres
DB_SCHEMA=public
# Telegram
TELEGRAM_BOT_TOKEN=your-telegram-bot-token

View File

@@ -100,6 +100,7 @@ jobs:
- name: Run database migrations
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
DB_SCHEMA: ${{ github.ref == 'refs/heads/main' && 'public' || 'test' }}
run: bun run db:migrate
- name: Setup gcloud

View File

@@ -97,6 +97,7 @@ module "bot_api_service" {
env = merge(
{
NODE_ENV = var.environment
DB_SCHEMA = var.db_schema
},
var.bot_purchase_parser_model == null ? {} : {
PURCHASE_PARSER_MODEL = var.bot_purchase_parser_model

View File

@@ -259,3 +259,9 @@ variable "github_deploy_service_account_id" {
type = string
default = "github-deployer"
}
variable "db_schema" {
description = "Database schema name for the application"
type = string
default = "public"
}

View File

@@ -18,12 +18,12 @@
"format:check": "bunx oxfmt --check .",
"db:generate": "bunx drizzle-kit generate --config packages/db/drizzle.config.ts",
"db:check": "bunx drizzle-kit check --config packages/db/drizzle.config.ts",
"db:migrate": "bunx drizzle-kit migrate --config packages/db/drizzle.config.ts",
"db:migrate": "bun run packages/db/src/migrate.ts",
"db:migrations:check": "bun run scripts/check-migration-hygiene.ts",
"db:migrations:manifest": "bun run scripts/update-migration-checksums.ts",
"db:push": "bunx drizzle-kit push --config packages/db/drizzle.config.ts",
"db:studio": "bunx drizzle-kit studio --config packages/db/drizzle.config.ts",
"db:seed": "set -a; [ -f .env ] && . ./.env; set +a; bun run --filter @household/db seed",
"db:seed": "set -a; [ -f .env ] && . ./.env; set +a; DB_SCHEMA=${DB_SCHEMA:-public} bun run --filter @household/db seed",
"review:coderabbit": "coderabbit --prompt-only --base main || ~/.local/bin/coderabbit --prompt-only --base main",
"infra:fmt": "terraform -chdir=infra/terraform fmt -recursive",
"infra:fmt:check": "terraform -chdir=infra/terraform fmt -check -recursive",

View File

@@ -1,14 +1,14 @@
import { defineConfig } from 'drizzle-kit'
const dbCredentials = process.env.DATABASE_URL
? {
url: process.env.DATABASE_URL
}
: undefined
export default defineConfig({
dialect: 'postgresql',
schema: './packages/db/src/schema.ts',
out: './packages/db/drizzle',
dbCredentials
dbCredentials: {
url: process.env.DATABASE_URL!
},
migrations: {
schema: process.env.DB_SCHEMA || 'public',
table: '__drizzle_migrations'
}
})

View File

@@ -0,0 +1 @@
ALTER TABLE "household_billing_settings" ADD COLUMN "rent_payment_destinations" jsonb;

File diff suppressed because it is too large Load Diff

View File

@@ -141,6 +141,13 @@
"when": 1773327708167,
"tag": "0019_faithful_madame_masque",
"breakpoints": true
},
{
"idx": 20,
"version": "7",
"when": 1773590603863,
"tag": "0020_natural_mauler",
"breakpoints": true
}
]
}

View File

@@ -7,9 +7,19 @@ export interface DbClientOptions {
}
export function createDbClient(databaseUrl: string, options: DbClientOptions = {}) {
const dbSchema = process.env.DB_SCHEMA || 'public'
const queryClient = postgres(databaseUrl, {
max: options.max ?? 5,
prepare: options.prepare ?? false
prepare: options.prepare ?? false,
onnotice: () => {},
connection: {
search_path: dbSchema
},
transform: {
...postgres.camel,
undefined: null
}
})
const db = drizzle(queryClient)

View File

@@ -0,0 +1,35 @@
import postgres from 'postgres'
import { drizzle } from 'drizzle-orm/postgres-js'
import { migrate } from 'drizzle-orm/postgres-js/migrator'
import path from 'path'
const databaseUrl = process.env.DATABASE_URL
if (!databaseUrl) {
throw new Error('DATABASE_URL is not set')
}
const dbSchema = process.env.DB_SCHEMA || 'public'
console.log(`Running migrations for schema: ${dbSchema}...`)
const migrationClient = postgres(databaseUrl, {
max: 1,
onnotice: () => {}
})
// Explicitly set search_path to the target schema
// This ensures that 'CREATE TABLE "x"' goes into the right schema
await migrationClient.unsafe(`SET search_path TO ${dbSchema}`)
const db = drizzle(migrationClient)
// This runs migrations from the 'drizzle' folder
await migrate(db, {
migrationsFolder: path.resolve(__dirname, '../drizzle'),
migrationsSchema: dbSchema,
migrationsTable: '__drizzle_migrations'
})
console.log('Migrations applied successfully!')
await migrationClient.end()
process.exit(0)

View File

@@ -3,9 +3,8 @@ import { $ } from 'bun'
const PROJECT_ID = 'gen-lang-client-0200379851'
async function secretExists(name: string): Promise<boolean> {
const result =
(await $`gcloud secrets describe ${name} --project=${PROJECT_ID}`.quiet().exitCode) === 0
return result
const result = await $`gcloud secrets describe ${name} --project=${PROJECT_ID}`.quiet().nothrow()
return result.exitCode === 0
}
async function createSecret(name: string, value: string) {