mirror of
https://github.com/theodore-song/polymarket-analyst.git
synced 2026-08-24 04:58:08 +00:00
Expose safe shared-state diagnostics
This commit is contained in:
+24
@@ -27,6 +27,30 @@ export function databaseUrl() {
|
|||||||
return databaseUrls()[0] || "";
|
return databaseUrls()[0] || "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function databaseConnectionDiagnostics() {
|
||||||
|
const candidates = databaseUrls();
|
||||||
|
return {
|
||||||
|
candidates: candidates.length,
|
||||||
|
urls: candidates.map(value => {
|
||||||
|
try {
|
||||||
|
const parsed = new URL(value);
|
||||||
|
return {
|
||||||
|
valid_postgres_url: /^postgres(?:ql)?:$/i.test(parsed.protocol),
|
||||||
|
neon_host: /(?:^|\.)neon\.tech$/i.test(parsed.hostname),
|
||||||
|
pooled_host: /-pooler(?:\.|$)/i.test(parsed.hostname),
|
||||||
|
has_username: Boolean(parsed.username),
|
||||||
|
has_password: Boolean(parsed.password),
|
||||||
|
has_database_name: parsed.pathname.length > 1,
|
||||||
|
sslmode: parsed.searchParams.get("sslmode") || "missing",
|
||||||
|
channel_binding: parsed.searchParams.get("channel_binding") || "missing",
|
||||||
|
};
|
||||||
|
} catch {
|
||||||
|
return { valid_postgres_url: false };
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function hasDatabase() {
|
export function hasDatabase() {
|
||||||
return Boolean(databaseUrl());
|
return Boolean(databaseUrl());
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-3
@@ -1,5 +1,5 @@
|
|||||||
import { get, list, put } from "@vercel/blob";
|
import { get, list, put } from "@vercel/blob";
|
||||||
import { hasDatabase, readSharedAppState, writeSharedAppState } from "./_db.js";
|
import { databaseConnectionDiagnostics, hasDatabase, readSharedAppState, writeSharedAppState } from "./_db.js";
|
||||||
|
|
||||||
const STATE_PATH = process.env.PMA_STATE_PATH || "shared/state.json";
|
const STATE_PATH = process.env.PMA_STATE_PATH || "shared/state.json";
|
||||||
const STATE_VERSION_PREFIX = process.env.PMA_STATE_VERSION_PREFIX || "shared/state-versions/";
|
const STATE_VERSION_PREFIX = process.env.PMA_STATE_VERSION_PREFIX || "shared/state-versions/";
|
||||||
@@ -34,6 +34,16 @@ export function providerErrorCode(error) {
|
|||||||
return "unavailable";
|
return "unavailable";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function providerErrorMetadata(error) {
|
||||||
|
const name = String(error && error.name || "Error");
|
||||||
|
const code = String(error && (error.code || error.cause && error.cause.code) || "").toUpperCase();
|
||||||
|
return {
|
||||||
|
status: providerErrorCode(error),
|
||||||
|
error_class: /^[A-Za-z][A-Za-z0-9_]{0,39}$/.test(name) ? name : "Error",
|
||||||
|
error_code: /^[A-Z0-9_]{1,32}$/.test(code) ? code : "none",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
async function readJsonBlob() {
|
async function readJsonBlob() {
|
||||||
let databaseAvailable = false;
|
let databaseAvailable = false;
|
||||||
let databaseError = null;
|
let databaseError = null;
|
||||||
@@ -66,8 +76,10 @@ async function readJsonBlob() {
|
|||||||
if (primaryError && !databaseAvailable) {
|
if (primaryError && !databaseAvailable) {
|
||||||
const error = new Error("Shared state providers are unavailable");
|
const error = new Error("Shared state providers are unavailable");
|
||||||
error.providers = {
|
error.providers = {
|
||||||
database: hasDatabase() ? providerErrorCode(databaseError) : "not_configured",
|
database: hasDatabase()
|
||||||
blob: providerErrorCode(primaryError),
|
? { ...providerErrorMetadata(databaseError), ...databaseConnectionDiagnostics() }
|
||||||
|
: { status: "not_configured", candidates: 0, urls: [] },
|
||||||
|
blob: providerErrorMetadata(primaryError),
|
||||||
};
|
};
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
import { databaseUrl, databaseUrls, normalizeDatabaseUrl } from "../api/_db.js";
|
import { databaseConnectionDiagnostics, databaseUrl, databaseUrls, normalizeDatabaseUrl } from "../api/_db.js";
|
||||||
import { compactAgentState, compactSuggestion, providerErrorCode } from "../api/state.js";
|
import { compactAgentState, compactSuggestion, providerErrorCode } from "../api/state.js";
|
||||||
|
|
||||||
assert.equal(normalizeDatabaseUrl("psql 'postgresql://user:pass@example.test/db?sslmode=require'"),
|
assert.equal(normalizeDatabaseUrl("psql 'postgresql://user:pass@example.test/db?sslmode=require'"),
|
||||||
@@ -16,6 +16,12 @@ assert.equal(databaseUrl(), process.env.NEON_DATABASE_URL);
|
|||||||
assert.deepEqual(databaseUrls(), [process.env.NEON_DATABASE_URL]);
|
assert.deepEqual(databaseUrls(), [process.env.NEON_DATABASE_URL]);
|
||||||
process.env.DATABASE_URL = "postgresql://stale:pass@example.test/db";
|
process.env.DATABASE_URL = "postgresql://stale:pass@example.test/db";
|
||||||
assert.deepEqual(databaseUrls(), [process.env.DATABASE_URL, process.env.NEON_DATABASE_URL]);
|
assert.deepEqual(databaseUrls(), [process.env.DATABASE_URL, process.env.NEON_DATABASE_URL]);
|
||||||
|
const diagnostics = databaseConnectionDiagnostics();
|
||||||
|
assert.equal(diagnostics.candidates, 2);
|
||||||
|
assert.equal(diagnostics.urls[0].valid_postgres_url, true);
|
||||||
|
assert.equal(diagnostics.urls[0].has_username, true);
|
||||||
|
assert.equal(diagnostics.urls[0].has_password, true);
|
||||||
|
assert.equal(diagnostics.urls[0].has_database_name, true);
|
||||||
if (originalDatabaseUrl === undefined) delete process.env.DATABASE_URL;
|
if (originalDatabaseUrl === undefined) delete process.env.DATABASE_URL;
|
||||||
else process.env.DATABASE_URL = originalDatabaseUrl;
|
else process.env.DATABASE_URL = originalDatabaseUrl;
|
||||||
if (originalNeonUrl === undefined) delete process.env.NEON_DATABASE_URL;
|
if (originalNeonUrl === undefined) delete process.env.NEON_DATABASE_URL;
|
||||||
|
|||||||
Reference in New Issue
Block a user