index.ts•4.78 kB
import { SchemaPermissions } from "../types/index.js";
type LogType = "info" | "error";
// @INFO: Enable logging if ENABLE_LOGGING is true
const ENABLE_LOGGING =
process.env.ENABLE_LOGGING === "true" || process.env.ENABLE_LOGGING === "1";
export function log(type: LogType = "info", ...args: any[]): void {
if (!ENABLE_LOGGING) return;
switch (type) {
case "info":
console.info(...args);
break;
case "error":
console.error(...args);
break;
default:
console.log(...args);
}
}
// Function to parse schema-specific permissions from environment variables
export function parseSchemaPermissions(
permissionsString?: string,
): SchemaPermissions {
const permissions: SchemaPermissions = {};
if (!permissionsString) {
return permissions;
}
// Format: "schema1:true,schema2:false"
const permissionPairs = permissionsString.split(",");
for (const pair of permissionPairs) {
const [schema, value] = pair.split(":");
if (schema && value) {
permissions[schema.trim()] = value.trim() === "true";
}
}
return permissions;
}
// MySQL connection configuration type
export interface MySQLConnectionConfig {
host?: string;
port?: number;
user?: string;
password?: string;
database?: string;
socketPath?: string;
}
// Function to parse MySQL connection string (mysql CLI format)
// Example: mysql --default-auth=mysql_native_password -A -hrdsproxy.staging.luno.com -P3306 -uUSER -pPASS database_name
export function parseMySQLConnectionString(
connectionString: string,
): MySQLConnectionConfig {
const config: MySQLConnectionConfig = {};
// Remove 'mysql' command at the start if present
let cleanedString = connectionString.trim().replace(/^mysql\s+/, '');
// Parse flags and options
const tokens = [];
let currentToken = '';
let inQuotes = false;
let quoteChar: string | null = null;
for (let i = 0; i < cleanedString.length; i++) {
const char = cleanedString[i];
if ((char === '"' || char === "'") && (!inQuotes || char === quoteChar)) {
// Toggle quote state without adding the quote character
inQuotes = !inQuotes;
quoteChar = inQuotes ? char : null;
} else if (char === ' ' && !inQuotes) {
if (currentToken) {
tokens.push(currentToken);
currentToken = '';
}
} else {
currentToken += char;
}
}
if (currentToken) {
tokens.push(currentToken);
}
// Process tokens
for (let i = 0; i < tokens.length; i++) {
const token = tokens[i];
// Check for combined short options (e.g., -uUSER, -pPASS, -hHOST, -PPORT)
if (token.startsWith('-') && !token.startsWith('--')) {
const flag = token[1];
let value = token.substring(2);
// If no value attached, check next token
if (!value && i + 1 < tokens.length && !tokens[i + 1].startsWith('-')) {
value = tokens[i + 1];
i++;
}
switch (flag) {
case 'h':
config.host = value;
break;
case 'P': {
const port = parseInt(value, 10);
if (Number.isNaN(port) || !Number.isFinite(port) || port < 1 || port > 65535) {
throw new Error(`Invalid port: ${value}`);
}
config.port = port;
break;
}
case 'u':
config.user = value;
break;
case 'p':
config.password = value;
break;
case 'S':
config.socketPath = value;
break;
}
}
// Check for long options (e.g., --host=HOST, --port=PORT)
else if (token.startsWith('--')) {
const [flag, ...valueParts] = token.substring(2).split('=');
let value = valueParts.join('=');
// If no value with =, check next token
if (!value && i + 1 < tokens.length && !tokens[i + 1].startsWith('-')) {
value = tokens[i + 1];
i++;
}
switch (flag) {
case 'host':
config.host = value;
break;
case 'port': {
const port = parseInt(value, 10);
if (Number.isNaN(port) || !Number.isFinite(port) || port < 1 || port > 65535) {
throw new Error(`Invalid port: ${value}`);
}
config.port = port;
break;
}
case 'user':
config.user = value;
break;
case 'password':
config.password = value;
break;
case 'socket':
config.socketPath = value;
break;
}
}
// Last positional argument (not starting with -) is the database name
else if (!token.startsWith('-')) {
// Only consider it a database if it's one of the last arguments and not part of a flag
config.database = token;
}
}
return config;
}