@squadbase/vite-server 0.1.9-dev.f236b23 → 0.1.10-dev.cec85c2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +843 -364
- package/dist/connectors/aws-billing.js +18 -4
- package/dist/connectors/azure-sql.js +20374 -16
- package/dist/connectors/cosmosdb.d.ts +5 -0
- package/dist/connectors/cosmosdb.js +743 -0
- package/dist/connectors/google-ads.js +8 -48
- package/dist/connectors/jdbc.js +244 -165
- package/dist/connectors/oracle.js +20408 -26
- package/dist/connectors/semrush.js +109 -21
- package/dist/connectors/sqlserver.js +20370 -15
- package/dist/index.js +843 -364
- package/dist/main.js +843 -364
- package/dist/vite-plugin.js +843 -364
- package/package.json +7 -2
|
@@ -52,15 +52,6 @@ var parameters = {
|
|
|
52
52
|
type: "text",
|
|
53
53
|
secret: false,
|
|
54
54
|
required: false
|
|
55
|
-
}),
|
|
56
|
-
developerToken: new ParameterDefinition({
|
|
57
|
-
slug: "developer-token",
|
|
58
|
-
name: "Google Ads Developer Token",
|
|
59
|
-
description: "The developer token for accessing the Google Ads API. Required for all API requests.",
|
|
60
|
-
envVarBaseKey: "GOOGLE_ADS_DEVELOPER_TOKEN",
|
|
61
|
-
type: "text",
|
|
62
|
-
secret: true,
|
|
63
|
-
required: true
|
|
64
55
|
})
|
|
65
56
|
};
|
|
66
57
|
|
|
@@ -69,12 +60,6 @@ var BASE_URL = "https://googleads.googleapis.com/v18/";
|
|
|
69
60
|
function createClient(params, fetchFn = fetch) {
|
|
70
61
|
const rawCustomerId = params[parameters.customerId.slug];
|
|
71
62
|
const defaultCustomerId = rawCustomerId?.replace(/-/g, "") ?? "";
|
|
72
|
-
const developerToken = params[parameters.developerToken.slug];
|
|
73
|
-
if (!developerToken) {
|
|
74
|
-
throw new Error(
|
|
75
|
-
`google-ads: missing required parameter: ${parameters.developerToken.slug}`
|
|
76
|
-
);
|
|
77
|
-
}
|
|
78
63
|
function resolveCustomerId(override) {
|
|
79
64
|
const id = override?.replace(/-/g, "") ?? defaultCustomerId;
|
|
80
65
|
if (!id) {
|
|
@@ -88,7 +73,6 @@ function createClient(params, fetchFn = fetch) {
|
|
|
88
73
|
const resolvedPath = defaultCustomerId ? path2.replace(/\{customerId\}/g, defaultCustomerId) : path2;
|
|
89
74
|
const url = `${BASE_URL}${resolvedPath}`;
|
|
90
75
|
const headers = new Headers(init?.headers);
|
|
91
|
-
headers.set("developer-token", developerToken);
|
|
92
76
|
if (defaultCustomerId) {
|
|
93
77
|
headers.set("login-customer-id", defaultCustomerId);
|
|
94
78
|
}
|
|
@@ -99,7 +83,6 @@ function createClient(params, fetchFn = fetch) {
|
|
|
99
83
|
const url = `${BASE_URL}customers/${cid}/googleAds:searchStream`;
|
|
100
84
|
const headers = new Headers();
|
|
101
85
|
headers.set("Content-Type", "application/json");
|
|
102
|
-
headers.set("developer-token", developerToken);
|
|
103
86
|
headers.set("login-customer-id", cid);
|
|
104
87
|
const response = await fetchFn(url, {
|
|
105
88
|
method: "POST",
|
|
@@ -117,9 +100,7 @@ function createClient(params, fetchFn = fetch) {
|
|
|
117
100
|
}
|
|
118
101
|
async function listAccessibleCustomers() {
|
|
119
102
|
const url = `${BASE_URL}customers:listAccessibleCustomers`;
|
|
120
|
-
const
|
|
121
|
-
headers.set("developer-token", developerToken);
|
|
122
|
-
const response = await fetchFn(url, { method: "GET", headers });
|
|
103
|
+
const response = await fetchFn(url, { method: "GET" });
|
|
123
104
|
if (!response.ok) {
|
|
124
105
|
const body = await response.text();
|
|
125
106
|
throw new Error(
|
|
@@ -360,7 +341,6 @@ var listCustomersTool = new ConnectorTool({
|
|
|
360
341
|
`[connector-request] google-ads/${connection2.name}: listCustomers`
|
|
361
342
|
);
|
|
362
343
|
try {
|
|
363
|
-
const developerToken = parameters.developerToken.getValue(connection2);
|
|
364
344
|
const token = await getProxyToken(config.oauthProxy);
|
|
365
345
|
const proxyUrl = `https://${config.oauthProxy.sandboxId}.${config.oauthProxy.previewBaseDomain}/_sqcore/connections/${connectionId}/request`;
|
|
366
346
|
const controller = new AbortController();
|
|
@@ -374,10 +354,7 @@ var listCustomersTool = new ConnectorTool({
|
|
|
374
354
|
},
|
|
375
355
|
body: JSON.stringify({
|
|
376
356
|
url: `${BASE_URL2}customers:listAccessibleCustomers`,
|
|
377
|
-
method: "GET"
|
|
378
|
-
headers: {
|
|
379
|
-
"developer-token": developerToken
|
|
380
|
-
}
|
|
357
|
+
method: "GET"
|
|
381
358
|
}),
|
|
382
359
|
signal: controller.signal
|
|
383
360
|
});
|
|
@@ -403,7 +380,6 @@ var listCustomersTool = new ConnectorTool({
|
|
|
403
380
|
method: "POST",
|
|
404
381
|
headers: {
|
|
405
382
|
"Content-Type": "application/json",
|
|
406
|
-
"developer-token": developerToken,
|
|
407
383
|
"login-customer-id": cid
|
|
408
384
|
},
|
|
409
385
|
body: JSON.stringify({
|
|
@@ -449,30 +425,24 @@ var googleAdsOnboarding = new ConnectorOnboarding({
|
|
|
449
425
|
connectionSetupInstructions: {
|
|
450
426
|
ja: `\u4EE5\u4E0B\u306E\u624B\u9806\u3067Google Ads (OAuth) \u30B3\u30CD\u30AF\u30B7\u30E7\u30F3\u306E\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u3092\u884C\u3063\u3066\u304F\u3060\u3055\u3044\u3002
|
|
451
427
|
|
|
452
|
-
1. \
|
|
428
|
+
1. \`${listCustomersToolName}\` \u3092\u547C\u3073\u51FA\u3057\u3066\u3001OAuth\u3067\u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306AGoogle Ads\u30AB\u30B9\u30BF\u30DE\u30FC\u30A2\u30AB\u30A6\u30F3\u30C8\u4E00\u89A7\u3092\u53D6\u5F97\u3059\u308B
|
|
453
429
|
2. \`updateConnectionParameters\` \u3092\u547C\u3073\u51FA\u3059:
|
|
454
|
-
- \`parameterSlug\`: \`"developer-token"\`
|
|
455
|
-
- \`value\`: \u30E6\u30FC\u30B6\u30FC\u304C\u63D0\u4F9B\u3057\u305F Developer Token
|
|
456
|
-
3. \`${listCustomersToolName}\` \u3092\u547C\u3073\u51FA\u3057\u3066\u3001OAuth\u3067\u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306AGoogle Ads\u30AB\u30B9\u30BF\u30DE\u30FC\u30A2\u30AB\u30A6\u30F3\u30C8\u4E00\u89A7\u3092\u53D6\u5F97\u3059\u308B
|
|
457
|
-
4. \`updateConnectionParameters\` \u3092\u547C\u3073\u51FA\u3059:
|
|
458
430
|
- \`parameterSlug\`: \`"customer-id"\`
|
|
459
431
|
- \`options\`: \u30AB\u30B9\u30BF\u30DE\u30FC\u4E00\u89A7\u3002\u5404 option \u306E \`label\` \u306F \`\u30A2\u30AB\u30A6\u30F3\u30C8\u540D (id: \u30AB\u30B9\u30BF\u30DE\u30FCID)\` \u306E\u5F62\u5F0F\u3001\`value\` \u306F\u30AB\u30B9\u30BF\u30DE\u30FCID
|
|
460
|
-
|
|
432
|
+
- \u30AB\u30B9\u30BF\u30DE\u30FC\u304C **0\u4EF6** \u306E\u5834\u5408\u306F\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u3092\u4E2D\u65AD\u3057\u3001\u30E6\u30FC\u30B6\u30FC\u306B\u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306A\u30A2\u30AB\u30A6\u30F3\u30C8\u304C\u306A\u3044\u65E8\u3092\u4F1D\u3048\u308B
|
|
433
|
+
3. \u30E6\u30FC\u30B6\u30FC\u304C\u9078\u629E\u3057\u305F\u30AB\u30B9\u30BF\u30DE\u30FC\u306E \`label\` \u304C\u30E1\u30C3\u30BB\u30FC\u30B8\u3068\u3057\u3066\u5C4A\u304F\u306E\u3067\u3001\u6B21\u306E\u30B9\u30C6\u30C3\u30D7\u306B\u9032\u3080
|
|
461
434
|
|
|
462
435
|
#### \u5236\u7D04
|
|
463
436
|
- **\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u4E2D\u306B\u30EC\u30DD\u30FC\u30C8\u30C7\u30FC\u30BF\u3092\u53D6\u5F97\u3057\u306A\u3044\u3053\u3068**\u3002\u5B9F\u884C\u3057\u3066\u3088\u3044\u306E\u306F\u4E0A\u8A18\u624B\u9806\u3067\u6307\u5B9A\u3055\u308C\u305F\u30E1\u30BF\u30C7\u30FC\u30BF\u53D6\u5F97\u306E\u307F
|
|
464
437
|
- \u30C4\u30FC\u30EB\u9593\u306F1\u6587\u3060\u3051\u66F8\u3044\u3066\u5373\u6B21\u306E\u30C4\u30FC\u30EB\u547C\u3073\u51FA\u3057\u3002\u4E0D\u8981\u306A\u8AAC\u660E\u306F\u7701\u7565\u3057\u3001\u52B9\u7387\u7684\u306B\u9032\u3081\u308B`,
|
|
465
438
|
en: `Follow these steps to set up the Google Ads (OAuth) connection.
|
|
466
439
|
|
|
467
|
-
1.
|
|
440
|
+
1. Call \`${listCustomersToolName}\` to get the list of Google Ads customer accounts accessible with the OAuth credentials
|
|
468
441
|
2. Call \`updateConnectionParameters\`:
|
|
469
|
-
- \`parameterSlug\`: \`"developer-token"\`
|
|
470
|
-
- \`value\`: The Developer Token provided by the user
|
|
471
|
-
3. Call \`${listCustomersToolName}\` to get the list of Google Ads customer accounts accessible with the OAuth credentials
|
|
472
|
-
4. Call \`updateConnectionParameters\`:
|
|
473
442
|
- \`parameterSlug\`: \`"customer-id"\`
|
|
474
443
|
- \`options\`: The customer list. Each option's \`label\` should be \`Account Name (id: customerId)\`, \`value\` should be the customer ID
|
|
475
|
-
|
|
444
|
+
- If **0 customers** are returned, abort setup and inform the user that no accessible accounts are available
|
|
445
|
+
3. The \`label\` of the user's selected customer will arrive as a message. Proceed to the next step
|
|
476
446
|
|
|
477
447
|
#### Constraints
|
|
478
448
|
- **Do NOT fetch report data during setup**. Only the metadata requests specified in the steps above are allowed
|
|
@@ -571,7 +541,6 @@ Authentication is handled automatically via OAuth proxy.
|
|
|
571
541
|
const controller = new AbortController();
|
|
572
542
|
const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS2);
|
|
573
543
|
try {
|
|
574
|
-
const developerToken = parameters.developerToken.getValue(connection2);
|
|
575
544
|
const response = await fetch(proxyUrl, {
|
|
576
545
|
method: "POST",
|
|
577
546
|
headers: {
|
|
@@ -583,7 +552,6 @@ Authentication is handled automatically via OAuth proxy.
|
|
|
583
552
|
method,
|
|
584
553
|
headers: {
|
|
585
554
|
"Content-Type": "application/json",
|
|
586
|
-
"developer-token": developerToken,
|
|
587
555
|
...customerId ? { "login-customer-id": customerId } : {}
|
|
588
556
|
},
|
|
589
557
|
...method === "POST" && body ? { body: JSON.stringify(body) } : {}
|
|
@@ -764,20 +732,12 @@ const customerIds = await ads.listAccessibleCustomers();
|
|
|
764
732
|
if (!customerId) {
|
|
765
733
|
return { success: true };
|
|
766
734
|
}
|
|
767
|
-
const developerToken = params[parameters.developerToken.slug];
|
|
768
|
-
if (!developerToken) {
|
|
769
|
-
return {
|
|
770
|
-
success: false,
|
|
771
|
-
error: "Developer token is required"
|
|
772
|
-
};
|
|
773
|
-
}
|
|
774
735
|
const url = `https://googleads.googleapis.com/v18/customers/${customerId}/googleAds:searchStream`;
|
|
775
736
|
try {
|
|
776
737
|
const res = await proxyFetch(url, {
|
|
777
738
|
method: "POST",
|
|
778
739
|
headers: {
|
|
779
740
|
"Content-Type": "application/json",
|
|
780
|
-
"developer-token": developerToken,
|
|
781
741
|
"login-customer-id": customerId
|
|
782
742
|
},
|
|
783
743
|
body: JSON.stringify({
|
package/dist/connectors/jdbc.js
CHANGED
|
@@ -20254,6 +20254,150 @@ var ParameterDefinition = class {
|
|
|
20254
20254
|
}
|
|
20255
20255
|
};
|
|
20256
20256
|
|
|
20257
|
+
// ../connectors/src/lib/ssh-tunnel.ts
|
|
20258
|
+
var sshTunnelParameters = {
|
|
20259
|
+
sshHost: new ParameterDefinition({
|
|
20260
|
+
slug: "ssh-host",
|
|
20261
|
+
name: "SSH Tunnel Host",
|
|
20262
|
+
description: "Optional. Hostname of the SSH bastion to tunnel through. Leave empty to connect directly.",
|
|
20263
|
+
envVarBaseKey: "SSH_TUNNEL_HOST",
|
|
20264
|
+
type: "text",
|
|
20265
|
+
secret: false,
|
|
20266
|
+
required: false
|
|
20267
|
+
}),
|
|
20268
|
+
sshPort: new ParameterDefinition({
|
|
20269
|
+
slug: "ssh-port",
|
|
20270
|
+
name: "SSH Tunnel Port",
|
|
20271
|
+
description: "Optional. SSH port of the bastion host (default: 22).",
|
|
20272
|
+
envVarBaseKey: "SSH_TUNNEL_PORT",
|
|
20273
|
+
type: "text",
|
|
20274
|
+
secret: false,
|
|
20275
|
+
required: false
|
|
20276
|
+
}),
|
|
20277
|
+
sshUsername: new ParameterDefinition({
|
|
20278
|
+
slug: "ssh-username",
|
|
20279
|
+
name: "SSH Tunnel Username",
|
|
20280
|
+
description: "Optional. Username for SSH authentication. Required when SSH Tunnel Host is set.",
|
|
20281
|
+
envVarBaseKey: "SSH_TUNNEL_USERNAME",
|
|
20282
|
+
type: "text",
|
|
20283
|
+
secret: false,
|
|
20284
|
+
required: false
|
|
20285
|
+
}),
|
|
20286
|
+
sshPrivateKeyBase64: new ParameterDefinition({
|
|
20287
|
+
slug: "ssh-private-key-base64",
|
|
20288
|
+
name: "SSH Private Key",
|
|
20289
|
+
description: "Optional. Private key (PEM, base64-encoded) used for SSH authentication. Required when SSH Tunnel Host is set.",
|
|
20290
|
+
envVarBaseKey: "SSH_TUNNEL_PRIVATE_KEY_BASE64",
|
|
20291
|
+
type: "base64EncodedText",
|
|
20292
|
+
secret: true,
|
|
20293
|
+
required: false
|
|
20294
|
+
}),
|
|
20295
|
+
sshPassphrase: new ParameterDefinition({
|
|
20296
|
+
slug: "ssh-passphrase",
|
|
20297
|
+
name: "SSH Private Key Passphrase",
|
|
20298
|
+
description: "Optional. Passphrase for the SSH private key, if it is encrypted.",
|
|
20299
|
+
envVarBaseKey: "SSH_TUNNEL_PASSPHRASE",
|
|
20300
|
+
type: "text",
|
|
20301
|
+
secret: true,
|
|
20302
|
+
required: false
|
|
20303
|
+
})
|
|
20304
|
+
};
|
|
20305
|
+
var NOOP_TUNNEL = (connectionUrl) => ({
|
|
20306
|
+
connectionUrl,
|
|
20307
|
+
close: async () => {
|
|
20308
|
+
}
|
|
20309
|
+
});
|
|
20310
|
+
var NOOP_TUNNEL_HOSTPORT = (host, port) => ({
|
|
20311
|
+
host,
|
|
20312
|
+
port,
|
|
20313
|
+
close: async () => {
|
|
20314
|
+
}
|
|
20315
|
+
});
|
|
20316
|
+
function connectionParamsToRecord(connection2) {
|
|
20317
|
+
const out = {};
|
|
20318
|
+
for (const p of connection2.parameters) {
|
|
20319
|
+
if (p.value != null) out[p.parameterSlug] = p.value;
|
|
20320
|
+
}
|
|
20321
|
+
return out;
|
|
20322
|
+
}
|
|
20323
|
+
async function maybeOpenSshTunnelHostPort(params, dbHost, dbPort) {
|
|
20324
|
+
const sshHost = params[sshTunnelParameters.sshHost.slug];
|
|
20325
|
+
if (!sshHost) return NOOP_TUNNEL_HOSTPORT(dbHost, dbPort);
|
|
20326
|
+
const sshUsername = params[sshTunnelParameters.sshUsername.slug];
|
|
20327
|
+
const sshPrivateKeyBase64 = params[sshTunnelParameters.sshPrivateKeyBase64.slug];
|
|
20328
|
+
if (!sshUsername || !sshPrivateKeyBase64) {
|
|
20329
|
+
throw new Error(
|
|
20330
|
+
"SSH tunnel requires `ssh-username` and `ssh-private-key-base64` when `ssh-host` is set."
|
|
20331
|
+
);
|
|
20332
|
+
}
|
|
20333
|
+
const sshPort = Number(params[sshTunnelParameters.sshPort.slug] || "22") || 22;
|
|
20334
|
+
const sshPassphrase = params[sshTunnelParameters.sshPassphrase.slug];
|
|
20335
|
+
const [{ Client }, net] = await Promise.all([
|
|
20336
|
+
Promise.resolve().then(() => __toESM(require_lib3(), 1)),
|
|
20337
|
+
import("net")
|
|
20338
|
+
]);
|
|
20339
|
+
const sshClient = new Client();
|
|
20340
|
+
await new Promise((resolve, reject) => {
|
|
20341
|
+
sshClient.once("ready", () => resolve());
|
|
20342
|
+
sshClient.once("error", reject);
|
|
20343
|
+
sshClient.connect({
|
|
20344
|
+
host: sshHost,
|
|
20345
|
+
port: sshPort,
|
|
20346
|
+
username: sshUsername,
|
|
20347
|
+
privateKey: Buffer.from(sshPrivateKeyBase64, "base64"),
|
|
20348
|
+
passphrase: sshPassphrase || void 0,
|
|
20349
|
+
readyTimeout: 1e4
|
|
20350
|
+
});
|
|
20351
|
+
});
|
|
20352
|
+
const server = net.createServer((socket) => {
|
|
20353
|
+
sshClient.forwardOut(
|
|
20354
|
+
socket.remoteAddress ?? "127.0.0.1",
|
|
20355
|
+
socket.remotePort ?? 0,
|
|
20356
|
+
dbHost,
|
|
20357
|
+
dbPort,
|
|
20358
|
+
(err, stream) => {
|
|
20359
|
+
if (err) {
|
|
20360
|
+
socket.destroy(err);
|
|
20361
|
+
return;
|
|
20362
|
+
}
|
|
20363
|
+
socket.pipe(stream).pipe(socket);
|
|
20364
|
+
}
|
|
20365
|
+
);
|
|
20366
|
+
});
|
|
20367
|
+
await new Promise((resolve, reject) => {
|
|
20368
|
+
server.once("error", reject);
|
|
20369
|
+
server.listen(0, "127.0.0.1", () => resolve());
|
|
20370
|
+
});
|
|
20371
|
+
const address = server.address();
|
|
20372
|
+
if (!address || typeof address === "string") {
|
|
20373
|
+
server.close();
|
|
20374
|
+
sshClient.end();
|
|
20375
|
+
throw new Error("Failed to allocate local port for SSH tunnel.");
|
|
20376
|
+
}
|
|
20377
|
+
return {
|
|
20378
|
+
host: "127.0.0.1",
|
|
20379
|
+
port: address.port,
|
|
20380
|
+
close: async () => {
|
|
20381
|
+
await new Promise((resolve) => server.close(() => resolve()));
|
|
20382
|
+
sshClient.end();
|
|
20383
|
+
}
|
|
20384
|
+
};
|
|
20385
|
+
}
|
|
20386
|
+
async function maybeOpenSshTunnel(params, connectionUrl, defaultDbPort) {
|
|
20387
|
+
const sshHost = params[sshTunnelParameters.sshHost.slug];
|
|
20388
|
+
if (!sshHost) return NOOP_TUNNEL(connectionUrl);
|
|
20389
|
+
const url = new URL(connectionUrl);
|
|
20390
|
+
const dbHost = url.hostname;
|
|
20391
|
+
const dbPort = url.port ? Number(url.port) : defaultDbPort;
|
|
20392
|
+
const tunnel = await maybeOpenSshTunnelHostPort(params, dbHost, dbPort);
|
|
20393
|
+
url.hostname = tunnel.host;
|
|
20394
|
+
url.port = String(tunnel.port);
|
|
20395
|
+
return {
|
|
20396
|
+
connectionUrl: url.toString(),
|
|
20397
|
+
close: tunnel.close
|
|
20398
|
+
};
|
|
20399
|
+
}
|
|
20400
|
+
|
|
20257
20401
|
// ../connectors/src/connectors/sqlserver/utils.ts
|
|
20258
20402
|
var SQLSERVER_PREFIX_RE = /^(?:jdbc:)?sqlserver:\/\//i;
|
|
20259
20403
|
var TRUE_VALUES = /* @__PURE__ */ new Set(["true", "1", "yes"]);
|
|
@@ -20332,17 +20476,27 @@ async function importMssql() {
|
|
|
20332
20476
|
}
|
|
20333
20477
|
async function runMssqlQuery(parsed, sql, options = {}) {
|
|
20334
20478
|
const sqlMod = await importMssql();
|
|
20335
|
-
const
|
|
20336
|
-
|
|
20337
|
-
|
|
20338
|
-
|
|
20339
|
-
|
|
20479
|
+
const tunnel = options.tunnelParams ? await maybeOpenSshTunnelHostPort(
|
|
20480
|
+
options.tunnelParams,
|
|
20481
|
+
parsed.server,
|
|
20482
|
+
parsed.port
|
|
20483
|
+
) : null;
|
|
20340
20484
|
try {
|
|
20341
|
-
const
|
|
20342
|
-
const
|
|
20343
|
-
|
|
20485
|
+
const tunneled = tunnel ? { ...parsed, server: tunnel.host, port: tunnel.port } : parsed;
|
|
20486
|
+
const config = toMssqlConfig(tunneled, {
|
|
20487
|
+
encrypt: options.forceEncrypt
|
|
20488
|
+
});
|
|
20489
|
+
const pool = new sqlMod.ConnectionPool(config);
|
|
20490
|
+
await pool.connect();
|
|
20491
|
+
try {
|
|
20492
|
+
const result = await pool.request().query(sql);
|
|
20493
|
+
const recordset = result.recordset ?? [];
|
|
20494
|
+
return { rows: recordset };
|
|
20495
|
+
} finally {
|
|
20496
|
+
await pool.close();
|
|
20497
|
+
}
|
|
20344
20498
|
} finally {
|
|
20345
|
-
await
|
|
20499
|
+
await tunnel?.close();
|
|
20346
20500
|
}
|
|
20347
20501
|
}
|
|
20348
20502
|
async function checkMssqlConnection(url, credentials, options = {}) {
|
|
@@ -20430,35 +20584,69 @@ function parseOracleJdbcUrl(jdbcUrl, options = {}) {
|
|
|
20430
20584
|
function redactOracleUrl(jdbcUrl) {
|
|
20431
20585
|
return jdbcUrl.replace(/(:\/\/)([^@/]+)@/, "$1***@").replace(/(thin:)([^@]+)@/i, "$1***@");
|
|
20432
20586
|
}
|
|
20587
|
+
function parseOracleConnectStringHostPort(connectString) {
|
|
20588
|
+
const m = /^([^:/]+):(\d+)(.*)$/.exec(connectString);
|
|
20589
|
+
if (!m) return null;
|
|
20590
|
+
return { host: m[1], port: Number(m[2]), trailing: m[3] };
|
|
20591
|
+
}
|
|
20592
|
+
function rewriteOracleConnectStringHostPort(connectString, host, port) {
|
|
20593
|
+
const parts = parseOracleConnectStringHostPort(connectString);
|
|
20594
|
+
if (!parts) return connectString;
|
|
20595
|
+
return `${host}:${port}${parts.trailing}`;
|
|
20596
|
+
}
|
|
20433
20597
|
|
|
20434
20598
|
// ../connectors/src/lib/oracle-runner.ts
|
|
20435
20599
|
async function importOracleDb() {
|
|
20436
20600
|
const mod = await import("oracledb");
|
|
20437
20601
|
return mod.default ?? mod;
|
|
20438
20602
|
}
|
|
20439
|
-
async function runOracleQuery(parsed, sql) {
|
|
20603
|
+
async function runOracleQuery(parsed, sql, options = {}) {
|
|
20440
20604
|
const oracledb = await importOracleDb();
|
|
20441
|
-
|
|
20442
|
-
|
|
20443
|
-
|
|
20444
|
-
|
|
20445
|
-
|
|
20605
|
+
let tunnel = null;
|
|
20606
|
+
if (options.tunnelParams) {
|
|
20607
|
+
const hostPort = parseOracleConnectStringHostPort(parsed.connectString);
|
|
20608
|
+
if (hostPort) {
|
|
20609
|
+
tunnel = await maybeOpenSshTunnelHostPort(
|
|
20610
|
+
options.tunnelParams,
|
|
20611
|
+
hostPort.host,
|
|
20612
|
+
hostPort.port
|
|
20613
|
+
);
|
|
20614
|
+
}
|
|
20615
|
+
}
|
|
20446
20616
|
try {
|
|
20447
|
-
const
|
|
20448
|
-
|
|
20449
|
-
|
|
20450
|
-
|
|
20451
|
-
|
|
20617
|
+
const connectString = tunnel ? rewriteOracleConnectStringHostPort(
|
|
20618
|
+
parsed.connectString,
|
|
20619
|
+
tunnel.host,
|
|
20620
|
+
tunnel.port
|
|
20621
|
+
) : parsed.connectString;
|
|
20622
|
+
const connection2 = await oracledb.getConnection({
|
|
20623
|
+
user: parsed.user,
|
|
20624
|
+
password: parsed.password,
|
|
20625
|
+
connectString
|
|
20452
20626
|
});
|
|
20453
|
-
return { rows: result.rows ?? [] };
|
|
20454
|
-
} finally {
|
|
20455
20627
|
try {
|
|
20456
|
-
await connection2.
|
|
20457
|
-
|
|
20628
|
+
const result = await connection2.execute(
|
|
20629
|
+
sql,
|
|
20630
|
+
[],
|
|
20631
|
+
{
|
|
20632
|
+
outFormat: oracledb.OUT_FORMAT_OBJECT,
|
|
20633
|
+
// Bound by the connector's own row cap, but keep the driver from
|
|
20634
|
+
// streaming arbitrarily large result sets.
|
|
20635
|
+
maxRows: 5e3
|
|
20636
|
+
}
|
|
20637
|
+
);
|
|
20638
|
+
return { rows: result.rows ?? [] };
|
|
20639
|
+
} finally {
|
|
20640
|
+
try {
|
|
20641
|
+
await connection2.close();
|
|
20642
|
+
} catch {
|
|
20643
|
+
}
|
|
20458
20644
|
}
|
|
20645
|
+
} finally {
|
|
20646
|
+
await tunnel?.close();
|
|
20459
20647
|
}
|
|
20460
20648
|
}
|
|
20461
|
-
async function checkOracleConnection(url, credentials) {
|
|
20649
|
+
async function checkOracleConnection(url, credentials, options = {}) {
|
|
20462
20650
|
let parsed;
|
|
20463
20651
|
try {
|
|
20464
20652
|
parsed = parseOracleJdbcUrl(url, credentials);
|
|
@@ -20469,7 +20657,7 @@ async function checkOracleConnection(url, credentials) {
|
|
|
20469
20657
|
};
|
|
20470
20658
|
}
|
|
20471
20659
|
try {
|
|
20472
|
-
await runOracleQuery(parsed, "SELECT 1 FROM DUAL");
|
|
20660
|
+
await runOracleQuery(parsed, "SELECT 1 FROM DUAL", options);
|
|
20473
20661
|
return { success: true };
|
|
20474
20662
|
} catch (err) {
|
|
20475
20663
|
let msg = err instanceof Error ? err.message : String(err);
|
|
@@ -20478,134 +20666,6 @@ async function checkOracleConnection(url, credentials) {
|
|
|
20478
20666
|
}
|
|
20479
20667
|
}
|
|
20480
20668
|
|
|
20481
|
-
// ../connectors/src/lib/ssh-tunnel.ts
|
|
20482
|
-
var sshTunnelParameters = {
|
|
20483
|
-
sshHost: new ParameterDefinition({
|
|
20484
|
-
slug: "ssh-host",
|
|
20485
|
-
name: "SSH Tunnel Host",
|
|
20486
|
-
description: "Optional. Hostname of the SSH bastion to tunnel through. Leave empty to connect directly.",
|
|
20487
|
-
envVarBaseKey: "SSH_TUNNEL_HOST",
|
|
20488
|
-
type: "text",
|
|
20489
|
-
secret: false,
|
|
20490
|
-
required: false
|
|
20491
|
-
}),
|
|
20492
|
-
sshPort: new ParameterDefinition({
|
|
20493
|
-
slug: "ssh-port",
|
|
20494
|
-
name: "SSH Tunnel Port",
|
|
20495
|
-
description: "Optional. SSH port of the bastion host (default: 22).",
|
|
20496
|
-
envVarBaseKey: "SSH_TUNNEL_PORT",
|
|
20497
|
-
type: "text",
|
|
20498
|
-
secret: false,
|
|
20499
|
-
required: false
|
|
20500
|
-
}),
|
|
20501
|
-
sshUsername: new ParameterDefinition({
|
|
20502
|
-
slug: "ssh-username",
|
|
20503
|
-
name: "SSH Tunnel Username",
|
|
20504
|
-
description: "Optional. Username for SSH authentication. Required when SSH Tunnel Host is set.",
|
|
20505
|
-
envVarBaseKey: "SSH_TUNNEL_USERNAME",
|
|
20506
|
-
type: "text",
|
|
20507
|
-
secret: false,
|
|
20508
|
-
required: false
|
|
20509
|
-
}),
|
|
20510
|
-
sshPrivateKeyBase64: new ParameterDefinition({
|
|
20511
|
-
slug: "ssh-private-key-base64",
|
|
20512
|
-
name: "SSH Private Key",
|
|
20513
|
-
description: "Optional. Private key (PEM, base64-encoded) used for SSH authentication. Required when SSH Tunnel Host is set.",
|
|
20514
|
-
envVarBaseKey: "SSH_TUNNEL_PRIVATE_KEY_BASE64",
|
|
20515
|
-
type: "base64EncodedText",
|
|
20516
|
-
secret: true,
|
|
20517
|
-
required: false
|
|
20518
|
-
}),
|
|
20519
|
-
sshPassphrase: new ParameterDefinition({
|
|
20520
|
-
slug: "ssh-passphrase",
|
|
20521
|
-
name: "SSH Private Key Passphrase",
|
|
20522
|
-
description: "Optional. Passphrase for the SSH private key, if it is encrypted.",
|
|
20523
|
-
envVarBaseKey: "SSH_TUNNEL_PASSPHRASE",
|
|
20524
|
-
type: "text",
|
|
20525
|
-
secret: true,
|
|
20526
|
-
required: false
|
|
20527
|
-
})
|
|
20528
|
-
};
|
|
20529
|
-
var NOOP_TUNNEL = (connectionUrl) => ({
|
|
20530
|
-
connectionUrl,
|
|
20531
|
-
close: async () => {
|
|
20532
|
-
}
|
|
20533
|
-
});
|
|
20534
|
-
function connectionParamsToRecord(connection2) {
|
|
20535
|
-
const out = {};
|
|
20536
|
-
for (const p of connection2.parameters) {
|
|
20537
|
-
if (p.value != null) out[p.parameterSlug] = p.value;
|
|
20538
|
-
}
|
|
20539
|
-
return out;
|
|
20540
|
-
}
|
|
20541
|
-
async function maybeOpenSshTunnel(params, connectionUrl, defaultDbPort) {
|
|
20542
|
-
const sshHost = params[sshTunnelParameters.sshHost.slug];
|
|
20543
|
-
if (!sshHost) return NOOP_TUNNEL(connectionUrl);
|
|
20544
|
-
const sshUsername = params[sshTunnelParameters.sshUsername.slug];
|
|
20545
|
-
const sshPrivateKeyBase64 = params[sshTunnelParameters.sshPrivateKeyBase64.slug];
|
|
20546
|
-
if (!sshUsername || !sshPrivateKeyBase64) {
|
|
20547
|
-
throw new Error(
|
|
20548
|
-
"SSH tunnel requires `ssh-username` and `ssh-private-key-base64` when `ssh-host` is set."
|
|
20549
|
-
);
|
|
20550
|
-
}
|
|
20551
|
-
const sshPort = Number(params[sshTunnelParameters.sshPort.slug] || "22") || 22;
|
|
20552
|
-
const sshPassphrase = params[sshTunnelParameters.sshPassphrase.slug];
|
|
20553
|
-
const url = new URL(connectionUrl);
|
|
20554
|
-
const dbHost = url.hostname;
|
|
20555
|
-
const dbPort = url.port ? Number(url.port) : defaultDbPort;
|
|
20556
|
-
const [{ Client }, net] = await Promise.all([
|
|
20557
|
-
Promise.resolve().then(() => __toESM(require_lib3(), 1)),
|
|
20558
|
-
import("net")
|
|
20559
|
-
]);
|
|
20560
|
-
const sshClient = new Client();
|
|
20561
|
-
await new Promise((resolve, reject) => {
|
|
20562
|
-
sshClient.once("ready", () => resolve());
|
|
20563
|
-
sshClient.once("error", reject);
|
|
20564
|
-
sshClient.connect({
|
|
20565
|
-
host: sshHost,
|
|
20566
|
-
port: sshPort,
|
|
20567
|
-
username: sshUsername,
|
|
20568
|
-
privateKey: Buffer.from(sshPrivateKeyBase64, "base64"),
|
|
20569
|
-
passphrase: sshPassphrase || void 0,
|
|
20570
|
-
readyTimeout: 1e4
|
|
20571
|
-
});
|
|
20572
|
-
});
|
|
20573
|
-
const server = net.createServer((socket) => {
|
|
20574
|
-
sshClient.forwardOut(
|
|
20575
|
-
socket.remoteAddress ?? "127.0.0.1",
|
|
20576
|
-
socket.remotePort ?? 0,
|
|
20577
|
-
dbHost,
|
|
20578
|
-
dbPort,
|
|
20579
|
-
(err, stream) => {
|
|
20580
|
-
if (err) {
|
|
20581
|
-
socket.destroy(err);
|
|
20582
|
-
return;
|
|
20583
|
-
}
|
|
20584
|
-
socket.pipe(stream).pipe(socket);
|
|
20585
|
-
}
|
|
20586
|
-
);
|
|
20587
|
-
});
|
|
20588
|
-
await new Promise((resolve, reject) => {
|
|
20589
|
-
server.once("error", reject);
|
|
20590
|
-
server.listen(0, "127.0.0.1", () => resolve());
|
|
20591
|
-
});
|
|
20592
|
-
const address = server.address();
|
|
20593
|
-
if (!address || typeof address === "string") {
|
|
20594
|
-
server.close();
|
|
20595
|
-
sshClient.end();
|
|
20596
|
-
throw new Error("Failed to allocate local port for SSH tunnel.");
|
|
20597
|
-
}
|
|
20598
|
-
url.hostname = "127.0.0.1";
|
|
20599
|
-
url.port = String(address.port);
|
|
20600
|
-
return {
|
|
20601
|
-
connectionUrl: url.toString(),
|
|
20602
|
-
close: async () => {
|
|
20603
|
-
await new Promise((resolve) => server.close(() => resolve()));
|
|
20604
|
-
sshClient.end();
|
|
20605
|
-
}
|
|
20606
|
-
};
|
|
20607
|
-
}
|
|
20608
|
-
|
|
20609
20669
|
// ../connectors/src/connectors/jdbc/parameters.ts
|
|
20610
20670
|
var parameters = {
|
|
20611
20671
|
jdbcUrl: new ParameterDefinition({
|
|
@@ -20749,7 +20809,9 @@ function createClient(params) {
|
|
|
20749
20809
|
username,
|
|
20750
20810
|
password
|
|
20751
20811
|
});
|
|
20752
|
-
const result = await runMssqlQuery(mssqlParsed, sql
|
|
20812
|
+
const result = await runMssqlQuery(mssqlParsed, sql, {
|
|
20813
|
+
tunnelParams: params
|
|
20814
|
+
});
|
|
20753
20815
|
return result.rows;
|
|
20754
20816
|
}
|
|
20755
20817
|
if (parsed.driver === "oracle") {
|
|
@@ -20763,7 +20825,9 @@ function createClient(params) {
|
|
|
20763
20825
|
password
|
|
20764
20826
|
});
|
|
20765
20827
|
const cleanSql = sql.replace(/;\s*$/, "");
|
|
20766
|
-
const result = await runOracleQuery(oracleParsed, cleanSql
|
|
20828
|
+
const result = await runOracleQuery(oracleParsed, cleanSql, {
|
|
20829
|
+
tunnelParams: params
|
|
20830
|
+
});
|
|
20767
20831
|
return result.rows;
|
|
20768
20832
|
}
|
|
20769
20833
|
const tunnel = await maybeOpenSshTunnel(
|
|
@@ -21042,12 +21106,13 @@ Always bound results: LIMIT for PG/MySQL/Redshift, TOP for SQL Server, FETCH FIR
|
|
|
21042
21106
|
};
|
|
21043
21107
|
}
|
|
21044
21108
|
try {
|
|
21109
|
+
const tunnelParams = connectionParamsToRecord(connection2);
|
|
21045
21110
|
if (parsed.driver === "sqlserver") {
|
|
21046
21111
|
const mssqlParsed = parseSqlServerJdbcUrl(parsed.originalUrl, {
|
|
21047
21112
|
username,
|
|
21048
21113
|
password
|
|
21049
21114
|
});
|
|
21050
|
-
const result = await runMssqlQuery(mssqlParsed, sql);
|
|
21115
|
+
const result = await runMssqlQuery(mssqlParsed, sql, { tunnelParams });
|
|
21051
21116
|
const rows = result.rows;
|
|
21052
21117
|
return {
|
|
21053
21118
|
success: true,
|
|
@@ -21062,7 +21127,9 @@ Always bound results: LIMIT for PG/MySQL/Redshift, TOP for SQL Server, FETCH FIR
|
|
|
21062
21127
|
password
|
|
21063
21128
|
});
|
|
21064
21129
|
const cleanSql = sql.replace(/;\s*$/, "");
|
|
21065
|
-
const result = await runOracleQuery(oracleParsed, cleanSql
|
|
21130
|
+
const result = await runOracleQuery(oracleParsed, cleanSql, {
|
|
21131
|
+
tunnelParams
|
|
21132
|
+
});
|
|
21066
21133
|
const rows = result.rows;
|
|
21067
21134
|
return {
|
|
21068
21135
|
success: true,
|
|
@@ -21074,7 +21141,7 @@ Always bound results: LIMIT for PG/MySQL/Redshift, TOP for SQL Server, FETCH FIR
|
|
|
21074
21141
|
let tunnel;
|
|
21075
21142
|
try {
|
|
21076
21143
|
tunnel = await maybeOpenSshTunnel(
|
|
21077
|
-
|
|
21144
|
+
tunnelParams,
|
|
21078
21145
|
parsed.nativeUrl,
|
|
21079
21146
|
parsed.defaultPort
|
|
21080
21147
|
);
|
|
@@ -21248,10 +21315,18 @@ JDBC URL \u306E\u30D7\u30EC\u30D5\u30A3\u30C3\u30AF\u30B9\u306B\u3088\u308A\u65B
|
|
|
21248
21315
|
};
|
|
21249
21316
|
}
|
|
21250
21317
|
if (parsed.driver === "sqlserver") {
|
|
21251
|
-
return checkMssqlConnection(
|
|
21318
|
+
return checkMssqlConnection(
|
|
21319
|
+
parsed.originalUrl,
|
|
21320
|
+
{ username, password },
|
|
21321
|
+
{ tunnelParams: params }
|
|
21322
|
+
);
|
|
21252
21323
|
}
|
|
21253
21324
|
if (parsed.driver === "oracle") {
|
|
21254
|
-
return checkOracleConnection(
|
|
21325
|
+
return checkOracleConnection(
|
|
21326
|
+
parsed.originalUrl,
|
|
21327
|
+
{ username, password },
|
|
21328
|
+
{ tunnelParams: params }
|
|
21329
|
+
);
|
|
21255
21330
|
}
|
|
21256
21331
|
const tunnel = await maybeOpenSshTunnel(
|
|
21257
21332
|
params,
|
|
@@ -21308,10 +21383,12 @@ JDBC URL \u306E\u30D7\u30EC\u30D5\u30A3\u30C3\u30AF\u30B9\u306B\u3088\u308A\u65B
|
|
|
21308
21383
|
});
|
|
21309
21384
|
const sample = unwrapSampleLimit(sql);
|
|
21310
21385
|
if (sample) {
|
|
21311
|
-
const result = await runMssqlQuery(mssqlParsed, sample.inner
|
|
21386
|
+
const result = await runMssqlQuery(mssqlParsed, sample.inner, {
|
|
21387
|
+
tunnelParams: params
|
|
21388
|
+
});
|
|
21312
21389
|
return { rows: result.rows.slice(0, sample.limit) };
|
|
21313
21390
|
}
|
|
21314
|
-
return runMssqlQuery(mssqlParsed, sql);
|
|
21391
|
+
return runMssqlQuery(mssqlParsed, sql, { tunnelParams: params });
|
|
21315
21392
|
}
|
|
21316
21393
|
if (parsed.driver === "oracle") {
|
|
21317
21394
|
const oracleParsed = parseOracleJdbcUrl(parsed.originalUrl, {
|
|
@@ -21321,11 +21398,13 @@ JDBC URL \u306E\u30D7\u30EC\u30D5\u30A3\u30C3\u30AF\u30B9\u306B\u3088\u308A\u65B
|
|
|
21321
21398
|
const sample = unwrapSampleLimit(sql);
|
|
21322
21399
|
if (sample) {
|
|
21323
21400
|
const inner = sample.inner.replace(/;\s*$/, "");
|
|
21324
|
-
const result = await runOracleQuery(oracleParsed, inner
|
|
21401
|
+
const result = await runOracleQuery(oracleParsed, inner, {
|
|
21402
|
+
tunnelParams: params
|
|
21403
|
+
});
|
|
21325
21404
|
return { rows: result.rows.slice(0, sample.limit) };
|
|
21326
21405
|
}
|
|
21327
21406
|
const cleanSql = sql.replace(/;\s*$/, "");
|
|
21328
|
-
return runOracleQuery(oracleParsed, cleanSql);
|
|
21407
|
+
return runOracleQuery(oracleParsed, cleanSql, { tunnelParams: params });
|
|
21329
21408
|
}
|
|
21330
21409
|
const tunnel = await maybeOpenSshTunnel(
|
|
21331
21410
|
params,
|