@pgplex/pgconsole 0.0.4 → 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-D1j7ALWF.js → index-BP7svqLg.js} +6 -6
- package/dist/client/assets/index-BqZk-vnS.css +2 -0
- package/dist/client/assets/{sql-qjXzJq_H.js → sql-CiHxzEYZ.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/server.mjs +182 -80
- package/dist/server.mjs.map +2 -2
- package/package.json +1 -1
- package/dist/client/assets/index-DgePIECN.css +0 -2
package/dist/server.mjs
CHANGED
|
@@ -41271,57 +41271,36 @@ See https://docs.pgconsole.com/configuration/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
|
}
|
|
@@ -41395,10 +41374,10 @@ See https://docs.pgconsole.com/configuration/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 (perm === "
|
|
41377
|
+
if (perm === "*") {
|
|
41399
41378
|
permissions.push(...validPermissions);
|
|
41400
41379
|
} else if (!validPermissions.includes(perm)) {
|
|
41401
|
-
throw new Error(`IAM rule ${ruleNum} has invalid permission: ${perm}. Must be one of: ${validPermissions.join(", ")},
|
|
41380
|
+
throw new Error(`IAM rule ${ruleNum} has invalid permission: ${perm}. Must be one of: ${validPermissions.join(", ")}, *`);
|
|
41402
41381
|
} else {
|
|
41403
41382
|
permissions.push(perm);
|
|
41404
41383
|
}
|
|
@@ -41412,7 +41391,7 @@ See https://docs.pgconsole.com/configuration/config`);
|
|
|
41412
41391
|
throw new Error(`IAM rule ${ruleNum} has invalid member: must be non-empty string`);
|
|
41413
41392
|
}
|
|
41414
41393
|
const m2 = member.trim();
|
|
41415
|
-
if (m2 === "
|
|
41394
|
+
if (m2 === "*") {
|
|
41416
41395
|
members.push(m2);
|
|
41417
41396
|
} else if (m2.startsWith("user:")) {
|
|
41418
41397
|
const email = m2.slice(5);
|
|
@@ -41430,7 +41409,7 @@ See https://docs.pgconsole.com/configuration/config`);
|
|
|
41430
41409
|
}
|
|
41431
41410
|
members.push(m2);
|
|
41432
41411
|
} else {
|
|
41433
|
-
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"`);
|
|
41434
41413
|
}
|
|
41435
41414
|
}
|
|
41436
41415
|
iam.push({ connection, permissions, members });
|
|
@@ -41472,6 +41451,9 @@ function getConnectionById(id) {
|
|
|
41472
41451
|
function getAuthConfig() {
|
|
41473
41452
|
return loadedConfig.auth;
|
|
41474
41453
|
}
|
|
41454
|
+
function getAuthProvider(type) {
|
|
41455
|
+
return loadedConfig.auth?.providers.find((p) => p.type === type);
|
|
41456
|
+
}
|
|
41475
41457
|
function getUsers() {
|
|
41476
41458
|
return loadedConfig.users;
|
|
41477
41459
|
}
|
|
@@ -41634,11 +41616,24 @@ function auditSQL(actor, connection, database, sql, success, duration_ms, row_co
|
|
|
41634
41616
|
if (error) event.error = error;
|
|
41635
41617
|
emit(event);
|
|
41636
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
|
+
}
|
|
41637
41632
|
|
|
41638
41633
|
// server/lib/oauth/google.ts
|
|
41639
41634
|
function registerGoogleOAuth(router3, opts) {
|
|
41640
41635
|
router3.get("/google", (_req, res) => {
|
|
41641
|
-
const google =
|
|
41636
|
+
const google = getAuthProvider("google");
|
|
41642
41637
|
if (!google) {
|
|
41643
41638
|
return res.status(400).json({ error: "Google OAuth not configured" });
|
|
41644
41639
|
}
|
|
@@ -41655,7 +41650,7 @@ function registerGoogleOAuth(router3, opts) {
|
|
|
41655
41650
|
return res.redirect(`https://accounts.google.com/o/oauth2/v2/auth?${params}`);
|
|
41656
41651
|
});
|
|
41657
41652
|
router3.get("/google/callback", async (req, res) => {
|
|
41658
|
-
const google =
|
|
41653
|
+
const google = getAuthProvider("google");
|
|
41659
41654
|
const externalUrl = getExternalUrl();
|
|
41660
41655
|
if (!google) {
|
|
41661
41656
|
return res.redirect(`${externalUrl}/signin?error=not_configured`);
|
|
@@ -41725,7 +41720,7 @@ function registerGoogleOAuth(router3, opts) {
|
|
|
41725
41720
|
// server/lib/oauth/keycloak.ts
|
|
41726
41721
|
function registerKeycloakOAuth(router3, opts) {
|
|
41727
41722
|
router3.get("/keycloak", (_req, res) => {
|
|
41728
|
-
const keycloak =
|
|
41723
|
+
const keycloak = getAuthProvider("keycloak");
|
|
41729
41724
|
if (!keycloak) {
|
|
41730
41725
|
return res.status(400).json({ error: "Keycloak not configured" });
|
|
41731
41726
|
}
|
|
@@ -41742,7 +41737,7 @@ function registerKeycloakOAuth(router3, opts) {
|
|
|
41742
41737
|
return res.redirect(`${keycloak.issuer_url}/protocol/openid-connect/auth?${params}`);
|
|
41743
41738
|
});
|
|
41744
41739
|
router3.get("/keycloak/callback", async (req, res) => {
|
|
41745
|
-
const keycloak =
|
|
41740
|
+
const keycloak = getAuthProvider("keycloak");
|
|
41746
41741
|
const externalUrl = getExternalUrl();
|
|
41747
41742
|
if (!keycloak) {
|
|
41748
41743
|
return res.redirect(`${externalUrl}/signin?error=not_configured`);
|
|
@@ -41809,7 +41804,7 @@ function registerKeycloakOAuth(router3, opts) {
|
|
|
41809
41804
|
// server/lib/oauth/okta.ts
|
|
41810
41805
|
function registerOktaOAuth(router3, opts) {
|
|
41811
41806
|
router3.get("/okta", (_req, res) => {
|
|
41812
|
-
const okta =
|
|
41807
|
+
const okta = getAuthProvider("okta");
|
|
41813
41808
|
if (!okta) {
|
|
41814
41809
|
return res.status(400).json({ error: "Okta not configured" });
|
|
41815
41810
|
}
|
|
@@ -41826,7 +41821,7 @@ function registerOktaOAuth(router3, opts) {
|
|
|
41826
41821
|
return res.redirect(`${okta.issuer_url}/v1/authorize?${params}`);
|
|
41827
41822
|
});
|
|
41828
41823
|
router3.get("/okta/callback", async (req, res) => {
|
|
41829
|
-
const okta =
|
|
41824
|
+
const okta = getAuthProvider("okta");
|
|
41830
41825
|
const externalUrl = getExternalUrl();
|
|
41831
41826
|
if (!okta) {
|
|
41832
41827
|
return res.redirect(`${externalUrl}/signin?error=not_configured`);
|
|
@@ -42014,14 +42009,12 @@ router.get("/providers", (_req, res) => {
|
|
|
42014
42009
|
const plan = getPlan();
|
|
42015
42010
|
const providers = [];
|
|
42016
42011
|
if (getUsers().some((u) => u.password)) providers.push({ name: "basic" });
|
|
42017
|
-
|
|
42018
|
-
|
|
42019
|
-
|
|
42020
|
-
|
|
42021
|
-
|
|
42022
|
-
|
|
42023
|
-
providers.push({ name: key });
|
|
42024
|
-
}
|
|
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 });
|
|
42025
42018
|
}
|
|
42026
42019
|
}
|
|
42027
42020
|
return res.json({ providers });
|
|
@@ -56345,6 +56338,93 @@ var TerminateSessionResponse = class _TerminateSessionResponse extends Message {
|
|
|
56345
56338
|
return proto3.util.equals(_TerminateSessionResponse, a, b);
|
|
56346
56339
|
}
|
|
56347
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
|
+
};
|
|
56348
56428
|
|
|
56349
56429
|
// src/gen/query_connect.ts
|
|
56350
56430
|
var QueryService = {
|
|
@@ -56511,6 +56591,15 @@ var QueryService = {
|
|
|
56511
56591
|
I: TerminateSessionRequest,
|
|
56512
56592
|
O: TerminateSessionResponse,
|
|
56513
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
|
|
56514
56603
|
}
|
|
56515
56604
|
}
|
|
56516
56605
|
};
|
|
@@ -57484,12 +57573,17 @@ function setConnectionVersion(connectionId, version) {
|
|
|
57484
57573
|
const info = { version };
|
|
57485
57574
|
connectionCache.set(connectionId, info);
|
|
57486
57575
|
}
|
|
57487
|
-
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 || "";
|
|
57488
57580
|
const match = versionString.match(/PostgreSQL (\d+)/);
|
|
57489
57581
|
if (!match?.[1]) {
|
|
57490
57582
|
throw new Error(`Failed to extract PostgreSQL version from: ${versionString}`);
|
|
57491
57583
|
}
|
|
57492
|
-
|
|
57584
|
+
const version = match[1];
|
|
57585
|
+
setConnectionVersion(connectionId, version);
|
|
57586
|
+
return version;
|
|
57493
57587
|
}
|
|
57494
57588
|
|
|
57495
57589
|
// server/lib/iam.ts
|
|
@@ -57538,7 +57632,7 @@ function getUserPermissions(email, connectionId) {
|
|
|
57538
57632
|
continue;
|
|
57539
57633
|
}
|
|
57540
57634
|
const matches = rule.members.some((member) => {
|
|
57541
|
-
if (member === "
|
|
57635
|
+
if (member === "*") {
|
|
57542
57636
|
return true;
|
|
57543
57637
|
}
|
|
57544
57638
|
if (member.startsWith("user:")) {
|
|
@@ -57640,11 +57734,7 @@ var connectionServiceHandlers = {
|
|
|
57640
57734
|
sslMode: conn.ssl_mode || "prefer"
|
|
57641
57735
|
}, user?.email);
|
|
57642
57736
|
try {
|
|
57643
|
-
await client
|
|
57644
|
-
const versionResult = await client`SELECT version()`;
|
|
57645
|
-
const versionString = versionResult[0]?.version || "";
|
|
57646
|
-
const version = extractPostgresVersion(versionString);
|
|
57647
|
-
setConnectionVersion(req.id, version);
|
|
57737
|
+
await testAndCacheConnection(client, req.id);
|
|
57648
57738
|
const latencyMs = Date.now() - start2;
|
|
57649
57739
|
return { success: true, error: "", latencyMs };
|
|
57650
57740
|
} catch (err) {
|
|
@@ -63197,6 +63287,21 @@ var queryServiceHandlers = {
|
|
|
63197
63287
|
error: err instanceof Error ? err.message : "Failed to terminate session"
|
|
63198
63288
|
};
|
|
63199
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 {};
|
|
63200
63305
|
}
|
|
63201
63306
|
};
|
|
63202
63307
|
|
|
@@ -92450,11 +92555,7 @@ async function testAllConnections() {
|
|
|
92450
92555
|
sslMode: conn.ssl_mode || "prefer"
|
|
92451
92556
|
});
|
|
92452
92557
|
try {
|
|
92453
|
-
await client
|
|
92454
|
-
const versionResult = await client`SELECT version()`;
|
|
92455
|
-
const versionString = versionResult[0]?.version || "";
|
|
92456
|
-
const version = extractPostgresVersion(versionString);
|
|
92457
|
-
setConnectionVersion(conn.id, version);
|
|
92558
|
+
const version = await testAndCacheConnection(client, conn.id);
|
|
92458
92559
|
console.log(` \u2713 ${conn.name} (PostgreSQL ${version})`);
|
|
92459
92560
|
} finally {
|
|
92460
92561
|
await client.end();
|
|
@@ -92532,6 +92633,7 @@ async function start() {
|
|
|
92532
92633
|
const configPath = args.config || "pgconsole.toml";
|
|
92533
92634
|
try {
|
|
92534
92635
|
await loadConfig(configPath);
|
|
92636
|
+
console.log(`\u2713 Loaded config from: ${path4.resolve(configPath)}`);
|
|
92535
92637
|
} catch (error) {
|
|
92536
92638
|
console.error("Failed to load config:", error instanceof Error ? error.message : error);
|
|
92537
92639
|
process.exit(1);
|
|
@@ -92561,7 +92663,7 @@ async function start() {
|
|
|
92561
92663
|
\\ \\_\\ /\\____/
|
|
92562
92664
|
\\/_/ \\_/__/
|
|
92563
92665
|
|
|
92564
|
-
Version ${"0.0
|
|
92666
|
+
Version ${"0.1.0"}
|
|
92565
92667
|
`);
|
|
92566
92668
|
console.log(`Server running on http://localhost:${port}`);
|
|
92567
92669
|
const browserUrl = false ? `http://localhost:5173` : getExternalUrl() || `http://localhost:${port}`;
|