db: Drizzle schemas on domain Postgres schemas, baseline migration, reorg SQL

- pgSchema tables for auth, money, house, ref, games, cs, animaldex, site, meta
  (lists excluded via schemaFilter; runtime list tables are managed by meta.*)
- drizzle/ baseline matches the reorged DB exactly (catalog diff = 0 on rehearsal)
- db/reorg: one-time SQL that moves the flat public schema (rehearsed on a prod copy)
- scripts/db-migrate.ts: migrator with --init to record the baseline and a guard
  against running the baseline on a non-empty, unrecorded database
This commit is contained in:
2026-10-23 22:03:42 +00:00
parent f94e3dc8ac
commit 64fb8df262
29 changed files with 12507 additions and 189 deletions

View File

@@ -1,9 +1,18 @@
import { index, integer, pgTable, text, timestamp, uniqueIndex, bigint } from 'drizzle-orm/pg-core';
import {
index,
integer,
primaryKey,
text,
timestamp,
uniqueIndex,
bigint
} from 'drizzle-orm/pg-core';
import { csSchema } from './schemas';
import { id } from './helpers';
// A Steam market hash name identifies a listing, including wear and special variants.
export const csMarketItems = pgTable(
'cs_market_items',
export const csMarketItems = csSchema.table(
'items',
{
id: id(),
marketHashName: text('market_hash_name').notNull(),
@@ -14,14 +23,14 @@ export const csMarketItems = pgTable(
lastSeenAt: timestamp('last_seen_at', { withTimezone: true }).notNull().defaultNow()
},
(table) => [
uniqueIndex('cs_market_items_hash_idx').on(table.marketHashName),
index('cs_market_items_name_idx').on(table.name)
uniqueIndex('items_hash_idx').on(table.marketHashName),
index('items_name_idx').on(table.name)
]
);
// Keep source observations intact. Aggregation happens only when the chart is requested.
export const csPriceObservations = pgTable(
'cs_price_observations',
export const csPriceObservations = csSchema.table(
'price_observations',
{
id: bigint('id', { mode: 'number' }).primaryKey().generatedAlwaysAsIdentity(),
itemId: integer('item_id')
@@ -36,25 +45,25 @@ export const csPriceObservations = pgTable(
importedAt: timestamp('imported_at', { withTimezone: true }).notNull().defaultNow()
},
(table) => [
uniqueIndex('cs_price_observations_unique_idx').on(
uniqueIndex('price_observations_unique_idx').on(
table.itemId,
table.source,
table.currency,
table.observedAt,
table.kind
),
index('cs_price_observations_lookup_idx').on(
index('price_observations_lookup_idx').on(
table.itemId,
table.source,
table.currency,
table.observedAt
),
index('cs_price_observations_time_idx').on(table.observedAt)
index('price_observations_time_idx').on(table.observedAt)
]
);
export const csItemDimensions = pgTable(
'cs_item_dimensions',
export const csItemDimensions = csSchema.table(
'item_dimensions',
{
itemId: integer('item_id')
.notNull()
@@ -63,19 +72,19 @@ export const csItemDimensions = pgTable(
value: text('value').notNull()
},
(table) => [
uniqueIndex('cs_item_dimensions_unique_idx').on(table.itemId, table.key),
index('cs_item_dimensions_filter_idx').on(table.key, table.value, table.itemId)
primaryKey({ name: 'item_dimensions_pkey', columns: [table.itemId, table.key] }),
index('item_dimensions_filter_idx').on(table.key, table.value, table.itemId)
]
);
export const csPriceStreams = pgTable(
'cs_price_streams',
export const csPriceStreams = csSchema.table(
'price_streams',
{
source: text('source').notNull(),
currency: text('currency').notNull(),
kind: text('kind').notNull()
},
(table) => [
uniqueIndex('cs_price_streams_unique_idx').on(table.source, table.currency, table.kind)
primaryKey({ name: 'price_streams_pkey', columns: [table.source, table.currency, table.kind] })
]
);