Files
cannaiq/backend/src/db/pool.ts
Kelly b7cfec0770 feat: AZ dispensary harmonization with Dutchie source of truth
Major changes:
- Add harmonize-az-dispensaries.ts script to sync dispensaries with Dutchie API
- Add migration 057 for crawl_enabled and dutchie_verified fields
- Remove legacy dutchie-az module (replaced by platforms/dutchie)
- Clean up deprecated crawlers, scrapers, and orchestrator code
- Update location-discovery to not fallback to slug when ID is missing
- Add crawl-rotator service for proxy rotation
- Add types/index.ts for shared type definitions
- Add woodpecker-agent k8s manifest

Harmonization script:
- Queries ConsumerDispensaries API for all 32 AZ cities
- Matches dispensaries by platform_dispensary_id (not slug)
- Updates existing records with full Dutchie data
- Creates new records for unmatched Dutchie dispensaries
- Disables dispensaries not found in Dutchie

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-08 10:19:49 -07:00

97 lines
2.6 KiB
TypeScript

/**
* Runtime Database Pool
*
* This is the canonical database pool for all runtime services.
* Import pool from here, NOT from migrate.ts.
*
* migrate.ts is for CLI migrations only and must NOT be imported at runtime.
*/
import dotenv from 'dotenv';
import { Pool } from 'pg';
// Load .env before any env var access
dotenv.config();
/**
* Get the database connection string from environment variables.
* Supports both CANNAIQ_DB_URL and individual CANNAIQ_DB_* vars.
*/
function getConnectionString(): string {
// Priority 1: Full connection URL
if (process.env.CANNAIQ_DB_URL) {
return process.env.CANNAIQ_DB_URL;
}
// Priority 2: Build from individual env vars
const host = process.env.CANNAIQ_DB_HOST;
const port = process.env.CANNAIQ_DB_PORT;
const name = process.env.CANNAIQ_DB_NAME;
const user = process.env.CANNAIQ_DB_USER;
const pass = process.env.CANNAIQ_DB_PASS;
// Check if all individual vars are present
if (host && port && name && user && pass) {
return `postgresql://${user}:${pass}@${host}:${port}/${name}`;
}
// Fallback: Try DATABASE_URL for legacy compatibility
if (process.env.DATABASE_URL) {
return process.env.DATABASE_URL;
}
// Report what's missing
const required = ['CANNAIQ_DB_HOST', 'CANNAIQ_DB_PORT', 'CANNAIQ_DB_NAME', 'CANNAIQ_DB_USER', 'CANNAIQ_DB_PASS'];
const missing = required.filter((key) => !process.env[key]);
throw new Error(
`[DB Pool] Missing database configuration.\n` +
`Set CANNAIQ_DB_URL, or all of: ${missing.join(', ')}`
);
}
// Lazy-initialized pool singleton
let _pool: Pool | null = null;
/**
* Get the database pool (lazy singleton)
*/
export function getPool(): Pool {
if (!_pool) {
_pool = new Pool({
connectionString: getConnectionString(),
max: 10,
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 5000,
});
_pool.on('error', (err) => {
console.error('[DB Pool] Unexpected error on idle client:', err);
});
}
return _pool;
}
/**
* The database pool for runtime use.
* This is a getter that lazily initializes on first access.
*/
export const pool = {
query: (queryTextOrConfig: string | import('pg').QueryConfig, values?: any[]): Promise<import('pg').QueryResult<any>> => {
return getPool().query(queryTextOrConfig as any, values);
},
connect: () => getPool().connect(),
end: () => getPool().end(),
on: (event: 'error' | 'connect' | 'acquire' | 'remove' | 'release', listener: (...args: any[]) => void) => getPool().on(event as any, listener),
};
/**
* Close the pool connection
*/
export async function closePool(): Promise<void> {
if (_pool) {
await _pool.end();
_pool = null;
}
}