106 lines
2.8 KiB
TypeScript
106 lines
2.8 KiB
TypeScript
import { IDb } from "@/db/IDb.js";
|
|
import { ILogger } from "@/utils/logger.js";
|
|
import { EnvConfig } from "@/service/system/settingsService.js";
|
|
import { Client, Pool } from "pg";
|
|
import { runTimescaleDBMigrations } from "@/db/migration/timescaledb/index.js";
|
|
|
|
const SERVICE_NAME = "TimescaleDB";
|
|
|
|
class TimescaleDB implements IDb {
|
|
private pool: Pool | null = null;
|
|
|
|
constructor(
|
|
private logger: ILogger,
|
|
private envSettings: EnvConfig
|
|
) {}
|
|
|
|
private ensureDatabaseExists = async (connectionString: string) => {
|
|
const url = new URL(connectionString);
|
|
const dbName = url.pathname.slice(1); // Remove leading "/"
|
|
|
|
// Connect to the default "postgres" database
|
|
url.pathname = "/postgres";
|
|
const client = new Client({ connectionString: url.toString() });
|
|
await client.connect();
|
|
|
|
const result = await client.query("SELECT 1 FROM pg_database WHERE datname = $1", [dbName]);
|
|
if (result.rowCount === 0) {
|
|
await client.query(`CREATE DATABASE "${dbName}"`);
|
|
this.logger.info({
|
|
message: `Created database "${dbName}"`,
|
|
service: SERVICE_NAME,
|
|
method: "ensureDatabaseExists",
|
|
});
|
|
}
|
|
await client.end();
|
|
};
|
|
|
|
connect = async () => {
|
|
try {
|
|
const connectionString = this.envSettings.dbConnectionString || "postgresql://postgres:password@localhost:5432/checkmate";
|
|
|
|
await this.ensureDatabaseExists(connectionString);
|
|
|
|
this.pool = new Pool({ connectionString });
|
|
|
|
const client = await this.pool.connect();
|
|
client.release();
|
|
|
|
// Ensure TimescaleDB extension is enabled
|
|
await this.pool.query("CREATE EXTENSION IF NOT EXISTS timescaledb;");
|
|
|
|
this.logger.info({
|
|
message: "Connected to TimescaleDB",
|
|
service: SERVICE_NAME,
|
|
method: "connect",
|
|
});
|
|
|
|
await runTimescaleDBMigrations(this.pool, this.logger);
|
|
} catch (error: unknown) {
|
|
this.logger.error({
|
|
message: error instanceof Error ? error.message : "Unknown error",
|
|
service: SERVICE_NAME,
|
|
method: "connect",
|
|
stack: error instanceof Error ? error.stack : undefined,
|
|
});
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
disconnect = async () => {
|
|
try {
|
|
if (this.pool === null) {
|
|
return;
|
|
}
|
|
this.logger.info({
|
|
message: "Disconnecting from TimescaleDB",
|
|
service: SERVICE_NAME,
|
|
method: "disconnect",
|
|
});
|
|
await this.pool.end();
|
|
this.pool = null;
|
|
this.logger.info({
|
|
message: "Disconnected from TimescaleDB",
|
|
service: SERVICE_NAME,
|
|
method: "disconnect",
|
|
});
|
|
} catch (error: unknown) {
|
|
this.logger.error({
|
|
message: error instanceof Error ? error.message : "Unknown error",
|
|
service: SERVICE_NAME,
|
|
method: "disconnect",
|
|
stack: error instanceof Error ? error.stack : undefined,
|
|
});
|
|
}
|
|
};
|
|
|
|
getPool = (): Pool => {
|
|
if (this.pool === null) {
|
|
throw new Error("TimescaleDB is not connected. Call connect() first.");
|
|
}
|
|
return this.pool;
|
|
};
|
|
}
|
|
|
|
export default TimescaleDB;
|