adba
Version:
Any DataBase to API
181 lines (180 loc) • 7.94 kB
JavaScript
;
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;