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>
97 lines
2.6 KiB
TypeScript
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;
|
|
}
|
|
}
|