This commit is contained in:
1 parent
c1541b80df
commit
b4c7908029
97 files changed
+12403
-154
No files matched your search
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
* 诊断 DATABASE_URL 与驱动/SSL 配置是否匹配。
|
||||
*
|
||||
* 覆盖两类常见问题:
|
||||
* 1. **驱动不匹配**:@neondatabase/serverless 的 neon-http 走 Neon 的 HTTP 代理,
|
||||
* 无法连接普通 Postgres(需改用 pg)。
|
||||
* 2. **SSL 不匹配**:本地 Postgres 默认不开 SSL,若连接串带 sslmode=require
|
||||
* 会报 "The server does not support SSL connections"。
|
||||
*
|
||||
* 用法:node scripts/db-diagnose.mjs
|
||||
*/
|
||||
|
||||
import { config } from "dotenv";
|
||||
import net from "node:net";
|
||||
import pg from "pg";
|
||||
|
||||
config({ path: ".env.local" });
|
||||
|
||||
const url = process.env.DATABASE_URL;
|
||||
if (!url) {
|
||||
console.error("缺少 DATABASE_URL");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let parsed;
|
||||
try {
|
||||
parsed = new URL(url);
|
||||
} catch {
|
||||
console.error(`DATABASE_URL 不是合法 URL: ${url}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const host = parsed.hostname;
|
||||
const port = Number(parsed.port || 5432);
|
||||
const database = parsed.pathname.replace(/^\//, "");
|
||||
const sslmode = parsed.searchParams.get("sslmode");
|
||||
const isNeonHost = /\.neon\.tech$/i.test(host);
|
||||
const isLocal = /^(localhost|127\.0\.0\.1|::1)$/.test(host);
|
||||
|
||||
console.log("── DATABASE_URL 解析 ─────────────────");
|
||||
console.log(` host: ${host}`);
|
||||
console.log(` port: ${port}`);
|
||||
console.log(` database: ${database}`);
|
||||
console.log(` sslmode: ${sslmode ?? "(未指定)"}`);
|
||||
console.log(` Neon 托管域名: ${isNeonHost ? "是" : "否"}`);
|
||||
console.log(` 本地地址: ${isLocal ? "是" : "否"}\n`);
|
||||
|
||||
/** 探测 TCP 端口是否可连接 */
|
||||
function probeTcp(hostname, tcpPort, timeoutMs = 3000) {
|
||||
return new Promise((resolve) => {
|
||||
const socket = net.createConnection({ host: hostname, port: tcpPort });
|
||||
const done = (result) => {
|
||||
socket.destroy();
|
||||
resolve(result);
|
||||
};
|
||||
|
||||
socket.setTimeout(timeoutMs);
|
||||
socket.once("connect", () => done(true));
|
||||
socket.once("timeout", () => done(false));
|
||||
socket.once("error", () => done(false));
|
||||
});
|
||||
}
|
||||
|
||||
const tcpReachable = await probeTcp(host, port);
|
||||
|
||||
console.log("── 网络连通性 ────────────────────────");
|
||||
console.log(` TCP ${host}:${port} 可达: ${tcpReachable ? "是" : "否"}\n`);
|
||||
|
||||
// 依次尝试「按 sslmode」与「不启用 SSL」,判断是否为 SSL 配置问题
|
||||
async function tryConnect(useSsl) {
|
||||
const client = new pg.Client({
|
||||
connectionString: url,
|
||||
ssl: useSsl ? { rejectUnauthorized: false } : undefined,
|
||||
connectionTimeoutMillis: 5000,
|
||||
});
|
||||
|
||||
try {
|
||||
await client.connect();
|
||||
await client.query("select 1");
|
||||
await client.end();
|
||||
return { ok: true };
|
||||
} catch (error) {
|
||||
await client.end().catch(() => {});
|
||||
return { ok: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
console.log("── 实际连接测试 ──────────────────────");
|
||||
const withConfiguredSsl = await tryConnect(
|
||||
sslmode === "disable" ? false : sslmode ? true : !isLocal,
|
||||
);
|
||||
console.log(
|
||||
` 按当前 sslmode 连接: ${withConfiguredSsl.ok ? "✓ 成功" : `✗ ${withConfiguredSsl.error}`}`,
|
||||
);
|
||||
|
||||
let oppositeResult = null;
|
||||
if (!withConfiguredSsl.ok) {
|
||||
oppositeResult = await tryConnect(!(sslmode === "disable"));
|
||||
console.log(
|
||||
` 改用相反 SSL 设置: ${oppositeResult.ok ? "✓ 成功" : `✗ ${oppositeResult.error}`}`,
|
||||
);
|
||||
}
|
||||
console.log("");
|
||||
|
||||
console.log("── 结论 ──────────────────────────────");
|
||||
if (withConfiguredSsl.ok) {
|
||||
console.log(" ✓ 配置正确,数据库可用。");
|
||||
} else if (oppositeResult?.ok) {
|
||||
console.log(" ✗ SSL 配置与服务器不匹配。");
|
||||
console.log(
|
||||
isLocal
|
||||
? " 本地 Postgres 默认不启用 SSL,请把连接串改为 ?sslmode=disable"
|
||||
: " 该服务器要求 SSL,请在连接串中加上 ?sslmode=require",
|
||||
);
|
||||
} else if (!tcpReachable) {
|
||||
console.log(" ✗ 端口不可达:请确认数据库已启动、主机与端口正确。");
|
||||
} else {
|
||||
console.log(" ✗ 连接失败,且与 SSL 无关,请检查用户名/密码/数据库名。");
|
||||
console.log(" → 若使用 neon-http 驱动连接普通 Postgres,需改用 pg 驱动。");
|
||||
}
|
||||
Reference in new issue
Block a user