patchwork-os 0.2.0-beta.6.canary.21 → 0.2.0-beta.6.canary.23
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/connectorRoutes.js +384 -0
- package/dist/connectorRoutes.js.map +1 -1
- package/dist/connectors/airtable.d.ts +108 -0
- package/dist/connectors/airtable.js +396 -0
- package/dist/connectors/airtable.js.map +1 -0
- package/dist/connectors/connectorRegistry.js +63 -0
- package/dist/connectors/connectorRegistry.js.map +1 -1
- package/dist/connectors/elasticsearch.d.ts +141 -0
- package/dist/connectors/elasticsearch.js +601 -0
- package/dist/connectors/elasticsearch.js.map +1 -0
- package/dist/connectors/figma.d.ts +130 -0
- package/dist/connectors/figma.js +387 -0
- package/dist/connectors/figma.js.map +1 -0
- package/dist/connectors/gmail.js +9 -0
- package/dist/connectors/gmail.js.map +1 -1
- package/dist/connectors/googleDrive.d.ts +12 -0
- package/dist/connectors/googleDrive.js +27 -0
- package/dist/connectors/googleDrive.js.map +1 -1
- package/dist/connectors/mongodb.d.ts +139 -0
- package/dist/connectors/mongodb.js +455 -0
- package/dist/connectors/mongodb.js.map +1 -0
- package/dist/connectors/postgres.d.ts +127 -0
- package/dist/connectors/postgres.js +512 -0
- package/dist/connectors/postgres.js.map +1 -0
- package/dist/connectors/redis.d.ts +140 -0
- package/dist/connectors/redis.js +571 -0
- package/dist/connectors/redis.js.map +1 -0
- package/dist/connectors/sendgrid.d.ts +102 -0
- package/dist/connectors/sendgrid.js +423 -0
- package/dist/connectors/sendgrid.js.map +1 -0
- package/dist/connectors/twilio.d.ts +118 -0
- package/dist/connectors/twilio.js +475 -0
- package/dist/connectors/twilio.js.map +1 -0
- package/dist/connectors/webflow.d.ts +118 -0
- package/dist/connectors/webflow.js +393 -0
- package/dist/connectors/webflow.js.map +1 -0
- package/dist/recipes/tools/gmail.js +27 -5
- package/dist/recipes/tools/gmail.js.map +1 -1
- package/dist/recipes/tools/googleDrive.js +64 -0
- package/dist/recipes/tools/googleDrive.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Postgres connector — read-only SQL access via the `pg` driver.
|
|
3
|
+
*
|
|
4
|
+
* Auth: connection string OR discrete host/port/database/user/password.
|
|
5
|
+
* - Stored: getSecretJsonSync("postgres") → PostgresTokens
|
|
6
|
+
*
|
|
7
|
+
* Tools: listTables, describeTable, query (SELECT-only), explain (SELECT-only)
|
|
8
|
+
*
|
|
9
|
+
* Driver: `pg` is loaded lazily via dynamic import. It is NOT a project
|
|
10
|
+
* dependency — operators must `npm install pg` in their workspace before
|
|
11
|
+
* using this connector.
|
|
12
|
+
*
|
|
13
|
+
* Extends BaseConnector for unified auth, retry, rate-limit, error handling.
|
|
14
|
+
*
|
|
15
|
+
* This file is the canonical template for the other data-store connectors
|
|
16
|
+
* (MongoDB, Redis, Elasticsearch). Keep the shape consistent.
|
|
17
|
+
*/
|
|
18
|
+
import { type AuthContext, BaseConnector, type ConnectorError, type ConnectorStatus } from "./baseConnector.js";
|
|
19
|
+
export interface PostgresTokens {
|
|
20
|
+
/** Full DSN: postgres://user:pass@host:port/db */
|
|
21
|
+
connectionString?: string;
|
|
22
|
+
host?: string;
|
|
23
|
+
port?: number;
|
|
24
|
+
database?: string;
|
|
25
|
+
user?: string;
|
|
26
|
+
password?: string;
|
|
27
|
+
ssl?: boolean;
|
|
28
|
+
connected_at: string;
|
|
29
|
+
}
|
|
30
|
+
export interface PostgresTable {
|
|
31
|
+
table_schema: string;
|
|
32
|
+
table_name: string;
|
|
33
|
+
}
|
|
34
|
+
export interface PostgresColumn {
|
|
35
|
+
column_name: string;
|
|
36
|
+
data_type: string;
|
|
37
|
+
is_nullable: string;
|
|
38
|
+
column_default: string | null;
|
|
39
|
+
}
|
|
40
|
+
export interface PostgresQueryResult {
|
|
41
|
+
rows: Record<string, unknown>[];
|
|
42
|
+
rowCount: number;
|
|
43
|
+
fields: {
|
|
44
|
+
name: string;
|
|
45
|
+
dataTypeID: number;
|
|
46
|
+
}[];
|
|
47
|
+
truncated: boolean;
|
|
48
|
+
}
|
|
49
|
+
type PgPoolClient = {
|
|
50
|
+
query: (text: string, values?: unknown[]) => Promise<PgQueryResultRaw>;
|
|
51
|
+
release: () => void;
|
|
52
|
+
};
|
|
53
|
+
type PgQueryResultRaw = {
|
|
54
|
+
rows: Record<string, unknown>[];
|
|
55
|
+
rowCount: number | null;
|
|
56
|
+
fields?: {
|
|
57
|
+
name: string;
|
|
58
|
+
dataTypeID: number;
|
|
59
|
+
}[];
|
|
60
|
+
};
|
|
61
|
+
type PgPool = {
|
|
62
|
+
query: (text: string, values?: unknown[]) => Promise<PgQueryResultRaw>;
|
|
63
|
+
connect: () => Promise<PgPoolClient>;
|
|
64
|
+
end: () => Promise<void>;
|
|
65
|
+
};
|
|
66
|
+
type PgModule = {
|
|
67
|
+
Pool: new (config: Record<string, unknown>) => PgPool;
|
|
68
|
+
};
|
|
69
|
+
export declare function __setPgModuleForTest(mod: PgModule | null): void;
|
|
70
|
+
/**
|
|
71
|
+
* True iff `sql` is a read-only statement (SELECT / SHOW / EXPLAIN / WITH).
|
|
72
|
+
* Strips leading comments + whitespace and inspects the first keyword.
|
|
73
|
+
* Defence in depth: pair with role-level read-only grants on the DB side.
|
|
74
|
+
*/
|
|
75
|
+
export declare function isReadOnlySql(sql: string): boolean;
|
|
76
|
+
/**
|
|
77
|
+
* Wrap a SELECT-style statement with `LIMIT n` if it does not already cap
|
|
78
|
+
* rows. Cheap heuristic: case-insensitive "limit" word search. The runtime
|
|
79
|
+
* row-count cap in `query()` is the real safety net.
|
|
80
|
+
*/
|
|
81
|
+
export declare function applyRowLimit(sql: string, limit: number): string;
|
|
82
|
+
export declare class PostgresConnector extends BaseConnector {
|
|
83
|
+
readonly providerName = "postgres";
|
|
84
|
+
private tokens;
|
|
85
|
+
private pool;
|
|
86
|
+
protected getOAuthConfig(): null;
|
|
87
|
+
authenticate(): Promise<AuthContext>;
|
|
88
|
+
healthCheck(): Promise<{
|
|
89
|
+
ok: boolean;
|
|
90
|
+
error?: ConnectorError;
|
|
91
|
+
}>;
|
|
92
|
+
normalizeError(error: unknown): ConnectorError;
|
|
93
|
+
getStatus(): ConnectorStatus;
|
|
94
|
+
private getPool;
|
|
95
|
+
/** Close the pool. Called by the HTTP disconnect handler. */
|
|
96
|
+
disconnect(): Promise<void>;
|
|
97
|
+
listTables(schema?: string): Promise<PostgresTable[]>;
|
|
98
|
+
describeTable(table: string, schema?: string): Promise<PostgresColumn[]>;
|
|
99
|
+
query(sql: string, params?: unknown[], rowLimit?: number): Promise<PostgresQueryResult>;
|
|
100
|
+
explain(sql: string): Promise<unknown>;
|
|
101
|
+
}
|
|
102
|
+
export declare function loadTokens(): PostgresTokens | null;
|
|
103
|
+
export declare function saveTokens(tokens: PostgresTokens): void;
|
|
104
|
+
export declare function clearTokens(): void;
|
|
105
|
+
export declare function getPostgresConnector(): PostgresConnector;
|
|
106
|
+
export { getPostgresConnector as postgres };
|
|
107
|
+
export interface ConnectorHandlerResult {
|
|
108
|
+
status: number;
|
|
109
|
+
body: string;
|
|
110
|
+
contentType?: string;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* POST /connections/postgres/connect
|
|
114
|
+
* body: { connectionString } OR { host, port?, database, user, password, ssl? }
|
|
115
|
+
*
|
|
116
|
+
* Validates by opening a connection, running `SELECT 1`, then closing.
|
|
117
|
+
* Only stores tokens after a successful round-trip.
|
|
118
|
+
*/
|
|
119
|
+
export declare function handlePostgresConnect(body: string): Promise<ConnectorHandlerResult>;
|
|
120
|
+
/**
|
|
121
|
+
* POST /connections/postgres/test
|
|
122
|
+
*/
|
|
123
|
+
export declare function handlePostgresTest(): Promise<ConnectorHandlerResult>;
|
|
124
|
+
/**
|
|
125
|
+
* DELETE /connections/postgres
|
|
126
|
+
*/
|
|
127
|
+
export declare function handlePostgresDisconnect(): Promise<ConnectorHandlerResult>;
|
|
@@ -0,0 +1,512 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Postgres connector — read-only SQL access via the `pg` driver.
|
|
3
|
+
*
|
|
4
|
+
* Auth: connection string OR discrete host/port/database/user/password.
|
|
5
|
+
* - Stored: getSecretJsonSync("postgres") → PostgresTokens
|
|
6
|
+
*
|
|
7
|
+
* Tools: listTables, describeTable, query (SELECT-only), explain (SELECT-only)
|
|
8
|
+
*
|
|
9
|
+
* Driver: `pg` is loaded lazily via dynamic import. It is NOT a project
|
|
10
|
+
* dependency — operators must `npm install pg` in their workspace before
|
|
11
|
+
* using this connector.
|
|
12
|
+
*
|
|
13
|
+
* Extends BaseConnector for unified auth, retry, rate-limit, error handling.
|
|
14
|
+
*
|
|
15
|
+
* This file is the canonical template for the other data-store connectors
|
|
16
|
+
* (MongoDB, Redis, Elasticsearch). Keep the shape consistent.
|
|
17
|
+
*/
|
|
18
|
+
import { BaseConnector, } from "./baseConnector.js";
|
|
19
|
+
import { deleteSecretJsonSync, getSecretJsonSync, storeSecretJsonSync, } from "./tokenStorage.js";
|
|
20
|
+
let _pgModulePromise = null;
|
|
21
|
+
async function loadPgDriver() {
|
|
22
|
+
if (!_pgModulePromise) {
|
|
23
|
+
// @ts-expect-error — `pg` is an optional peer dependency, not in package.json
|
|
24
|
+
_pgModulePromise = import("pg").then((m) => m.default ??
|
|
25
|
+
m, () => null);
|
|
26
|
+
}
|
|
27
|
+
const mod = await _pgModulePromise;
|
|
28
|
+
if (!mod) {
|
|
29
|
+
throw new Error("Postgres driver not installed. Run: npm install pg");
|
|
30
|
+
}
|
|
31
|
+
return mod;
|
|
32
|
+
}
|
|
33
|
+
// Test seam — lets the test file inject a fake pg module without touching disk.
|
|
34
|
+
export function __setPgModuleForTest(mod) {
|
|
35
|
+
_pgModulePromise = mod ? Promise.resolve(mod) : null;
|
|
36
|
+
}
|
|
37
|
+
// ── SQL safety guard ────────────────────────────────────────────────────────
|
|
38
|
+
const READ_ONLY_KEYWORDS = new Set(["select", "show", "explain", "with"]);
|
|
39
|
+
/**
|
|
40
|
+
* True iff `sql` is a read-only statement (SELECT / SHOW / EXPLAIN / WITH).
|
|
41
|
+
* Strips leading comments + whitespace and inspects the first keyword.
|
|
42
|
+
* Defence in depth: pair with role-level read-only grants on the DB side.
|
|
43
|
+
*/
|
|
44
|
+
export function isReadOnlySql(sql) {
|
|
45
|
+
if (typeof sql !== "string")
|
|
46
|
+
return false;
|
|
47
|
+
let s = sql.trim();
|
|
48
|
+
// Strip leading -- line comments and /* */ block comments
|
|
49
|
+
while (s.length > 0) {
|
|
50
|
+
if (s.startsWith("--")) {
|
|
51
|
+
const nl = s.indexOf("\n");
|
|
52
|
+
s = nl === -1 ? "" : s.slice(nl + 1).trim();
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (s.startsWith("/*")) {
|
|
56
|
+
const end = s.indexOf("*/");
|
|
57
|
+
s = end === -1 ? "" : s.slice(end + 2).trim();
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
const match = s.match(/^([a-zA-Z]+)/);
|
|
63
|
+
if (!match)
|
|
64
|
+
return false;
|
|
65
|
+
const kw = match[1].toLowerCase();
|
|
66
|
+
if (!READ_ONLY_KEYWORDS.has(kw))
|
|
67
|
+
return false;
|
|
68
|
+
// Reject semicolon-chained statements (cheap heuristic — rejects trailing
|
|
69
|
+
// ";" too, accept that). Allows a single trailing semicolon.
|
|
70
|
+
const stripped = s.replace(/;\s*$/, "");
|
|
71
|
+
if (stripped.includes(";"))
|
|
72
|
+
return false;
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Wrap a SELECT-style statement with `LIMIT n` if it does not already cap
|
|
77
|
+
* rows. Cheap heuristic: case-insensitive "limit" word search. The runtime
|
|
78
|
+
* row-count cap in `query()` is the real safety net.
|
|
79
|
+
*/
|
|
80
|
+
export function applyRowLimit(sql, limit) {
|
|
81
|
+
const trimmed = sql.replace(/;\s*$/, "").trim();
|
|
82
|
+
if (/\blimit\s+\d+/i.test(trimmed))
|
|
83
|
+
return trimmed;
|
|
84
|
+
return `${trimmed} LIMIT ${limit}`;
|
|
85
|
+
}
|
|
86
|
+
// ── Connector ───────────────────────────────────────────────────────────────
|
|
87
|
+
export class PostgresConnector extends BaseConnector {
|
|
88
|
+
providerName = "postgres";
|
|
89
|
+
tokens = null;
|
|
90
|
+
pool = null;
|
|
91
|
+
getOAuthConfig() {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
async authenticate() {
|
|
95
|
+
const tokens = loadTokens();
|
|
96
|
+
if (!tokens) {
|
|
97
|
+
throw new Error("Postgres not connected. Run: patchwork-os connect postgres");
|
|
98
|
+
}
|
|
99
|
+
this.tokens = tokens;
|
|
100
|
+
return {
|
|
101
|
+
token: tokens.connectionString ?? "postgres",
|
|
102
|
+
scopes: ["read"],
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
async healthCheck() {
|
|
106
|
+
try {
|
|
107
|
+
const pool = await this.getPool();
|
|
108
|
+
await pool.query("SELECT 1");
|
|
109
|
+
return { ok: true };
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
return { ok: false, error: this.normalizeError(err) };
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
normalizeError(error) {
|
|
116
|
+
const code = error && typeof error === "object" && "code" in error
|
|
117
|
+
? String(error.code)
|
|
118
|
+
: "";
|
|
119
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
120
|
+
if (code === "28P01" || code === "28000") {
|
|
121
|
+
return {
|
|
122
|
+
code: "auth_expired",
|
|
123
|
+
message: `Postgres authentication failed: ${message}`,
|
|
124
|
+
retryable: false,
|
|
125
|
+
suggestedAction: "patchwork-os connect postgres",
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
if (code === "3D000") {
|
|
129
|
+
return {
|
|
130
|
+
code: "not_found",
|
|
131
|
+
message: `Postgres database does not exist: ${message}`,
|
|
132
|
+
retryable: false,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
if (code === "08006" ||
|
|
136
|
+
code === "08001" ||
|
|
137
|
+
code === "08000" ||
|
|
138
|
+
code === "08003" ||
|
|
139
|
+
code === "08004") {
|
|
140
|
+
return {
|
|
141
|
+
code: "network_error",
|
|
142
|
+
message: `Postgres connection error: ${message}`,
|
|
143
|
+
retryable: true,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
if (code === "42501") {
|
|
147
|
+
return {
|
|
148
|
+
code: "permission_denied",
|
|
149
|
+
message: `Postgres permission denied: ${message}`,
|
|
150
|
+
retryable: false,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
if (code === "42P01") {
|
|
154
|
+
return {
|
|
155
|
+
code: "not_found",
|
|
156
|
+
message: `Postgres table not found: ${message}`,
|
|
157
|
+
retryable: false,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
if (error instanceof Error &&
|
|
161
|
+
(message.includes("ENOTFOUND") ||
|
|
162
|
+
message.includes("ECONNREFUSED") ||
|
|
163
|
+
message.includes("ETIMEDOUT"))) {
|
|
164
|
+
return {
|
|
165
|
+
code: "network_error",
|
|
166
|
+
message: `Cannot connect to Postgres: ${message}`,
|
|
167
|
+
retryable: true,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
code: "provider_error",
|
|
172
|
+
message,
|
|
173
|
+
retryable: false,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
getStatus() {
|
|
177
|
+
const tokens = loadTokens();
|
|
178
|
+
return {
|
|
179
|
+
id: "postgres",
|
|
180
|
+
status: tokens ? "connected" : "disconnected",
|
|
181
|
+
lastSync: tokens?.connected_at,
|
|
182
|
+
workspace: tokens?.database && tokens?.host
|
|
183
|
+
? `Postgres ${tokens.database}@${tokens.host}`
|
|
184
|
+
: tokens?.database
|
|
185
|
+
? `Postgres ${tokens.database}`
|
|
186
|
+
: undefined,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
// ── Pool management ───────────────────────────────────────────────────────
|
|
190
|
+
async getPool() {
|
|
191
|
+
if (this.pool)
|
|
192
|
+
return this.pool;
|
|
193
|
+
if (!this.tokens) {
|
|
194
|
+
const loaded = loadTokens();
|
|
195
|
+
if (!loaded) {
|
|
196
|
+
throw new Error("Postgres not connected");
|
|
197
|
+
}
|
|
198
|
+
this.tokens = loaded;
|
|
199
|
+
}
|
|
200
|
+
const pg = await loadPgDriver();
|
|
201
|
+
this.pool = new pg.Pool(buildPgConfig(this.tokens));
|
|
202
|
+
return this.pool;
|
|
203
|
+
}
|
|
204
|
+
/** Close the pool. Called by the HTTP disconnect handler. */
|
|
205
|
+
async disconnect() {
|
|
206
|
+
if (this.pool) {
|
|
207
|
+
try {
|
|
208
|
+
await this.pool.end();
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
// ignore — pool may already be closed
|
|
212
|
+
}
|
|
213
|
+
this.pool = null;
|
|
214
|
+
}
|
|
215
|
+
this.tokens = null;
|
|
216
|
+
}
|
|
217
|
+
// ── API Methods ───────────────────────────────────────────────────────────
|
|
218
|
+
async listTables(schema) {
|
|
219
|
+
const result = await this.apiCall(async () => {
|
|
220
|
+
const pool = await this.getPool();
|
|
221
|
+
const params = [];
|
|
222
|
+
let sql = "SELECT table_schema, table_name FROM information_schema.tables WHERE table_schema NOT IN ('pg_catalog','information_schema')";
|
|
223
|
+
if (schema) {
|
|
224
|
+
params.push(schema);
|
|
225
|
+
sql += " AND table_schema = $1";
|
|
226
|
+
}
|
|
227
|
+
sql += " ORDER BY table_schema, table_name";
|
|
228
|
+
const r = await pool.query(sql, params);
|
|
229
|
+
return r.rows;
|
|
230
|
+
});
|
|
231
|
+
if ("error" in result)
|
|
232
|
+
throw new Error(result.error.message);
|
|
233
|
+
return result.data;
|
|
234
|
+
}
|
|
235
|
+
async describeTable(table, schema = "public") {
|
|
236
|
+
const result = await this.apiCall(async () => {
|
|
237
|
+
const pool = await this.getPool();
|
|
238
|
+
const r = await pool.query("SELECT column_name, data_type, is_nullable, column_default FROM information_schema.columns WHERE table_schema = $1 AND table_name = $2 ORDER BY ordinal_position", [schema, table]);
|
|
239
|
+
return r.rows;
|
|
240
|
+
});
|
|
241
|
+
if ("error" in result)
|
|
242
|
+
throw new Error(result.error.message);
|
|
243
|
+
return result.data;
|
|
244
|
+
}
|
|
245
|
+
async query(sql, params = [], rowLimit = 100) {
|
|
246
|
+
if (!isReadOnlySql(sql)) {
|
|
247
|
+
throw new Error("Only read-only statements (SELECT / SHOW / EXPLAIN / WITH) are permitted");
|
|
248
|
+
}
|
|
249
|
+
const cap = Math.max(1, Math.min(rowLimit, 10_000));
|
|
250
|
+
const bounded = applyRowLimit(sql, cap);
|
|
251
|
+
const result = await this.apiCall(async () => {
|
|
252
|
+
const pool = await this.getPool();
|
|
253
|
+
const r = await pool.query(bounded, params);
|
|
254
|
+
const rows = r.rows ?? [];
|
|
255
|
+
const truncated = rows.length >= cap;
|
|
256
|
+
return {
|
|
257
|
+
rows: rows.slice(0, cap),
|
|
258
|
+
rowCount: r.rowCount ?? rows.length,
|
|
259
|
+
fields: r.fields ?? [],
|
|
260
|
+
truncated,
|
|
261
|
+
};
|
|
262
|
+
});
|
|
263
|
+
if ("error" in result)
|
|
264
|
+
throw new Error(result.error.message);
|
|
265
|
+
return result.data;
|
|
266
|
+
}
|
|
267
|
+
async explain(sql) {
|
|
268
|
+
if (!isReadOnlySql(sql)) {
|
|
269
|
+
throw new Error("Only read-only statements (SELECT / SHOW / EXPLAIN / WITH) are permitted");
|
|
270
|
+
}
|
|
271
|
+
const stripped = sql.replace(/;\s*$/, "").trim();
|
|
272
|
+
const wrapped = `EXPLAIN (FORMAT JSON) ${stripped}`;
|
|
273
|
+
const result = await this.apiCall(async () => {
|
|
274
|
+
const pool = await this.getPool();
|
|
275
|
+
const r = await pool.query(wrapped);
|
|
276
|
+
// pg returns a single-row, single-column JSON array
|
|
277
|
+
const first = r.rows[0];
|
|
278
|
+
if (!first)
|
|
279
|
+
return null;
|
|
280
|
+
const key = Object.keys(first)[0];
|
|
281
|
+
return key ? first[key] : first;
|
|
282
|
+
});
|
|
283
|
+
if ("error" in result)
|
|
284
|
+
throw new Error(result.error.message);
|
|
285
|
+
return result.data;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
// ── Config builder ──────────────────────────────────────────────────────────
|
|
289
|
+
function buildPgConfig(tokens) {
|
|
290
|
+
const config = {
|
|
291
|
+
// Single-client pool — datasource workloads are short-lived; keep
|
|
292
|
+
// connections cheap.
|
|
293
|
+
max: 4,
|
|
294
|
+
idleTimeoutMillis: 30_000,
|
|
295
|
+
connectionTimeoutMillis: 10_000,
|
|
296
|
+
};
|
|
297
|
+
if (tokens.connectionString) {
|
|
298
|
+
config.connectionString = tokens.connectionString;
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
if (tokens.host)
|
|
302
|
+
config.host = tokens.host;
|
|
303
|
+
if (tokens.port)
|
|
304
|
+
config.port = tokens.port;
|
|
305
|
+
if (tokens.database)
|
|
306
|
+
config.database = tokens.database;
|
|
307
|
+
if (tokens.user)
|
|
308
|
+
config.user = tokens.user;
|
|
309
|
+
if (tokens.password)
|
|
310
|
+
config.password = tokens.password;
|
|
311
|
+
}
|
|
312
|
+
if (tokens.ssl)
|
|
313
|
+
config.ssl = { rejectUnauthorized: false };
|
|
314
|
+
return config;
|
|
315
|
+
}
|
|
316
|
+
// ── Token persistence ───────────────────────────────────────────────────────
|
|
317
|
+
export function loadTokens() {
|
|
318
|
+
return getSecretJsonSync("postgres");
|
|
319
|
+
}
|
|
320
|
+
export function saveTokens(tokens) {
|
|
321
|
+
storeSecretJsonSync("postgres", tokens);
|
|
322
|
+
}
|
|
323
|
+
export function clearTokens() {
|
|
324
|
+
try {
|
|
325
|
+
deleteSecretJsonSync("postgres");
|
|
326
|
+
}
|
|
327
|
+
catch {
|
|
328
|
+
// ignore
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
// ── Singleton instance ──────────────────────────────────────────────────────
|
|
332
|
+
let _instance = null;
|
|
333
|
+
function resetPostgresConnector() {
|
|
334
|
+
if (_instance) {
|
|
335
|
+
// Best-effort pool teardown — don't block.
|
|
336
|
+
void _instance.disconnect();
|
|
337
|
+
}
|
|
338
|
+
_instance = null;
|
|
339
|
+
}
|
|
340
|
+
export function getPostgresConnector() {
|
|
341
|
+
if (!_instance) {
|
|
342
|
+
_instance = new PostgresConnector();
|
|
343
|
+
}
|
|
344
|
+
return _instance;
|
|
345
|
+
}
|
|
346
|
+
export { getPostgresConnector as postgres };
|
|
347
|
+
/**
|
|
348
|
+
* POST /connections/postgres/connect
|
|
349
|
+
* body: { connectionString } OR { host, port?, database, user, password, ssl? }
|
|
350
|
+
*
|
|
351
|
+
* Validates by opening a connection, running `SELECT 1`, then closing.
|
|
352
|
+
* Only stores tokens after a successful round-trip.
|
|
353
|
+
*/
|
|
354
|
+
export async function handlePostgresConnect(body) {
|
|
355
|
+
let parsed;
|
|
356
|
+
try {
|
|
357
|
+
parsed = JSON.parse(body);
|
|
358
|
+
}
|
|
359
|
+
catch {
|
|
360
|
+
return {
|
|
361
|
+
status: 400,
|
|
362
|
+
contentType: "application/json",
|
|
363
|
+
body: JSON.stringify({ ok: false, error: "Invalid JSON body" }),
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
const candidate = { connected_at: new Date().toISOString() };
|
|
367
|
+
if (typeof parsed.connectionString === "string" && parsed.connectionString) {
|
|
368
|
+
candidate.connectionString = parsed.connectionString;
|
|
369
|
+
}
|
|
370
|
+
else {
|
|
371
|
+
if (typeof parsed.host !== "string" || !parsed.host) {
|
|
372
|
+
return {
|
|
373
|
+
status: 400,
|
|
374
|
+
contentType: "application/json",
|
|
375
|
+
body: JSON.stringify({
|
|
376
|
+
ok: false,
|
|
377
|
+
error: "Provide connectionString OR host+database+user+password",
|
|
378
|
+
}),
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
if (typeof parsed.database !== "string" || !parsed.database) {
|
|
382
|
+
return {
|
|
383
|
+
status: 400,
|
|
384
|
+
contentType: "application/json",
|
|
385
|
+
body: JSON.stringify({ ok: false, error: "database is required" }),
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
if (typeof parsed.user !== "string" || !parsed.user) {
|
|
389
|
+
return {
|
|
390
|
+
status: 400,
|
|
391
|
+
contentType: "application/json",
|
|
392
|
+
body: JSON.stringify({ ok: false, error: "user is required" }),
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
if (typeof parsed.password !== "string") {
|
|
396
|
+
return {
|
|
397
|
+
status: 400,
|
|
398
|
+
contentType: "application/json",
|
|
399
|
+
body: JSON.stringify({ ok: false, error: "password is required" }),
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
candidate.host = parsed.host;
|
|
403
|
+
candidate.database = parsed.database;
|
|
404
|
+
candidate.user = parsed.user;
|
|
405
|
+
candidate.password = parsed.password;
|
|
406
|
+
if (typeof parsed.port === "number")
|
|
407
|
+
candidate.port = parsed.port;
|
|
408
|
+
if (typeof parsed.ssl === "boolean")
|
|
409
|
+
candidate.ssl = parsed.ssl;
|
|
410
|
+
}
|
|
411
|
+
// Validate by opening a pool + SELECT 1.
|
|
412
|
+
let pg;
|
|
413
|
+
try {
|
|
414
|
+
pg = await loadPgDriver();
|
|
415
|
+
}
|
|
416
|
+
catch (err) {
|
|
417
|
+
return {
|
|
418
|
+
status: 500,
|
|
419
|
+
contentType: "application/json",
|
|
420
|
+
body: JSON.stringify({
|
|
421
|
+
ok: false,
|
|
422
|
+
error: err instanceof Error ? err.message : String(err),
|
|
423
|
+
}),
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
const pool = new pg.Pool(buildPgConfig(candidate));
|
|
427
|
+
try {
|
|
428
|
+
await pool.query("SELECT 1");
|
|
429
|
+
}
|
|
430
|
+
catch (err) {
|
|
431
|
+
try {
|
|
432
|
+
await pool.end();
|
|
433
|
+
}
|
|
434
|
+
catch {
|
|
435
|
+
/* ignore */
|
|
436
|
+
}
|
|
437
|
+
const norm = getPostgresConnector().normalizeError(err);
|
|
438
|
+
return {
|
|
439
|
+
status: norm.code === "auth_expired" || norm.code === "permission_denied"
|
|
440
|
+
? 401
|
|
441
|
+
: 400,
|
|
442
|
+
contentType: "application/json",
|
|
443
|
+
body: JSON.stringify({ ok: false, error: norm.message }),
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
try {
|
|
447
|
+
await pool.end();
|
|
448
|
+
}
|
|
449
|
+
catch {
|
|
450
|
+
/* ignore */
|
|
451
|
+
}
|
|
452
|
+
saveTokens(candidate);
|
|
453
|
+
resetPostgresConnector();
|
|
454
|
+
return {
|
|
455
|
+
status: 200,
|
|
456
|
+
contentType: "application/json",
|
|
457
|
+
body: JSON.stringify({
|
|
458
|
+
ok: true,
|
|
459
|
+
database: candidate.database,
|
|
460
|
+
host: candidate.host,
|
|
461
|
+
connectedAt: candidate.connected_at,
|
|
462
|
+
}),
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* POST /connections/postgres/test
|
|
467
|
+
*/
|
|
468
|
+
export async function handlePostgresTest() {
|
|
469
|
+
const tokens = loadTokens();
|
|
470
|
+
if (!tokens) {
|
|
471
|
+
return {
|
|
472
|
+
status: 400,
|
|
473
|
+
contentType: "application/json",
|
|
474
|
+
body: JSON.stringify({ ok: false, error: "Postgres not connected" }),
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
try {
|
|
478
|
+
const connector = getPostgresConnector();
|
|
479
|
+
const check = await connector.healthCheck();
|
|
480
|
+
return {
|
|
481
|
+
status: check.ok ? 200 : 401,
|
|
482
|
+
contentType: "application/json",
|
|
483
|
+
body: JSON.stringify(check.ok ? { ok: true } : { ok: false, error: check.error?.message }),
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
catch (err) {
|
|
487
|
+
return {
|
|
488
|
+
status: 500,
|
|
489
|
+
contentType: "application/json",
|
|
490
|
+
body: JSON.stringify({
|
|
491
|
+
ok: false,
|
|
492
|
+
error: err instanceof Error ? err.message : String(err),
|
|
493
|
+
}),
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* DELETE /connections/postgres
|
|
499
|
+
*/
|
|
500
|
+
export async function handlePostgresDisconnect() {
|
|
501
|
+
if (_instance) {
|
|
502
|
+
await _instance.disconnect();
|
|
503
|
+
}
|
|
504
|
+
clearTokens();
|
|
505
|
+
resetPostgresConnector();
|
|
506
|
+
return {
|
|
507
|
+
status: 200,
|
|
508
|
+
contentType: "application/json",
|
|
509
|
+
body: JSON.stringify({ ok: true }),
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
//# sourceMappingURL=postgres.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgres.js","sourceRoot":"","sources":["../../src/connectors/postgres.ts"],"names":[],"mappings":"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"}
|