@carllee1983/dbcli 1.5.0 → 1.5.2
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/CHANGELOG.md +14 -0
- package/README.md +18 -0
- package/README.zh-TW.md +18 -0
- package/assets/SKILL.md +5 -1
- package/dist/cli.mjs +180 -26
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,20 @@ All notable changes to dbcli are documented here.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.5.2] - 2026-04-22
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **Doctor diagnostics for MongoDB SRV**: `dbcli doctor` now reports whether the current execution environment can resolve `mongodb+srv://` connections directly or only through the DNS-over-HTTPS fallback used by the MongoDB adapter.
|
|
13
|
+
- **Documentation**: Clarified the new MongoDB SRV environment diagnostic in README, README.zh-TW, and `assets/SKILL.md`.
|
|
14
|
+
|
|
15
|
+
## [1.5.1] - 2026-04-22
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- **MongoDB SRV Connections**: `mongodb+srv://` URIs are now expanded and connected through the MongoDB adapter, and MongoDB operations consistently use the configured database.
|
|
20
|
+
- **MongoDB Documentation**: Clarified SRV URI support and configured-database behavior in README, README.zh-TW, and `assets/SKILL.md`.
|
|
21
|
+
|
|
8
22
|
## [1.5.0] - 2026-04-21
|
|
9
23
|
|
|
10
24
|
### Added
|
package/README.md
CHANGED
|
@@ -89,6 +89,23 @@ dbcli query "SELECT * FROM users"
|
|
|
89
89
|
dbcli skill --install claude
|
|
90
90
|
```
|
|
91
91
|
|
|
92
|
+
### MongoDB Atlas / SRV Connections
|
|
93
|
+
|
|
94
|
+
MongoDB connections are supported via both standard `mongodb://` URIs and Atlas-style `mongodb+srv://` URIs.
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
# Atlas / SRV connection
|
|
98
|
+
dbcli init --system mongodb --conn-name atlas --uri "mongodb+srv://user:pass@cluster.example.mongodb.net/mydb"
|
|
99
|
+
|
|
100
|
+
# List collections in the configured MongoDB database
|
|
101
|
+
dbcli list --use atlas
|
|
102
|
+
|
|
103
|
+
# Query a collection with JSON filter or pipeline
|
|
104
|
+
dbcli query '{"status":"active"}' --collection users --use atlas
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
For MongoDB, `list` and `query` operate on the database configured for the connection, and `query` requires `--collection <name>`.
|
|
108
|
+
|
|
92
109
|
---
|
|
93
110
|
|
|
94
111
|
## Multi-connection Support (v2)
|
|
@@ -637,6 +654,7 @@ dbcli doctor --format json # JSON output for AI agents
|
|
|
637
654
|
- **Environment:** Bun version compatibility, dbcli version (compares with npm registry)
|
|
638
655
|
- **Configuration:** Config file exists/valid, permission level, blacklist completeness
|
|
639
656
|
- **Connection & Data:** Database connectivity, schema cache freshness (> 7 days warning), large table warnings (> 1M rows)
|
|
657
|
+
- **MongoDB SRV diagnostics:** For `mongodb+srv://` connections, `doctor` reports whether the current execution environment can resolve SRV records directly or only through the DNS-over-HTTPS fallback used by `dbcli`
|
|
640
658
|
|
|
641
659
|
**Options:** `--format <text|json>`
|
|
642
660
|
**Exit code:** 0 = all pass or warnings only, 1 = errors found
|
package/README.zh-TW.md
CHANGED
|
@@ -95,6 +95,23 @@ dbcli migrate create posts --column "id:int:pk" "title:varchar(100)"
|
|
|
95
95
|
dbcli skill --install claude
|
|
96
96
|
```
|
|
97
97
|
|
|
98
|
+
### MongoDB Atlas / SRV 連線
|
|
99
|
+
|
|
100
|
+
MongoDB 連線同時支援標準 `mongodb://` URI 與 Atlas 常用的 `mongodb+srv://` URI。
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
# Atlas / SRV 連線
|
|
104
|
+
dbcli init --system mongodb --conn-name atlas --uri "mongodb+srv://user:pass@cluster.example.mongodb.net/mydb"
|
|
105
|
+
|
|
106
|
+
# 列出該 MongoDB 連線中的集合
|
|
107
|
+
dbcli list --use atlas
|
|
108
|
+
|
|
109
|
+
# 以 JSON filter 或 pipeline 查詢某個集合
|
|
110
|
+
dbcli query '{"status":"active"}' --collection users --use atlas
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
對 MongoDB 而言,`list` 與 `query` 會使用該連線設定中的資料庫;`query` 也必須指定 `--collection <名稱>`。
|
|
114
|
+
|
|
98
115
|
---
|
|
99
116
|
|
|
100
117
|
## 多重連線支援 (v2)
|
|
@@ -647,6 +664,7 @@ dbcli doctor --format json # JSON 輸出(供 AI 代理)
|
|
|
647
664
|
- **環境:** Bun 版本相容性、dbcli 版本(與 npm registry 比對)
|
|
648
665
|
- **設定:** 設定檔是否存在/有效、權限等級、黑名單完整性
|
|
649
666
|
- **連線與資料:** 資料庫連線、schema 快取新鮮度(超過 7 天警告)、大表警告(超過 100 萬列)
|
|
667
|
+
- **MongoDB SRV 偵測:** 對 `mongodb+srv://` 連線,`doctor` 會回報目前執行環境能否直接解析 SRV 記錄,或只能依賴 dbcli 內建的 DNS-over-HTTPS fallback
|
|
650
668
|
|
|
651
669
|
**選項:** `--format <text|json>`
|
|
652
670
|
**結束代碼:** 0 = 全部通過或僅警告,1 = 有錯誤
|
package/assets/SKILL.md
CHANGED
|
@@ -248,6 +248,8 @@ dbcli doctor --format json # JSON output for AI agents
|
|
|
248
248
|
- Configuration: config file exists/valid, permission level, blacklist completeness (detects unprotected sensitive columns)
|
|
249
249
|
- Connection & Data: database connectivity, schema cache freshness (warns if > 7 days), large table warnings (> 1M rows)
|
|
250
250
|
|
|
251
|
+
> **MongoDB SRV diagnostics:** When the active connection uses `mongodb+srv://`, `doctor` reports whether the current runtime can resolve SRV records directly or only through the DNS-over-HTTPS fallback used by dbcli. This helps spot execution-environment DNS restrictions even when Compass can connect.
|
|
252
|
+
|
|
251
253
|
**Exit code:** 0 if all pass or warnings only, 1 if any error
|
|
252
254
|
**Options:** `--format <text|json>`
|
|
253
255
|
|
|
@@ -351,6 +353,8 @@ dbcli migrate drop-enum status --execute --force
|
|
|
351
353
|
|
|
352
354
|
MongoDB connections use a JSON-based query model instead of SQL.
|
|
353
355
|
|
|
356
|
+
Atlas-style `mongodb+srv://` URIs are supported. `list` and `query` run against the database configured for the connection, and `query` always requires `--collection <name>`.
|
|
357
|
+
|
|
354
358
|
**Supported commands:** `init`, `list`, `query`, `status`, `use`, `shell`, `doctor`, `upgrade`, `completion`
|
|
355
359
|
|
|
356
360
|
**Not supported (exit with error):** `schema`, `insert`, `update`, `delete`, `export`, `diff`, `migrate`, `check`
|
|
@@ -359,7 +363,7 @@ MongoDB connections use a JSON-based query model instead of SQL.
|
|
|
359
363
|
|
|
360
364
|
```bash
|
|
361
365
|
# 1. Initialize (URI or individual params)
|
|
362
|
-
dbcli init --system mongodb --uri "mongodb://
|
|
366
|
+
dbcli init --system mongodb --uri "mongodb+srv://user:pass@cluster.example.mongodb.net/mydb"
|
|
363
367
|
|
|
364
368
|
# 2. List collections
|
|
365
369
|
dbcli list --format json
|
package/dist/cli.mjs
CHANGED
|
@@ -63644,7 +63644,7 @@ var require_URLSearchParams = __commonJS((exports) => {
|
|
|
63644
63644
|
}
|
|
63645
63645
|
const ctorRegistry = utils.initCtorRegistry(globalObject);
|
|
63646
63646
|
|
|
63647
|
-
class
|
|
63647
|
+
class URLSearchParams2 {
|
|
63648
63648
|
constructor() {
|
|
63649
63649
|
const args = [];
|
|
63650
63650
|
{
|
|
@@ -63923,7 +63923,7 @@ var require_URLSearchParams = __commonJS((exports) => {
|
|
|
63923
63923
|
return esValue[implSymbol]["size"];
|
|
63924
63924
|
}
|
|
63925
63925
|
}
|
|
63926
|
-
Object.defineProperties(
|
|
63926
|
+
Object.defineProperties(URLSearchParams2.prototype, {
|
|
63927
63927
|
append: { enumerable: true },
|
|
63928
63928
|
delete: { enumerable: true },
|
|
63929
63929
|
get: { enumerable: true },
|
|
@@ -63938,9 +63938,9 @@ var require_URLSearchParams = __commonJS((exports) => {
|
|
|
63938
63938
|
forEach: { enumerable: true },
|
|
63939
63939
|
size: { enumerable: true },
|
|
63940
63940
|
[Symbol.toStringTag]: { value: "URLSearchParams", configurable: true },
|
|
63941
|
-
[Symbol.iterator]: { value:
|
|
63941
|
+
[Symbol.iterator]: { value: URLSearchParams2.prototype.entries, configurable: true, writable: true }
|
|
63942
63942
|
});
|
|
63943
|
-
ctorRegistry[interfaceName] =
|
|
63943
|
+
ctorRegistry[interfaceName] = URLSearchParams2;
|
|
63944
63944
|
ctorRegistry["URLSearchParams Iterator"] = Object.create(ctorRegistry["%IteratorPrototype%"], {
|
|
63945
63945
|
[Symbol.toStringTag]: {
|
|
63946
63946
|
configurable: true,
|
|
@@ -63967,7 +63967,7 @@ var require_URLSearchParams = __commonJS((exports) => {
|
|
|
63967
63967
|
Object.defineProperty(globalObject, interfaceName, {
|
|
63968
63968
|
configurable: true,
|
|
63969
63969
|
writable: true,
|
|
63970
|
-
value:
|
|
63970
|
+
value: URLSearchParams2
|
|
63971
63971
|
});
|
|
63972
63972
|
};
|
|
63973
63973
|
var Impl = require_URLSearchParams_impl();
|
|
@@ -63977,7 +63977,7 @@ var require_URLSearchParams = __commonJS((exports) => {
|
|
|
63977
63977
|
var require_URL_impl = __commonJS((exports) => {
|
|
63978
63978
|
var usm = require_url_state_machine();
|
|
63979
63979
|
var urlencoded = require_urlencoded();
|
|
63980
|
-
var
|
|
63980
|
+
var URLSearchParams2 = require_URLSearchParams();
|
|
63981
63981
|
exports.implementation = class URLImpl {
|
|
63982
63982
|
constructor(globalObject, [url, base]) {
|
|
63983
63983
|
let parsedBase = null;
|
|
@@ -63993,7 +63993,7 @@ var require_URL_impl = __commonJS((exports) => {
|
|
|
63993
63993
|
}
|
|
63994
63994
|
const query = parsedURL.query !== null ? parsedURL.query : "";
|
|
63995
63995
|
this._url = parsedURL;
|
|
63996
|
-
this._query =
|
|
63996
|
+
this._query = URLSearchParams2.createImpl(globalObject, [query], { doNotStripQMark: true });
|
|
63997
63997
|
this._query._url = this;
|
|
63998
63998
|
}
|
|
63999
63999
|
static parse(globalObject, input, base) {
|
|
@@ -64551,19 +64551,19 @@ var require_URL = __commonJS((exports) => {
|
|
|
64551
64551
|
// node_modules/whatwg-url/webidl2js-wrapper.js
|
|
64552
64552
|
var require_webidl2js_wrapper = __commonJS((exports) => {
|
|
64553
64553
|
var URL2 = require_URL();
|
|
64554
|
-
var
|
|
64554
|
+
var URLSearchParams2 = require_URLSearchParams();
|
|
64555
64555
|
exports.URL = URL2;
|
|
64556
|
-
exports.URLSearchParams =
|
|
64556
|
+
exports.URLSearchParams = URLSearchParams2;
|
|
64557
64557
|
});
|
|
64558
64558
|
|
|
64559
64559
|
// node_modules/whatwg-url/index.js
|
|
64560
64560
|
var require_whatwg_url = __commonJS((exports) => {
|
|
64561
|
-
var { URL: URL2, URLSearchParams } = require_webidl2js_wrapper();
|
|
64561
|
+
var { URL: URL2, URLSearchParams: URLSearchParams2 } = require_webidl2js_wrapper();
|
|
64562
64562
|
var urlStateMachine = require_url_state_machine();
|
|
64563
64563
|
var percentEncoding = require_percent_encoding();
|
|
64564
64564
|
var sharedGlobalObject = { Array, Object, Promise, String, TypeError };
|
|
64565
64565
|
URL2.install(sharedGlobalObject, ["Window"]);
|
|
64566
|
-
|
|
64566
|
+
URLSearchParams2.install(sharedGlobalObject, ["Window"]);
|
|
64567
64567
|
exports.URL = sharedGlobalObject.URL;
|
|
64568
64568
|
exports.URLSearchParams = sharedGlobalObject.URLSearchParams;
|
|
64569
64569
|
exports.parseURL = urlStateMachine.parseURL;
|
|
@@ -77140,7 +77140,7 @@ var {
|
|
|
77140
77140
|
// package.json
|
|
77141
77141
|
var package_default = {
|
|
77142
77142
|
name: "@carllee1983/dbcli",
|
|
77143
|
-
version: "1.5.
|
|
77143
|
+
version: "1.5.2",
|
|
77144
77144
|
description: "Database CLI for AI agents",
|
|
77145
77145
|
type: "module",
|
|
77146
77146
|
publishConfig: {
|
|
@@ -78976,6 +78976,7 @@ class MySQLAdapter {
|
|
|
78976
78976
|
}
|
|
78977
78977
|
|
|
78978
78978
|
// src/adapters/mongodb-adapter.ts
|
|
78979
|
+
import { resolveSrv, resolveTxt } from "dns/promises";
|
|
78979
78980
|
class MongoDBAdapter {
|
|
78980
78981
|
options;
|
|
78981
78982
|
ClientClass;
|
|
@@ -79000,9 +79001,101 @@ class MongoDBAdapter {
|
|
|
79000
79001
|
}
|
|
79001
79002
|
return `mongodb://${host}:${port}/${database}`;
|
|
79002
79003
|
}
|
|
79004
|
+
parseTxtRecords(records) {
|
|
79005
|
+
const combined = records.flat().map((record) => record.replace(/^"|"$/g, "")).join("&");
|
|
79006
|
+
if (!combined)
|
|
79007
|
+
return {};
|
|
79008
|
+
const params = new URLSearchParams(combined);
|
|
79009
|
+
return Object.fromEntries(params.entries());
|
|
79010
|
+
}
|
|
79011
|
+
async resolveSrvHosts(hostname) {
|
|
79012
|
+
const srvName = `_mongodb._tcp.${hostname}`;
|
|
79013
|
+
try {
|
|
79014
|
+
const records = await resolveSrv(srvName);
|
|
79015
|
+
if (!records.length) {
|
|
79016
|
+
throw new Error(`No SRV records found for ${srvName}`);
|
|
79017
|
+
}
|
|
79018
|
+
return records.map((record) => {
|
|
79019
|
+
const target = String(record.name || record.target || "").replace(/\.$/, "");
|
|
79020
|
+
return `${target}:${record.port}`;
|
|
79021
|
+
});
|
|
79022
|
+
} catch (error) {
|
|
79023
|
+
const code = error?.code;
|
|
79024
|
+
if (code !== "ECONNREFUSED") {
|
|
79025
|
+
throw error;
|
|
79026
|
+
}
|
|
79027
|
+
}
|
|
79028
|
+
const response = await fetch(`https://dns.google/resolve?name=${encodeURIComponent(srvName)}&type=SRV`);
|
|
79029
|
+
if (!response.ok) {
|
|
79030
|
+
throw new Error(`SRV lookup failed with HTTP ${response.status}`);
|
|
79031
|
+
}
|
|
79032
|
+
const payload = await response.json();
|
|
79033
|
+
if (payload.Status !== 0 || !payload.Answer?.length) {
|
|
79034
|
+
throw new Error(payload.Comment || `No SRV records found for ${srvName}`);
|
|
79035
|
+
}
|
|
79036
|
+
return payload.Answer.map((answer) => {
|
|
79037
|
+
const parts = answer.data.trim().split(/\s+/);
|
|
79038
|
+
const port = parts[2];
|
|
79039
|
+
const target = parts.slice(3).join(" ").replace(/\.$/, "");
|
|
79040
|
+
return `${target}:${port}`;
|
|
79041
|
+
});
|
|
79042
|
+
}
|
|
79043
|
+
async resolveTxtOptions(hostname) {
|
|
79044
|
+
try {
|
|
79045
|
+
const records = await resolveTxt(hostname);
|
|
79046
|
+
return this.parseTxtRecords(records);
|
|
79047
|
+
} catch (error) {
|
|
79048
|
+
const code = error?.code;
|
|
79049
|
+
if (code !== "ECONNREFUSED") {
|
|
79050
|
+
throw error;
|
|
79051
|
+
}
|
|
79052
|
+
}
|
|
79053
|
+
const response = await fetch(`https://dns.google/resolve?name=${encodeURIComponent(hostname)}&type=TXT`);
|
|
79054
|
+
if (!response.ok) {
|
|
79055
|
+
throw new Error(`TXT lookup failed with HTTP ${response.status}`);
|
|
79056
|
+
}
|
|
79057
|
+
const payload = await response.json();
|
|
79058
|
+
if (payload.Status !== 0 || !payload.Answer?.length) {
|
|
79059
|
+
return {};
|
|
79060
|
+
}
|
|
79061
|
+
return this.parseTxtRecords(payload.Answer.map((answer) => [answer.data]));
|
|
79062
|
+
}
|
|
79063
|
+
async buildResolvedUri() {
|
|
79064
|
+
if (!this.options.uri) {
|
|
79065
|
+
return this.buildUri();
|
|
79066
|
+
}
|
|
79067
|
+
if (!this.options.uri.startsWith("mongodb+srv://")) {
|
|
79068
|
+
return this.options.uri;
|
|
79069
|
+
}
|
|
79070
|
+
const url = new URL(this.options.uri);
|
|
79071
|
+
const hosts = await this.resolveSrvHosts(url.hostname);
|
|
79072
|
+
const txtOptions = await this.resolveTxtOptions(url.hostname);
|
|
79073
|
+
const query = new URLSearchParams(url.searchParams);
|
|
79074
|
+
for (const [key2, value] of Object.entries(txtOptions)) {
|
|
79075
|
+
if (!query.has(key2)) {
|
|
79076
|
+
query.set(key2, value);
|
|
79077
|
+
}
|
|
79078
|
+
}
|
|
79079
|
+
if (!query.has("tls") && !query.has("ssl")) {
|
|
79080
|
+
query.set("tls", "true");
|
|
79081
|
+
}
|
|
79082
|
+
if ((url.username || url.password) && !query.has("authSource")) {
|
|
79083
|
+
query.set("authSource", "admin");
|
|
79084
|
+
}
|
|
79085
|
+
const userInfo = url.username || url.password ? `${encodeURIComponent(decodeURIComponent(url.username))}${url.password ? `:${encodeURIComponent(decodeURIComponent(url.password))}` : ""}@` : "";
|
|
79086
|
+
const path = url.pathname && url.pathname !== "/" ? url.pathname : `/${this.options.database || "test"}`;
|
|
79087
|
+
const search = query.toString();
|
|
79088
|
+
return `mongodb://${userInfo}${hosts.join(",")}${path}${search ? `?${search}` : ""}`;
|
|
79089
|
+
}
|
|
79090
|
+
getDatabase() {
|
|
79091
|
+
if (!this.client) {
|
|
79092
|
+
throw new ConnectionError("UNKNOWN", "\u5C1A\u672A\u9023\u7DDA\uFF0C\u8ACB\u5148\u547C\u53EB connect()", []);
|
|
79093
|
+
}
|
|
79094
|
+
return this.client.db(this.options.database || undefined);
|
|
79095
|
+
}
|
|
79003
79096
|
async connect() {
|
|
79004
79097
|
const ClientClass = await this.resolveClientClass();
|
|
79005
|
-
const uri = this.
|
|
79098
|
+
const uri = await this.buildResolvedUri();
|
|
79006
79099
|
try {
|
|
79007
79100
|
this.client = new ClientClass(uri, { serverSelectionTimeoutMS: this.options.timeout ?? 5000 });
|
|
79008
79101
|
await this.client.connect();
|
|
@@ -79022,11 +79115,8 @@ class MongoDBAdapter {
|
|
|
79022
79115
|
}
|
|
79023
79116
|
}
|
|
79024
79117
|
async execute(query, params) {
|
|
79025
|
-
|
|
79026
|
-
throw new ConnectionError("UNKNOWN", "\u5C1A\u672A\u9023\u7DDA\uFF0C\u8ACB\u5148\u547C\u53EB connect()", []);
|
|
79027
|
-
}
|
|
79118
|
+
const db = this.getDatabase();
|
|
79028
79119
|
const collectionName = params?.[0];
|
|
79029
|
-
const db = this.client.db();
|
|
79030
79120
|
const collection = db.collection(collectionName);
|
|
79031
79121
|
let parsed;
|
|
79032
79122
|
try {
|
|
@@ -79043,10 +79133,7 @@ class MongoDBAdapter {
|
|
|
79043
79133
|
return { rows: docs, affectedRows: docs.length };
|
|
79044
79134
|
}
|
|
79045
79135
|
async listCollections() {
|
|
79046
|
-
|
|
79047
|
-
throw new ConnectionError("UNKNOWN", "\u5C1A\u672A\u9023\u7DDA\uFF0C\u8ACB\u5148\u547C\u53EB connect()", []);
|
|
79048
|
-
}
|
|
79049
|
-
const db = this.client.db();
|
|
79136
|
+
const db = this.getDatabase();
|
|
79050
79137
|
const colls = await db.listCollections().toArray();
|
|
79051
79138
|
const results = await Promise.all(colls.map(async (col) => {
|
|
79052
79139
|
try {
|
|
@@ -79061,14 +79148,11 @@ class MongoDBAdapter {
|
|
|
79061
79148
|
async testConnection() {
|
|
79062
79149
|
if (!this.client)
|
|
79063
79150
|
return false;
|
|
79064
|
-
await this.
|
|
79151
|
+
await this.getDatabase().command({ ping: 1 });
|
|
79065
79152
|
return true;
|
|
79066
79153
|
}
|
|
79067
79154
|
async getServerVersion() {
|
|
79068
|
-
|
|
79069
|
-
throw new ConnectionError("UNKNOWN", "\u5C1A\u672A\u9023\u7DDA\uFF0C\u8ACB\u5148\u547C\u53EB connect()", []);
|
|
79070
|
-
}
|
|
79071
|
-
const info = await this.client.db().admin().serverInfo();
|
|
79155
|
+
const info = await this.getDatabase().admin().serverInfo();
|
|
79072
79156
|
return info.version ?? "unknown";
|
|
79073
79157
|
}
|
|
79074
79158
|
}
|
|
@@ -82687,6 +82771,7 @@ var statusCommand = new Command("status").description("Show current configuratio
|
|
|
82687
82771
|
init_validation();
|
|
82688
82772
|
init_schema_path();
|
|
82689
82773
|
import { join as join10 } from "path";
|
|
82774
|
+
import { resolveSrv as resolveSrv2 } from "dns/promises";
|
|
82690
82775
|
var ALLOWED_FORMATS7 = ["text", "json"];
|
|
82691
82776
|
var SENSITIVE_PATTERNS = [
|
|
82692
82777
|
"password",
|
|
@@ -82831,6 +82916,68 @@ var runDoctorChecks = {
|
|
|
82831
82916
|
message: versionResult.supported ? t_vars("version.doctor_pass", vars) : t_vars("version.doctor_warn", vars)
|
|
82832
82917
|
};
|
|
82833
82918
|
},
|
|
82919
|
+
async checkMongoSrvConnectivity(uri, deps = {}) {
|
|
82920
|
+
if (!uri || !uri.startsWith("mongodb+srv://")) {
|
|
82921
|
+
return null;
|
|
82922
|
+
}
|
|
82923
|
+
const url = new URL(uri);
|
|
82924
|
+
const srvName = `_mongodb._tcp.${url.hostname}`;
|
|
82925
|
+
const resolveSrvFn = deps.resolveSrvFn ?? resolveSrv2;
|
|
82926
|
+
const fetchFn = deps.fetchFn ?? fetch;
|
|
82927
|
+
try {
|
|
82928
|
+
const records = await resolveSrvFn(srvName);
|
|
82929
|
+
if (!records.length) {
|
|
82930
|
+
return {
|
|
82931
|
+
group: "Environment",
|
|
82932
|
+
label: "MongoDB SRV lookup",
|
|
82933
|
+
status: "error",
|
|
82934
|
+
message: `No SRV records found for ${url.hostname}`
|
|
82935
|
+
};
|
|
82936
|
+
}
|
|
82937
|
+
return {
|
|
82938
|
+
group: "Environment",
|
|
82939
|
+
label: "MongoDB SRV lookup",
|
|
82940
|
+
status: "pass",
|
|
82941
|
+
message: `MongoDB SRV lookup reachable for ${url.hostname} (${records.length} record(s))`
|
|
82942
|
+
};
|
|
82943
|
+
} catch (error) {
|
|
82944
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
82945
|
+
const code = error?.code;
|
|
82946
|
+
const isExecutionEnvIssue = code === "ECONNREFUSED" || code === "ETIMEDOUT" || message.includes("Unable to connect. Is the computer able to access the url?") || message.includes("ConnectionRefused");
|
|
82947
|
+
if (!isExecutionEnvIssue) {
|
|
82948
|
+
return {
|
|
82949
|
+
group: "Environment",
|
|
82950
|
+
label: "MongoDB SRV lookup",
|
|
82951
|
+
status: "error",
|
|
82952
|
+
message: `MongoDB SRV lookup failed for ${url.hostname}: ${message}`
|
|
82953
|
+
};
|
|
82954
|
+
}
|
|
82955
|
+
try {
|
|
82956
|
+
const response = await fetchFn(`https://dns.google/resolve?name=${encodeURIComponent(srvName)}&type=SRV`, { signal: AbortSignal.timeout(5000) });
|
|
82957
|
+
if (!response.ok) {
|
|
82958
|
+
throw new Error(`HTTP ${response.status}`);
|
|
82959
|
+
}
|
|
82960
|
+
const payload = await response.json();
|
|
82961
|
+
if (payload.Status !== 0 || !payload.Answer?.length) {
|
|
82962
|
+
throw new Error(payload.Comment || `No SRV records found for ${url.hostname}`);
|
|
82963
|
+
}
|
|
82964
|
+
return {
|
|
82965
|
+
group: "Environment",
|
|
82966
|
+
label: "MongoDB SRV lookup",
|
|
82967
|
+
status: "warn",
|
|
82968
|
+
message: `Direct SRV DNS lookup failed in this shell, but DNS-over-HTTPS fallback resolved ` + `${url.hostname}. dbcli can still connect here, but this runtime environment cannot perform direct SRV lookups.`
|
|
82969
|
+
};
|
|
82970
|
+
} catch (fallbackError) {
|
|
82971
|
+
const fallbackMessage = fallbackError instanceof Error ? fallbackError.message : String(fallbackError);
|
|
82972
|
+
return {
|
|
82973
|
+
group: "Environment",
|
|
82974
|
+
label: "MongoDB SRV lookup",
|
|
82975
|
+
status: "error",
|
|
82976
|
+
message: `MongoDB SRV lookup failed in this environment (${code || "unknown"}); ` + `DNS-over-HTTPS fallback also failed: ${fallbackMessage}`
|
|
82977
|
+
};
|
|
82978
|
+
}
|
|
82979
|
+
}
|
|
82980
|
+
},
|
|
82834
82981
|
checkLargeTables(tables) {
|
|
82835
82982
|
const large = tables.filter((t7) => (t7.estimatedRowCount ?? 0) > 1e6);
|
|
82836
82983
|
if (large.length === 0) {
|
|
@@ -82927,6 +83074,13 @@ var runDoctorChecks = {
|
|
|
82927
83074
|
};
|
|
82928
83075
|
async function collectMongoDoctorResults(config) {
|
|
82929
83076
|
const results = [];
|
|
83077
|
+
const srvCheck = await runDoctorChecks.checkMongoSrvConnectivity(config.connection?.uri);
|
|
83078
|
+
if (srvCheck) {
|
|
83079
|
+
results.push(srvCheck);
|
|
83080
|
+
if (srvCheck.status === "error") {
|
|
83081
|
+
return results;
|
|
83082
|
+
}
|
|
83083
|
+
}
|
|
82930
83084
|
const adapter = AdapterFactory.createMongoDBAdapter(config.connection);
|
|
82931
83085
|
await adapter.connect();
|
|
82932
83086
|
try {
|