UNPKG

adba

Version:
181 lines (180 loc) 7.94 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const os_1 = __importDefault(require("os")); const knex_1 = __importDefault(require("knex")); const tunnel_ssh_1 = require("tunnel-ssh"); function wrapKnexInstance(knexInstance, conn, server) { const originalDestroy = knexInstance.destroy.bind(knexInstance); return new Proxy(knexInstance, { get(target, prop) { if (prop === 'destroy') { return () => __awaiter(this, void 0, void 0, function* () { const result = yield originalDestroy(); conn.destroy(); yield new Promise((resolve) => server.close(() => { console.log('tunnel & mysql conn CLOSE'); resolve(null); })); return result; }); } return Reflect.get(target, prop); } }); } /** * Helper to create and validate Knex instance * @param config Knex configuration * @returns Object with `.current` knex instance or false if connection fails */ function createKnex(config) { return __awaiter(this, void 0, void 0, function* () { var _a, _b, _c, _d, _e; const db = (0, knex_1.default)(config); db.on('query', (queryData) => { var _a; if (((_a = process.env.ENV) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== 'prod') { console.log('==========SQL Query======'); console.log(queryData.sql); console.log('==========\n'); } }); try { const response = yield db.raw('SELECT 2 + 2 AS result'); const client = (db.client.config.client || '').toLowerCase(); let result; switch (client) { case 'mysql': case 'mysql2': // MySQL returns [ [ { result: 4 } ], fields ] if (Array.isArray(response) && Array.isArray(response[0])) { result = (_a = response[0][0]) === null || _a === void 0 ? void 0 : _a.result; } break; case 'pg': case 'postgres': case 'postgresql': // PostgreSQL returns { rows: [ { result: 4 } ] } result = (_c = (_b = response === null || response === void 0 ? void 0 : response.rows) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.result; break; case 'sqlite3': // SQLite returns { rows: [ { result: 4 } ] } result = (_e = (_d = response === null || response === void 0 ? void 0 : response.rows) === null || _d === void 0 ? void 0 : _d[0]) === null || _e === void 0 ? void 0 : _e.result; break; default: throw new Error(`Unsupported DB client: ${client}`); } console.log('Database connection verification: 2+2=', result); if (result !== 4) throw new Error('2+2!=' + result); return { current: db }; } catch (error) { console.error('Database connection failed: ', error.message); return false; } }); } /** * Create SSH tunnel and attach Knex instance * @param tunnelConfig Configuration for the SSH tunnel * @param knexConfig Knex configuration object * @returns Object with `.current` knex instance or false if fails */ function createTunnel(tunnelConfig, knexConfig) { return __awaiter(this, void 0, void 0, function* () { const { tunnelOptions, serverOptions, sshOptions, forwardOptions } = tunnelConfig; const [server, conn] = yield (0, tunnel_ssh_1.createTunnel)(tunnelOptions, serverOptions, sshOptions, forwardOptions); const port = server.address().port; knexConfig.connection = Object.assign(Object.assign({}, knexConfig.connection), { port }); console.log('TUNNEL on port:', port); const db = yield createKnex(knexConfig); if (!db) { conn.destroy(); yield new Promise((resolve) => server.close(() => { console.log('tunnel & mysql conn CLOSE'); resolve(null); })); return false; } db.current = wrapKnexInstance(db.current, conn, server); return db; }); } /** * Generate knex instance with optional SSH tunneling * @param host DB Host * @param user DB User * @param password DB Password * @param database DB Name * @param conf Additional config including tunnel, knexConfig and connection * @returns Object with `.current` knex instance or false */ function knexInstances() { return __awaiter(this, arguments, void 0, function* (_a = {}) { var { host, user, password, database } = _a, conf = __rest(_a, ["host", "user", "password", "database"]); const { tunnel = false, knexConfig = {}, knexConnection = {} } = conf; const kconf = Object.assign({ client: knexConfig.client || 'mysql2' }, knexConfig); const kconn = Object.assign({ host, user, password, database }, knexConnection); kconf.connection = kconn; if (tunnel) { const sshConfig = tunnel; const privateKeyPath = path_1.default.resolve(sshConfig.privateKey.replace('~', os_1.default.homedir())); const sshOptions = { privateKey: fs_1.default.readFileSync(privateKeyPath), username: sshConfig.username, host: sshConfig.host, port: sshConfig.port, keepaliveCountMax: 720, keepaliveInterval: 120000, }; if (sshConfig.passphrase) sshOptions.passphrase = sshConfig.passphrase; const tunnelConfig = { tunnelOptions: { autoClose: false, }, serverOptions: {}, sshOptions, forwardOptions: { srcAddr: sshConfig.localhost || host, dstAddr: host, dstPort: kconn.port || 3306, } }; return yield createTunnel(tunnelConfig, kconf); } else { return yield createKnex(kconf); } }); } exports.default = knexInstances;