Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
…into syl/pool
  • Loading branch information
wiltsecarpenter committed Oct 30, 2023
2 parents 9dc999a + 33aeca2 commit fe05138
Show file tree
Hide file tree
Showing 9 changed files with 490 additions and 154 deletions.
19 changes: 0 additions & 19 deletions .eslintrc.cjs

This file was deleted.

26 changes: 26 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"parserOptions": {
"sourceType": "module",
"ecmaVersion": 2022
},
"env": {
"node": true,
"es6": true
},
"extends": ["eslint:recommended", "plugin:prettier/recommended"],
"rules": {
"no-ex-assign": "off",
"no-var":"error",
"object-shorthand": ["warn", "always"],
"prettier/prettier": ["warn", {"bracketSpacing": false, "trailingComma": "es5"}]
},
"overrides": [
{
"files": ["*.test.js"],
"env": {
"mocha": true
}
}
],
"root": true
}
13 changes: 11 additions & 2 deletions bin/observable-database-proxy.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,20 @@ argv
name,
start
)
.command(`add <name>`, `Add a new database proxy configuration`, name, add)
.command(
`add <name> [--standalone]`,
`Add a new database proxy configuration`,
name,
add
)
.command(
`remove <name>`,
`Remove an existing database proxy configuration`,
name,
remove
)
.command(
`reset <name>`,
`reset <name> [--standalone]`,
`Reset the shared secret for an existing database proxy configuration`,
name,
reset
Expand All @@ -40,6 +45,10 @@ argv
`sslkey`,
`Set the SSL private key location for an HTTPS database proxy`
)
.describe(
`standalone`,
`Standalone mode: create a secret for standalone clients`
)
.example(`$0 start localdb`, `Run an HTTP database proxy named "localdb"`)
.example(
`$0 start localssl --sslkey ../ssl/localhost.key --sslcert ../ssl/localhost.crt`,
Expand Down
61 changes: 56 additions & 5 deletions lib/commands.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
/* eslint-disable no-console */

import {randomBytes} from "crypto";
import {createInterface} from "readline";
import open from "open";
import {
readConfig,
readDecodedConfig,
writeConfig,
decodeSecret,
encodeSecret,
} from "./config.js";
import {server} from "./server.js";
import {server, types} from "./server.js";
import {exit} from "./errors.js";

export function start(argv) {
Expand All @@ -17,7 +19,7 @@ export function start(argv) {
}

export async function add(argv, reset = false) {
const {name, sslkey, sslcert} = argv;
const {name, sslkey, sslcert, standalone} = argv;
let config = readConfig();
let url;
let token;
Expand All @@ -39,7 +41,12 @@ export async function add(argv, reset = false) {
if (reset) {
if (!config[name]) exit(`No configuration found for "${name}"`);
url = config[name].url;
} else {
token = config[name].token;
server_host = config[name].server_host;
path = config[name].path;
username = config[name].username;
password = config[name].password;
} else if (!standalone) {
const wantsSSL = sslkey || sslcert;
// open browser
const observable =
Expand All @@ -53,8 +60,50 @@ export async function add(argv, reset = false) {
);
}

// paste secret (secret, origin, name, type, host, port, ssl)
const secret = await question("Secret: ");
let secret;
if (standalone) {
let {type, host, origin, port, ssl} = reset
? decodeSecret(config[name].secret)
: {};
if (!type) {
for (;;) {
type = await question("Database type: ");
if (types.includes(type)) break;
console.log(`Invalid type, should be one of: ${types.join(", ")}`);
}
} else if (!types.includes(type)) {
exit(`Invalid type, should be one of: ${type.join(", ")}`);
}
if (!host || !port || !ssl) {
const proxyUrl = new URL(
(await question(
"Observable database proxy Url [https://127.0.0.1:2899]:"
)) || "https://127.0.0.1:2899"
);
host = proxyUrl.hostname;
port = proxyUrl.port;
ssl = proxyUrl.protocol !== "https" ? "disabled" : "required";
}
if (!origin) {
origin =
(await question("Standalone server origin [https://127.0.0.1:3000]:")) ||
"https://127.0.0.1:3000";
}

const secretPayload = {
name,
type,
host,
port,
ssl,
origin,
secret: randomBytes(32).toString("hex"),
};
secret = encodeSecret(secretPayload);
} else {
// paste secret (secret, origin, name, type, host, port, ssl)
secret = await question("Secret: ");
}
const decoded = decodeSecret(secret);
if (decoded.name !== name)
return exit(`Name mismatch: "${decoded.name}" (server), "${name} (proxy)"`);
Expand Down Expand Up @@ -104,6 +153,8 @@ export async function add(argv, reset = false) {
writeConfig(config);

console.log(`Configuration ${reset ? `reset` : `added`} for "${name}"`);
if (standalone)
console.log(`Secret for DabaseClient("${name}"):\ndb:${name}:${secret}`);
}

export function reset(argv) {
Expand Down
5 changes: 5 additions & 0 deletions lib/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,8 @@ export function decodeSecret(secret) {
exit(error);
}
}

export function encodeSecret(arg) {
const buffer = Buffer.from(JSON.stringify(arg)).toString("base64");
return buffer;
}
12 changes: 11 additions & 1 deletion lib/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,19 @@ import oracle from "./oracle.js";
import databricks from "./databricks.js";
import logger from "../middleware/logger.js";

export const types = [
"databricks",
"mysql",
"mssql",
"postgres",
"snowflake",
"oracle",
"mongosql",
];

export async function server(config, argv) {
const development = process.env.NODE_ENV === "development";
const developmentOrigin = "https://worker.test:5000";
const developmentOrigin = "https://login.worker.test:5000";

const {
name,
Expand Down
2 changes: 1 addition & 1 deletion lib/validate.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export const validateQueryPayload = ajv.compile({
required: ["sql"],
properties: {
sql: {type: "string", minLength: 1, maxLength: 32 * 1000},
params: {type: ["object", "array"]},
params: {anyOf: [{type: ["object"]}, {type: ["array"]}]}
},
});
export const validateDescribeColumnsPayload = ajv.compile({
Expand Down
13 changes: 7 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@observablehq/database-proxy",
"description": "A local proxy to connect private Observable notebooks to private databases",
"version": "3.3.0",
"version": "3.4.0",
"type": "module",
"engines": {
"node": ">=14.19.0"
Expand All @@ -24,7 +24,7 @@
"ajv": "^8.11.0",
"lru-cache": "^6.0.0",
"micro": "^9.3.4",
"mssql": "^9.0.1",
"mssql": "^9.1.1",
"mysql2": "^3.0.1",
"open": "^6.3.0",
"pg": "^8.7.3",
Expand All @@ -38,13 +38,14 @@
"@babel/preset-env": "^7.19.4",
"@babel/register": "^7.18.9",
"chai": "^4.3.6",
"eslint": "^8.32.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint": "^8.50.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.0",
"mocha": "^10.1.0",
"mock-req": "^0.2.0",
"mock-res": "^0.6.0",
"nodemon": "^1.19.1",
"prettier": "^2.8.3",
"prettier": "^3.0.3",
"wait-on": "^6.0.1"
},
"peerDependencies": {
Expand All @@ -58,7 +59,7 @@
"test:ci": "docker-compose -f docker-compose.yml up --build --exit-code-from test",
"test:db": "docker-compose -f docker-compose.yml -f docker-compose.local.yml up mssql mysql"
},
"author": "Observable",
"author": "Observable, Inc.",
"license": "ISC",
"repository": {
"type": "git",
Expand Down
Loading

0 comments on commit fe05138

Please sign in to comment.