@worker-tools/deno-kv-storage
Version:
An implementation of the StorageArea (1,2,3) interface for Deno with an extensible system for supporting various database backends.
220 lines • 8.73 kB
JavaScript
import { parseConnectionUri } from "../utils/utils.js";
import { ConnectionParamsError } from "../client/error.js";
import { fromFileUrl, isAbsolute } from "../deps.js";
/**
* This function retrieves the connection options from the environmental variables
* as they are, without any extra parsing
*
* It will throw if no env permission was provided on startup
*/
function getPgEnv() {
return {
database: Deno.env.get("PGDATABASE"),
hostname: Deno.env.get("PGHOST"),
port: Deno.env.get("PGPORT"),
user: Deno.env.get("PGUSER"),
password: Deno.env.get("PGPASSWORD"),
applicationName: Deno.env.get("PGAPPNAME"),
};
}
function formatMissingParams(missingParams) {
return `Missing connection parameters: ${missingParams.join(", ")}`;
}
/**
* This validates the options passed are defined and have a value other than null
* or empty string, it throws a connection error otherwise
*
* @param has_env_access This parameter will change the error message if set to true,
* telling the user to pass env permissions in order to read environmental variables
*/
function assertRequiredOptions(options, requiredKeys, has_env_access) {
const missingParams = [];
for (const key of requiredKeys) {
if (options[key] === "" ||
options[key] === null ||
options[key] === undefined) {
missingParams.push(key);
}
}
if (missingParams.length) {
let missing_params_message = formatMissingParams(missingParams);
if (!has_env_access) {
missing_params_message +=
"\nConnection parameters can be read from environment variables only if Deno is run with env permission";
}
throw new ConnectionParamsError(missing_params_message);
}
}
function parseOptionsFromUri(connString) {
let postgres_uri;
try {
const uri = parseConnectionUri(connString);
postgres_uri = {
application_name: uri.params.application_name,
dbname: uri.path || uri.params.dbname,
driver: uri.driver,
host: uri.host || uri.params.host,
password: uri.password || uri.params.password,
port: uri.port || uri.params.port,
// Compatibility with JDBC, not standard
// Treat as sslmode=require
sslmode: uri.params.ssl === "true"
? "require"
: uri.params.sslmode,
user: uri.user || uri.params.user,
};
}
catch (e) {
// TODO
// Use error cause
throw new ConnectionParamsError(`Could not parse the connection string due to ${e}`);
}
if (!["postgres", "postgresql"].includes(postgres_uri.driver)) {
throw new ConnectionParamsError(`Supplied DSN has invalid driver: ${postgres_uri.driver}.`);
}
// No host by default means socket connection
const host_type = postgres_uri.host
? (isAbsolute(postgres_uri.host) ? "socket" : "tcp")
: "socket";
let tls;
switch (postgres_uri.sslmode) {
case undefined: {
break;
}
case "disable": {
tls = { enabled: false, enforce: false, caCertificates: [] };
break;
}
case "prefer": {
tls = { enabled: true, enforce: false, caCertificates: [] };
break;
}
case "require": {
tls = { enabled: true, enforce: true, caCertificates: [] };
break;
}
default: {
throw new ConnectionParamsError(`Supplied DSN has invalid sslmode '${postgres_uri.sslmode}'. Only 'disable', 'require', and 'prefer' are supported`);
}
}
return {
applicationName: postgres_uri.application_name,
database: postgres_uri.dbname,
hostname: postgres_uri.host,
host_type,
password: postgres_uri.password,
port: postgres_uri.port,
tls,
user: postgres_uri.user,
};
}
const DEFAULT_OPTIONS = {
applicationName: "deno_postgres",
connection: {
attempts: 1,
},
host: "127.0.0.1",
socket: "/tmp",
host_type: "socket",
port: 5432,
tls: {
enabled: true,
enforce: false,
caCertificates: [],
},
};
export function createParams(params = {}) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
if (typeof params === "string") {
params = parseOptionsFromUri(params);
}
let pgEnv = {};
let has_env_access = true;
try {
pgEnv = getPgEnv();
}
catch (e) {
if (e instanceof Deno.errors.PermissionDenied) {
has_env_access = false;
}
else {
throw e;
}
}
const provided_host = (_a = params.hostname) !== null && _a !== void 0 ? _a : pgEnv.hostname;
// If a host is provided, the default connection type is TCP
const host_type = (_b = params.host_type) !== null && _b !== void 0 ? _b : (provided_host ? "tcp" : DEFAULT_OPTIONS.host_type);
if (!["tcp", "socket"].includes(host_type)) {
throw new ConnectionParamsError(`"${host_type}" is not a valid host type`);
}
let host;
if (host_type === "socket") {
const socket = provided_host !== null && provided_host !== void 0 ? provided_host : DEFAULT_OPTIONS.socket;
try {
if (!isAbsolute(socket)) {
const parsed_host = new URL(socket, Deno.mainModule);
// Resolve relative path
if (parsed_host.protocol === "file:") {
host = fromFileUrl(parsed_host);
}
else {
throw new ConnectionParamsError("The provided host is not a file path");
}
}
else {
host = socket;
}
}
catch (e) {
// TODO
// Add error cause
throw new ConnectionParamsError(`Could not parse host "${socket}" due to "${e}"`);
}
}
else {
host = provided_host !== null && provided_host !== void 0 ? provided_host : DEFAULT_OPTIONS.host;
}
let port;
if (params.port) {
port = Number(params.port);
}
else if (pgEnv.port) {
port = Number(pgEnv.port);
}
else {
port = DEFAULT_OPTIONS.port;
}
if (Number.isNaN(port) || port === 0) {
throw new ConnectionParamsError(`"${(_c = params.port) !== null && _c !== void 0 ? _c : pgEnv.port}" is not a valid port number`);
}
if (host_type === "socket" && (params === null || params === void 0 ? void 0 : params.tls)) {
throw new ConnectionParamsError(`No TLS options are allowed when host type is set to "socket"`);
}
const tls_enabled = !!((_e = (_d = params === null || params === void 0 ? void 0 : params.tls) === null || _d === void 0 ? void 0 : _d.enabled) !== null && _e !== void 0 ? _e : DEFAULT_OPTIONS.tls.enabled);
const tls_enforced = !!((_g = (_f = params === null || params === void 0 ? void 0 : params.tls) === null || _f === void 0 ? void 0 : _f.enforce) !== null && _g !== void 0 ? _g : DEFAULT_OPTIONS.tls.enforce);
if (!tls_enabled && tls_enforced) {
throw new ConnectionParamsError("Can't enforce TLS when client has TLS encryption is disabled");
}
// TODO
// Perhaps username should be taken from the PC user as a default?
const connection_options = {
applicationName: (_j = (_h = params.applicationName) !== null && _h !== void 0 ? _h : pgEnv.applicationName) !== null && _j !== void 0 ? _j : DEFAULT_OPTIONS.applicationName,
connection: {
attempts: (_l = (_k = params === null || params === void 0 ? void 0 : params.connection) === null || _k === void 0 ? void 0 : _k.attempts) !== null && _l !== void 0 ? _l : DEFAULT_OPTIONS.connection.attempts,
},
database: (_m = params.database) !== null && _m !== void 0 ? _m : pgEnv.database,
hostname: host,
host_type,
password: (_o = params.password) !== null && _o !== void 0 ? _o : pgEnv.password,
port,
tls: {
enabled: tls_enabled,
enforce: tls_enforced,
caCertificates: (_q = (_p = params === null || params === void 0 ? void 0 : params.tls) === null || _p === void 0 ? void 0 : _p.caCertificates) !== null && _q !== void 0 ? _q : [],
},
user: (_r = params.user) !== null && _r !== void 0 ? _r : pgEnv.user,
};
assertRequiredOptions(connection_options, ["applicationName", "database", "hostname", "host_type", "port", "user"], has_env_access);
return connection_options;
}
//# sourceMappingURL=connection_params.js.map