This commit is contained in:
z.ai committed 2026-10-08 11:09:37 +08:00
1 parent c1541b80df
commit b4c7908029
97 files changed
+12403 -154

No files matched your search

+120
View File
@@ -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 驱动。");
}