@pgplex/pgconsole 0.0.3 → 0.1.0
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/client/assets/{index-DN0ojmgn.js → index-BP7svqLg.js} +11 -11
- package/dist/client/assets/index-BqZk-vnS.css +2 -0
- package/dist/client/assets/{sql-BWqTRAwa.js → sql-CiHxzEYZ.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/server.mjs +230 -94
- package/dist/server.mjs.map +2 -2
- package/package.json +1 -1
- package/dist/client/assets/index-BXV6P4Bi.css +0 -2
package/dist/server.mjs
CHANGED
|
@@ -41058,7 +41058,7 @@ async function loadConfig(configPath) {
|
|
|
41058
41058
|
const path5 = configPath || "pgconsole.toml";
|
|
41059
41059
|
if (!existsSync(path5)) {
|
|
41060
41060
|
throw new Error(`Config file not found: ${path5}
|
|
41061
|
-
See https://docs.pgconsole.com/configuration/
|
|
41061
|
+
See https://docs.pgconsole.com/configuration/config`);
|
|
41062
41062
|
}
|
|
41063
41063
|
const content = readFileSync(path5, "utf-8");
|
|
41064
41064
|
const parsed = parse2(content);
|
|
@@ -41271,57 +41271,36 @@ See https://docs.pgconsole.com/configuration/toml-config`);
|
|
|
41271
41271
|
if (a.jwt_secret.length < 32) {
|
|
41272
41272
|
throw new Error("Auth jwt_secret must be at least 32 characters");
|
|
41273
41273
|
}
|
|
41274
|
-
|
|
41274
|
+
const providers = [];
|
|
41275
41275
|
const rawProviders = a.providers;
|
|
41276
|
-
|
|
41277
|
-
|
|
41278
|
-
for (const
|
|
41279
|
-
const raw =
|
|
41280
|
-
if (
|
|
41281
|
-
|
|
41282
|
-
|
|
41283
|
-
|
|
41284
|
-
|
|
41285
|
-
|
|
41286
|
-
|
|
41287
|
-
|
|
41288
|
-
|
|
41289
|
-
|
|
41290
|
-
};
|
|
41291
|
-
}
|
|
41292
|
-
|
|
41293
|
-
|
|
41294
|
-
|
|
41295
|
-
|
|
41296
|
-
|
|
41297
|
-
|
|
41298
|
-
|
|
41299
|
-
|
|
41300
|
-
|
|
41301
|
-
providers.keycloak = {
|
|
41302
|
-
issuer_url: raw.issuer_url.replace(/\/+$/, ""),
|
|
41303
|
-
client_id: raw.client_id,
|
|
41304
|
-
client_secret: raw.client_secret
|
|
41305
|
-
};
|
|
41306
|
-
} else if (key === "okta") {
|
|
41307
|
-
if (!raw.issuer_url || typeof raw.issuer_url !== "string") {
|
|
41308
|
-
throw new Error("Okta provider missing required field: issuer_url");
|
|
41309
|
-
}
|
|
41310
|
-
if (!raw.client_id || typeof raw.client_id !== "string") {
|
|
41311
|
-
throw new Error("Okta provider missing required field: client_id");
|
|
41312
|
-
}
|
|
41313
|
-
if (!raw.client_secret || typeof raw.client_secret !== "string") {
|
|
41314
|
-
throw new Error("Okta provider missing required field: client_secret");
|
|
41315
|
-
}
|
|
41316
|
-
providers.okta = {
|
|
41317
|
-
issuer_url: raw.issuer_url.replace(/\/+$/, ""),
|
|
41318
|
-
client_id: raw.client_id,
|
|
41319
|
-
client_secret: raw.client_secret
|
|
41320
|
-
};
|
|
41321
|
-
}
|
|
41276
|
+
const validProviderTypes = ["google", "keycloak", "okta"];
|
|
41277
|
+
if (rawProviders && Array.isArray(rawProviders)) {
|
|
41278
|
+
for (const entry of rawProviders) {
|
|
41279
|
+
const raw = entry;
|
|
41280
|
+
if (!raw.type || typeof raw.type !== "string") {
|
|
41281
|
+
throw new Error("Auth provider missing required field: type");
|
|
41282
|
+
}
|
|
41283
|
+
if (!validProviderTypes.includes(raw.type)) {
|
|
41284
|
+
throw new Error(`Auth provider has invalid type: ${raw.type}. Must be one of: ${validProviderTypes.join(", ")}`);
|
|
41285
|
+
}
|
|
41286
|
+
if (!raw.client_id || typeof raw.client_id !== "string") {
|
|
41287
|
+
throw new Error(`${raw.type} provider missing required field: client_id`);
|
|
41288
|
+
}
|
|
41289
|
+
if (!raw.client_secret || typeof raw.client_secret !== "string") {
|
|
41290
|
+
throw new Error(`${raw.type} provider missing required field: client_secret`);
|
|
41291
|
+
}
|
|
41292
|
+
if ((raw.type === "keycloak" || raw.type === "okta") && (!raw.issuer_url || typeof raw.issuer_url !== "string")) {
|
|
41293
|
+
throw new Error(`${raw.type} provider missing required field: issuer_url`);
|
|
41294
|
+
}
|
|
41295
|
+
providers.push({
|
|
41296
|
+
type: raw.type,
|
|
41297
|
+
client_id: raw.client_id,
|
|
41298
|
+
client_secret: raw.client_secret,
|
|
41299
|
+
issuer_url: typeof raw.issuer_url === "string" ? raw.issuer_url.replace(/\/+$/, "") : void 0
|
|
41300
|
+
});
|
|
41322
41301
|
}
|
|
41323
41302
|
}
|
|
41324
|
-
const hasOAuthProvider = providers
|
|
41303
|
+
const hasOAuthProvider = providers.length > 0;
|
|
41325
41304
|
if (hasOAuthProvider && !external_url) {
|
|
41326
41305
|
throw new Error("[general] external_url is required when OAuth providers are enabled");
|
|
41327
41306
|
}
|
|
@@ -41375,7 +41354,7 @@ See https://docs.pgconsole.com/configuration/toml-config`);
|
|
|
41375
41354
|
}
|
|
41376
41355
|
}
|
|
41377
41356
|
const iam = [];
|
|
41378
|
-
const validPermissions = ["read", "write", "ddl", "admin"];
|
|
41357
|
+
const validPermissions = ["read", "write", "ddl", "admin", "explain", "execute", "export"];
|
|
41379
41358
|
const rawIAM = parsed.iam || [];
|
|
41380
41359
|
for (let i2 = 0; i2 < rawIAM.length; i2++) {
|
|
41381
41360
|
const rule = rawIAM[i2];
|
|
@@ -41395,10 +41374,13 @@ See https://docs.pgconsole.com/configuration/toml-config`);
|
|
|
41395
41374
|
if (typeof perm !== "string") {
|
|
41396
41375
|
throw new Error(`IAM rule ${ruleNum} has invalid permission: must be string`);
|
|
41397
41376
|
}
|
|
41398
|
-
if (
|
|
41399
|
-
|
|
41377
|
+
if (perm === "*") {
|
|
41378
|
+
permissions.push(...validPermissions);
|
|
41379
|
+
} else if (!validPermissions.includes(perm)) {
|
|
41380
|
+
throw new Error(`IAM rule ${ruleNum} has invalid permission: ${perm}. Must be one of: ${validPermissions.join(", ")}, *`);
|
|
41381
|
+
} else {
|
|
41382
|
+
permissions.push(perm);
|
|
41400
41383
|
}
|
|
41401
|
-
permissions.push(perm);
|
|
41402
41384
|
}
|
|
41403
41385
|
if (!Array.isArray(rule.members) || rule.members.length === 0) {
|
|
41404
41386
|
throw new Error(`IAM rule ${ruleNum} missing required field: members (must be non-empty array)`);
|
|
@@ -41409,7 +41391,7 @@ See https://docs.pgconsole.com/configuration/toml-config`);
|
|
|
41409
41391
|
throw new Error(`IAM rule ${ruleNum} has invalid member: must be non-empty string`);
|
|
41410
41392
|
}
|
|
41411
41393
|
const m2 = member.trim();
|
|
41412
|
-
if (m2 === "
|
|
41394
|
+
if (m2 === "*") {
|
|
41413
41395
|
members.push(m2);
|
|
41414
41396
|
} else if (m2.startsWith("user:")) {
|
|
41415
41397
|
const email = m2.slice(5);
|
|
@@ -41427,7 +41409,7 @@ See https://docs.pgconsole.com/configuration/toml-config`);
|
|
|
41427
41409
|
}
|
|
41428
41410
|
members.push(m2);
|
|
41429
41411
|
} else {
|
|
41430
|
-
throw new Error(`IAM rule ${ruleNum} has invalid member format: ${m2}. Must be "
|
|
41412
|
+
throw new Error(`IAM rule ${ruleNum} has invalid member format: ${m2}. Must be "*", "user:xxx", or "group:xxx"`);
|
|
41431
41413
|
}
|
|
41432
41414
|
}
|
|
41433
41415
|
iam.push({ connection, permissions, members });
|
|
@@ -41452,13 +41434,6 @@ See https://docs.pgconsole.com/configuration/toml-config`);
|
|
|
41452
41434
|
throw new Error(`Too many [[users]] entries: ${users.length} configured but current license only allows ${limit2}. Remove users or upgrade your license.`);
|
|
41453
41435
|
}
|
|
41454
41436
|
}
|
|
41455
|
-
console.log(`Loaded ${users.length} user(s), ${groups.length} group(s), ${labels.length} label(s) and ${connections.length} connection(s) from ${path5}`);
|
|
41456
|
-
if (ai) {
|
|
41457
|
-
console.log(`AI configured with ${ai.providers.length} provider(s)`);
|
|
41458
|
-
}
|
|
41459
|
-
if (iam.length > 0) {
|
|
41460
|
-
console.log(`IAM configured with ${iam.length} rule(s)`);
|
|
41461
|
-
}
|
|
41462
41437
|
}
|
|
41463
41438
|
function getLabels() {
|
|
41464
41439
|
return loadedConfig.labels;
|
|
@@ -41476,6 +41451,9 @@ function getConnectionById(id) {
|
|
|
41476
41451
|
function getAuthConfig() {
|
|
41477
41452
|
return loadedConfig.auth;
|
|
41478
41453
|
}
|
|
41454
|
+
function getAuthProvider(type) {
|
|
41455
|
+
return loadedConfig.auth?.providers.find((p) => p.type === type);
|
|
41456
|
+
}
|
|
41479
41457
|
function getUsers() {
|
|
41480
41458
|
return loadedConfig.users;
|
|
41481
41459
|
}
|
|
@@ -41638,11 +41616,24 @@ function auditSQL(actor, connection, database, sql, success, duration_ms, row_co
|
|
|
41638
41616
|
if (error) event.error = error;
|
|
41639
41617
|
emit(event);
|
|
41640
41618
|
}
|
|
41619
|
+
function auditExport(actor, connection, database, sql, row_count, format) {
|
|
41620
|
+
emit({
|
|
41621
|
+
type: "audit",
|
|
41622
|
+
ts: now(),
|
|
41623
|
+
action: "data.export",
|
|
41624
|
+
actor,
|
|
41625
|
+
connection,
|
|
41626
|
+
database,
|
|
41627
|
+
sql,
|
|
41628
|
+
row_count,
|
|
41629
|
+
format
|
|
41630
|
+
});
|
|
41631
|
+
}
|
|
41641
41632
|
|
|
41642
41633
|
// server/lib/oauth/google.ts
|
|
41643
41634
|
function registerGoogleOAuth(router3, opts) {
|
|
41644
41635
|
router3.get("/google", (_req, res) => {
|
|
41645
|
-
const google =
|
|
41636
|
+
const google = getAuthProvider("google");
|
|
41646
41637
|
if (!google) {
|
|
41647
41638
|
return res.status(400).json({ error: "Google OAuth not configured" });
|
|
41648
41639
|
}
|
|
@@ -41659,7 +41650,7 @@ function registerGoogleOAuth(router3, opts) {
|
|
|
41659
41650
|
return res.redirect(`https://accounts.google.com/o/oauth2/v2/auth?${params}`);
|
|
41660
41651
|
});
|
|
41661
41652
|
router3.get("/google/callback", async (req, res) => {
|
|
41662
|
-
const google =
|
|
41653
|
+
const google = getAuthProvider("google");
|
|
41663
41654
|
const externalUrl = getExternalUrl();
|
|
41664
41655
|
if (!google) {
|
|
41665
41656
|
return res.redirect(`${externalUrl}/signin?error=not_configured`);
|
|
@@ -41729,7 +41720,7 @@ function registerGoogleOAuth(router3, opts) {
|
|
|
41729
41720
|
// server/lib/oauth/keycloak.ts
|
|
41730
41721
|
function registerKeycloakOAuth(router3, opts) {
|
|
41731
41722
|
router3.get("/keycloak", (_req, res) => {
|
|
41732
|
-
const keycloak =
|
|
41723
|
+
const keycloak = getAuthProvider("keycloak");
|
|
41733
41724
|
if (!keycloak) {
|
|
41734
41725
|
return res.status(400).json({ error: "Keycloak not configured" });
|
|
41735
41726
|
}
|
|
@@ -41746,7 +41737,7 @@ function registerKeycloakOAuth(router3, opts) {
|
|
|
41746
41737
|
return res.redirect(`${keycloak.issuer_url}/protocol/openid-connect/auth?${params}`);
|
|
41747
41738
|
});
|
|
41748
41739
|
router3.get("/keycloak/callback", async (req, res) => {
|
|
41749
|
-
const keycloak =
|
|
41740
|
+
const keycloak = getAuthProvider("keycloak");
|
|
41750
41741
|
const externalUrl = getExternalUrl();
|
|
41751
41742
|
if (!keycloak) {
|
|
41752
41743
|
return res.redirect(`${externalUrl}/signin?error=not_configured`);
|
|
@@ -41813,7 +41804,7 @@ function registerKeycloakOAuth(router3, opts) {
|
|
|
41813
41804
|
// server/lib/oauth/okta.ts
|
|
41814
41805
|
function registerOktaOAuth(router3, opts) {
|
|
41815
41806
|
router3.get("/okta", (_req, res) => {
|
|
41816
|
-
const okta =
|
|
41807
|
+
const okta = getAuthProvider("okta");
|
|
41817
41808
|
if (!okta) {
|
|
41818
41809
|
return res.status(400).json({ error: "Okta not configured" });
|
|
41819
41810
|
}
|
|
@@ -41830,7 +41821,7 @@ function registerOktaOAuth(router3, opts) {
|
|
|
41830
41821
|
return res.redirect(`${okta.issuer_url}/v1/authorize?${params}`);
|
|
41831
41822
|
});
|
|
41832
41823
|
router3.get("/okta/callback", async (req, res) => {
|
|
41833
|
-
const okta =
|
|
41824
|
+
const okta = getAuthProvider("okta");
|
|
41834
41825
|
const externalUrl = getExternalUrl();
|
|
41835
41826
|
if (!okta) {
|
|
41836
41827
|
return res.redirect(`${externalUrl}/signin?error=not_configured`);
|
|
@@ -42018,14 +42009,12 @@ router.get("/providers", (_req, res) => {
|
|
|
42018
42009
|
const plan = getPlan();
|
|
42019
42010
|
const providers = [];
|
|
42020
42011
|
if (getUsers().some((u) => u.password)) providers.push({ name: "basic" });
|
|
42021
|
-
|
|
42022
|
-
|
|
42023
|
-
|
|
42024
|
-
|
|
42025
|
-
|
|
42026
|
-
|
|
42027
|
-
providers.push({ name: key });
|
|
42028
|
-
}
|
|
42012
|
+
for (const provider of config?.providers ?? []) {
|
|
42013
|
+
const feat = SSO_FEATURE[provider.type];
|
|
42014
|
+
if (feat && !feature(feat, plan)) {
|
|
42015
|
+
providers.push({ name: provider.type, requiredPlan: requiredPlan(feat) });
|
|
42016
|
+
} else {
|
|
42017
|
+
providers.push({ name: provider.type });
|
|
42029
42018
|
}
|
|
42030
42019
|
}
|
|
42031
42020
|
return res.json({ providers });
|
|
@@ -56349,6 +56338,93 @@ var TerminateSessionResponse = class _TerminateSessionResponse extends Message {
|
|
|
56349
56338
|
return proto3.util.equals(_TerminateSessionResponse, a, b);
|
|
56350
56339
|
}
|
|
56351
56340
|
};
|
|
56341
|
+
var AuditExportRequest = class _AuditExportRequest extends Message {
|
|
56342
|
+
/**
|
|
56343
|
+
* @generated from field: string connection_id = 1;
|
|
56344
|
+
*/
|
|
56345
|
+
connectionId = "";
|
|
56346
|
+
/**
|
|
56347
|
+
* @generated from field: string sql = 2;
|
|
56348
|
+
*/
|
|
56349
|
+
sql = "";
|
|
56350
|
+
/**
|
|
56351
|
+
* @generated from field: int32 row_count = 3;
|
|
56352
|
+
*/
|
|
56353
|
+
rowCount = 0;
|
|
56354
|
+
/**
|
|
56355
|
+
* @generated from field: string format = 4;
|
|
56356
|
+
*/
|
|
56357
|
+
format = "";
|
|
56358
|
+
constructor(data) {
|
|
56359
|
+
super();
|
|
56360
|
+
proto3.util.initPartial(data, this);
|
|
56361
|
+
}
|
|
56362
|
+
static runtime = proto3;
|
|
56363
|
+
static typeName = "query.v1.AuditExportRequest";
|
|
56364
|
+
static fields = proto3.util.newFieldList(() => [
|
|
56365
|
+
{
|
|
56366
|
+
no: 1,
|
|
56367
|
+
name: "connection_id",
|
|
56368
|
+
kind: "scalar",
|
|
56369
|
+
T: 9
|
|
56370
|
+
/* ScalarType.STRING */
|
|
56371
|
+
},
|
|
56372
|
+
{
|
|
56373
|
+
no: 2,
|
|
56374
|
+
name: "sql",
|
|
56375
|
+
kind: "scalar",
|
|
56376
|
+
T: 9
|
|
56377
|
+
/* ScalarType.STRING */
|
|
56378
|
+
},
|
|
56379
|
+
{
|
|
56380
|
+
no: 3,
|
|
56381
|
+
name: "row_count",
|
|
56382
|
+
kind: "scalar",
|
|
56383
|
+
T: 5
|
|
56384
|
+
/* ScalarType.INT32 */
|
|
56385
|
+
},
|
|
56386
|
+
{
|
|
56387
|
+
no: 4,
|
|
56388
|
+
name: "format",
|
|
56389
|
+
kind: "scalar",
|
|
56390
|
+
T: 9
|
|
56391
|
+
/* ScalarType.STRING */
|
|
56392
|
+
}
|
|
56393
|
+
]);
|
|
56394
|
+
static fromBinary(bytes, options) {
|
|
56395
|
+
return new _AuditExportRequest().fromBinary(bytes, options);
|
|
56396
|
+
}
|
|
56397
|
+
static fromJson(jsonValue, options) {
|
|
56398
|
+
return new _AuditExportRequest().fromJson(jsonValue, options);
|
|
56399
|
+
}
|
|
56400
|
+
static fromJsonString(jsonString, options) {
|
|
56401
|
+
return new _AuditExportRequest().fromJsonString(jsonString, options);
|
|
56402
|
+
}
|
|
56403
|
+
static equals(a, b) {
|
|
56404
|
+
return proto3.util.equals(_AuditExportRequest, a, b);
|
|
56405
|
+
}
|
|
56406
|
+
};
|
|
56407
|
+
var AuditExportResponse = class _AuditExportResponse extends Message {
|
|
56408
|
+
constructor(data) {
|
|
56409
|
+
super();
|
|
56410
|
+
proto3.util.initPartial(data, this);
|
|
56411
|
+
}
|
|
56412
|
+
static runtime = proto3;
|
|
56413
|
+
static typeName = "query.v1.AuditExportResponse";
|
|
56414
|
+
static fields = proto3.util.newFieldList(() => []);
|
|
56415
|
+
static fromBinary(bytes, options) {
|
|
56416
|
+
return new _AuditExportResponse().fromBinary(bytes, options);
|
|
56417
|
+
}
|
|
56418
|
+
static fromJson(jsonValue, options) {
|
|
56419
|
+
return new _AuditExportResponse().fromJson(jsonValue, options);
|
|
56420
|
+
}
|
|
56421
|
+
static fromJsonString(jsonString, options) {
|
|
56422
|
+
return new _AuditExportResponse().fromJsonString(jsonString, options);
|
|
56423
|
+
}
|
|
56424
|
+
static equals(a, b) {
|
|
56425
|
+
return proto3.util.equals(_AuditExportResponse, a, b);
|
|
56426
|
+
}
|
|
56427
|
+
};
|
|
56352
56428
|
|
|
56353
56429
|
// src/gen/query_connect.ts
|
|
56354
56430
|
var QueryService = {
|
|
@@ -56515,6 +56591,15 @@ var QueryService = {
|
|
|
56515
56591
|
I: TerminateSessionRequest,
|
|
56516
56592
|
O: TerminateSessionResponse,
|
|
56517
56593
|
kind: MethodKind.Unary
|
|
56594
|
+
},
|
|
56595
|
+
/**
|
|
56596
|
+
* @generated from rpc query.v1.QueryService.AuditExport
|
|
56597
|
+
*/
|
|
56598
|
+
auditExport: {
|
|
56599
|
+
name: "AuditExport",
|
|
56600
|
+
I: AuditExportRequest,
|
|
56601
|
+
O: AuditExportResponse,
|
|
56602
|
+
kind: MethodKind.Unary
|
|
56518
56603
|
}
|
|
56519
56604
|
}
|
|
56520
56605
|
};
|
|
@@ -57488,16 +57573,21 @@ function setConnectionVersion(connectionId, version) {
|
|
|
57488
57573
|
const info = { version };
|
|
57489
57574
|
connectionCache.set(connectionId, info);
|
|
57490
57575
|
}
|
|
57491
|
-
function
|
|
57576
|
+
async function testAndCacheConnection(client, connectionId) {
|
|
57577
|
+
await client`SELECT 1`;
|
|
57578
|
+
const versionResult = await client`SELECT version()`;
|
|
57579
|
+
const versionString = versionResult[0]?.version || "";
|
|
57492
57580
|
const match = versionString.match(/PostgreSQL (\d+)/);
|
|
57493
57581
|
if (!match?.[1]) {
|
|
57494
57582
|
throw new Error(`Failed to extract PostgreSQL version from: ${versionString}`);
|
|
57495
57583
|
}
|
|
57496
|
-
|
|
57584
|
+
const version = match[1];
|
|
57585
|
+
setConnectionVersion(connectionId, version);
|
|
57586
|
+
return version;
|
|
57497
57587
|
}
|
|
57498
57588
|
|
|
57499
57589
|
// server/lib/iam.ts
|
|
57500
|
-
var ALL_PERMISSIONS = ["read", "write", "ddl", "admin"];
|
|
57590
|
+
var ALL_PERMISSIONS = ["read", "write", "ddl", "admin", "explain", "execute", "export"];
|
|
57501
57591
|
function requirePermission(user, connectionId, permission, action) {
|
|
57502
57592
|
if (!user) {
|
|
57503
57593
|
throw new ConnectError("Authentication required", Code.Unauthenticated);
|
|
@@ -57542,7 +57632,7 @@ function getUserPermissions(email, connectionId) {
|
|
|
57542
57632
|
continue;
|
|
57543
57633
|
}
|
|
57544
57634
|
const matches = rule.members.some((member) => {
|
|
57545
|
-
if (member === "
|
|
57635
|
+
if (member === "*") {
|
|
57546
57636
|
return true;
|
|
57547
57637
|
}
|
|
57548
57638
|
if (member.startsWith("user:")) {
|
|
@@ -57644,11 +57734,7 @@ var connectionServiceHandlers = {
|
|
|
57644
57734
|
sslMode: conn.ssl_mode || "prefer"
|
|
57645
57735
|
}, user?.email);
|
|
57646
57736
|
try {
|
|
57647
|
-
await client
|
|
57648
|
-
const versionResult = await client`SELECT version()`;
|
|
57649
|
-
const versionString = versionResult[0]?.version || "";
|
|
57650
|
-
const version = extractPostgresVersion(versionString);
|
|
57651
|
-
setConnectionVersion(req.id, version);
|
|
57737
|
+
await testAndCacheConnection(client, req.id);
|
|
57652
57738
|
const latencyMs = Date.now() - start2;
|
|
57653
57739
|
return { success: true, error: "", latencyMs };
|
|
57654
57740
|
} catch (err) {
|
|
@@ -57757,6 +57843,7 @@ function transformStatement(node, source) {
|
|
|
57757
57843
|
if ("AlterPublicationStmt" in obj) return { kind: "alter_publication", source };
|
|
57758
57844
|
if ("ReassignOwnedStmt" in obj) return { kind: "reassign_owned", source };
|
|
57759
57845
|
if ("DropOwnedStmt" in obj) return { kind: "drop_owned", source };
|
|
57846
|
+
if ("CallStmt" in obj) return { kind: "call", source };
|
|
57760
57847
|
return { kind: "unknown", raw: node, source };
|
|
57761
57848
|
}
|
|
57762
57849
|
function transformSelect(raw) {
|
|
@@ -62078,9 +62165,14 @@ function getRequiredPermission(kind) {
|
|
|
62078
62165
|
switch (kind) {
|
|
62079
62166
|
// Read-only
|
|
62080
62167
|
case "select":
|
|
62081
|
-
case "explain":
|
|
62082
62168
|
case "show":
|
|
62083
62169
|
return "read";
|
|
62170
|
+
// Explain
|
|
62171
|
+
case "explain":
|
|
62172
|
+
return "explain";
|
|
62173
|
+
// Execute (stored procedures)
|
|
62174
|
+
case "call":
|
|
62175
|
+
return "execute";
|
|
62084
62176
|
// DML (data modification)
|
|
62085
62177
|
case "insert":
|
|
62086
62178
|
case "update":
|
|
@@ -62105,7 +62197,7 @@ function getRequiredPermission(kind) {
|
|
|
62105
62197
|
case "revoke":
|
|
62106
62198
|
case "refresh_matview":
|
|
62107
62199
|
return "ddl";
|
|
62108
|
-
// COPY
|
|
62200
|
+
// COPY (data import/export via server filesystem)
|
|
62109
62201
|
case "copy":
|
|
62110
62202
|
return "write";
|
|
62111
62203
|
// Session/transaction control - safe operations
|
|
@@ -63195,6 +63287,21 @@ var queryServiceHandlers = {
|
|
|
63195
63287
|
error: err instanceof Error ? err.message : "Failed to terminate session"
|
|
63196
63288
|
};
|
|
63197
63289
|
}
|
|
63290
|
+
},
|
|
63291
|
+
async auditExport(req, context) {
|
|
63292
|
+
if (!req.connectionId) {
|
|
63293
|
+
throw new ConnectError("connection_id is required", Code.InvalidArgument);
|
|
63294
|
+
}
|
|
63295
|
+
const user = await getUserFromContext(context.values);
|
|
63296
|
+
if (!user) {
|
|
63297
|
+
throw new ConnectError("Authentication required", Code.Unauthenticated);
|
|
63298
|
+
}
|
|
63299
|
+
const conn = getConnectionById(req.connectionId);
|
|
63300
|
+
if (!conn) {
|
|
63301
|
+
throw new ConnectError("Connection not found", Code.NotFound);
|
|
63302
|
+
}
|
|
63303
|
+
auditExport(user.email, req.connectionId, conn.database, req.sql, req.rowCount, req.format);
|
|
63304
|
+
return {};
|
|
63198
63305
|
}
|
|
63199
63306
|
};
|
|
63200
63307
|
|
|
@@ -92448,11 +92555,7 @@ async function testAllConnections() {
|
|
|
92448
92555
|
sslMode: conn.ssl_mode || "prefer"
|
|
92449
92556
|
});
|
|
92450
92557
|
try {
|
|
92451
|
-
await client
|
|
92452
|
-
const versionResult = await client`SELECT version()`;
|
|
92453
|
-
const versionString = versionResult[0]?.version || "";
|
|
92454
|
-
const version = extractPostgresVersion(versionString);
|
|
92455
|
-
setConnectionVersion(conn.id, version);
|
|
92558
|
+
const version = await testAndCacheConnection(client, conn.id);
|
|
92456
92559
|
console.log(` \u2713 ${conn.name} (PostgreSQL ${version})`);
|
|
92457
92560
|
} finally {
|
|
92458
92561
|
await client.end();
|
|
@@ -92527,8 +92630,10 @@ app.use((req, res, next) => {
|
|
|
92527
92630
|
async function start() {
|
|
92528
92631
|
const args = parseArgs();
|
|
92529
92632
|
const port = args.port || process.env.PORT || 9876;
|
|
92633
|
+
const configPath = args.config || "pgconsole.toml";
|
|
92530
92634
|
try {
|
|
92531
|
-
await loadConfig(
|
|
92635
|
+
await loadConfig(configPath);
|
|
92636
|
+
console.log(`\u2713 Loaded config from: ${path4.resolve(configPath)}`);
|
|
92532
92637
|
} catch (error) {
|
|
92533
92638
|
console.error("Failed to load config:", error instanceof Error ? error.message : error);
|
|
92534
92639
|
process.exit(1);
|
|
@@ -92546,9 +92651,40 @@ async function start() {
|
|
|
92546
92651
|
console.error("\nConnection test failed:", error instanceof Error ? error.message : error);
|
|
92547
92652
|
process.exit(1);
|
|
92548
92653
|
}
|
|
92549
|
-
app.listen(port, () => {
|
|
92654
|
+
const server = app.listen(port, () => {
|
|
92655
|
+
console.log(`
|
|
92656
|
+
___
|
|
92657
|
+
/\\_ \\
|
|
92658
|
+
_____ __ ___ ___ ___ ____ ___\\//\\ \\ __
|
|
92659
|
+
/\\ '__\`\\ /'_ \`\\ /'___\\ / __\`\\ /' _ \`\\ /',__\\ / __\`\\\\ \\ \\ /'__\`\\
|
|
92660
|
+
\\ \\ \\L\\ \\/\\ \\L\\ \\/\\ \\__//\\ \\L\\ \\/\\ \\/\\ \\/\\__, \`\\/\\ \\L\\ \\\\_\\ \\_/\\ __/
|
|
92661
|
+
\\ \\ ,__/\\ \\____ \\ \\____\\ \\____/\\ \\_\\ \\_\\/\\____/\\ \\____//\\____\\ \\____\\
|
|
92662
|
+
\\ \\ \\/ \\/___L\\ \\/____/\\/___/ \\/_/\\/_/\\/___/ \\/___/ \\/____/\\/____/
|
|
92663
|
+
\\ \\_\\ /\\____/
|
|
92664
|
+
\\/_/ \\_/__/
|
|
92665
|
+
|
|
92666
|
+
Version ${"0.1.0"}
|
|
92667
|
+
`);
|
|
92550
92668
|
console.log(`Server running on http://localhost:${port}`);
|
|
92669
|
+
const browserUrl = false ? `http://localhost:5173` : getExternalUrl() || `http://localhost:${port}`;
|
|
92670
|
+
console.log(`Open in browser: ${browserUrl}`);
|
|
92551
92671
|
});
|
|
92672
|
+
server.on("error", (err) => {
|
|
92673
|
+
if (err.code === "EADDRINUSE") {
|
|
92674
|
+
console.error(`Error: Port ${port} is already in use.`);
|
|
92675
|
+
} else {
|
|
92676
|
+
console.error(`Error starting server: ${err.message}`);
|
|
92677
|
+
}
|
|
92678
|
+
process.exit(1);
|
|
92679
|
+
});
|
|
92680
|
+
const shutdown = () => {
|
|
92681
|
+
console.log("\nShutting down...");
|
|
92682
|
+
server.close(() => {
|
|
92683
|
+
process.exit(0);
|
|
92684
|
+
});
|
|
92685
|
+
};
|
|
92686
|
+
process.on("SIGTERM", shutdown);
|
|
92687
|
+
process.on("SIGINT", shutdown);
|
|
92552
92688
|
}
|
|
92553
92689
|
start();
|
|
92554
92690
|
/*! Bundled license information:
|