@grapity/grapity 0.2.0 → 0.3.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/assets/index-Dq5tdnlb.js.map +1 -1
- package/dist/cli/index.js +631 -75
- package/dist/registry/{index-Baj_sSgl.d.ts → index-DUPbMrAe.d.ts} +3 -11
- package/dist/registry/index.d.ts +1 -1
- package/dist/registry/index.js +1 -3
- package/dist/registry/serve.d.ts +95 -5
- package/dist/registry/serve.js +517 -19
- package/package.json +1 -5
package/dist/cli/index.js
CHANGED
|
@@ -21,7 +21,8 @@ import yaml from "js-yaml";
|
|
|
21
21
|
var DEFAULT_CONFIG = {
|
|
22
22
|
mode: "local",
|
|
23
23
|
local: {
|
|
24
|
-
port: 3750
|
|
24
|
+
port: 3750,
|
|
25
|
+
database: "sqlite"
|
|
25
26
|
}
|
|
26
27
|
};
|
|
27
28
|
function getConfig() {
|
|
@@ -40,12 +41,18 @@ function getConfig() {
|
|
|
40
41
|
return DEFAULT_CONFIG;
|
|
41
42
|
}
|
|
42
43
|
const config = parsed;
|
|
44
|
+
let database = config.local?.database ?? DEFAULT_CONFIG.local.database;
|
|
45
|
+
if (!config.local?.database && config.local?.sqlitePath) {
|
|
46
|
+
database = "sqlite";
|
|
47
|
+
}
|
|
43
48
|
return {
|
|
44
49
|
mode: config.mode ?? DEFAULT_CONFIG.mode,
|
|
45
50
|
remote: config.remote,
|
|
46
51
|
local: {
|
|
47
52
|
port: config.local?.port ?? DEFAULT_CONFIG.local.port,
|
|
48
|
-
|
|
53
|
+
database,
|
|
54
|
+
sqlitePath: config.local?.sqlitePath,
|
|
55
|
+
postgresUrl: config.local?.postgresUrl
|
|
49
56
|
}
|
|
50
57
|
};
|
|
51
58
|
}
|
|
@@ -56,6 +63,9 @@ function getRegistryUrl() {
|
|
|
56
63
|
}
|
|
57
64
|
return `http://localhost:${config.local?.port ?? 3750}`;
|
|
58
65
|
}
|
|
66
|
+
function isPostgresqlUrl(value) {
|
|
67
|
+
return value.startsWith("postgresql://") || value.startsWith("postgres://");
|
|
68
|
+
}
|
|
59
69
|
|
|
60
70
|
// src/cli/client.ts
|
|
61
71
|
var BreakingChangeError = class extends Error {
|
|
@@ -69,16 +79,11 @@ var BreakingChangeError = class extends Error {
|
|
|
69
79
|
async function request(method, path12, body) {
|
|
70
80
|
const baseUrl = getRegistryUrl();
|
|
71
81
|
const url = `${baseUrl}${path12}`;
|
|
72
|
-
const config = getConfig();
|
|
73
|
-
const headers = {
|
|
74
|
-
"Content-Type": "application/json"
|
|
75
|
-
};
|
|
76
|
-
if (config.mode === "remote" && config.remote?.apiKey) {
|
|
77
|
-
headers["X-API-Key"] = config.remote.apiKey;
|
|
78
|
-
}
|
|
79
82
|
const response = await fetch(url, {
|
|
80
83
|
method,
|
|
81
|
-
headers
|
|
84
|
+
headers: {
|
|
85
|
+
"Content-Type": "application/json"
|
|
86
|
+
},
|
|
82
87
|
body: body ? JSON.stringify(body) : void 0
|
|
83
88
|
});
|
|
84
89
|
if (!response.ok) {
|
|
@@ -88,18 +93,13 @@ async function request(method, path12, body) {
|
|
|
88
93
|
}
|
|
89
94
|
throw new Error(error.message ?? `Request failed: ${response.status}`);
|
|
90
95
|
}
|
|
91
|
-
const
|
|
92
|
-
return
|
|
96
|
+
const text3 = await response.text();
|
|
97
|
+
return text3 ? JSON.parse(text3) : void 0;
|
|
93
98
|
}
|
|
94
99
|
async function requestText(method, path12) {
|
|
95
100
|
const baseUrl = getRegistryUrl();
|
|
96
101
|
const url = `${baseUrl}${path12}`;
|
|
97
|
-
const
|
|
98
|
-
const headers = {};
|
|
99
|
-
if (config.mode === "remote" && config.remote?.apiKey) {
|
|
100
|
-
headers["X-API-Key"] = config.remote.apiKey;
|
|
101
|
-
}
|
|
102
|
-
const response = await fetch(url, { method, headers });
|
|
102
|
+
const response = await fetch(url, { method });
|
|
103
103
|
if (!response.ok) {
|
|
104
104
|
const error = await response.json();
|
|
105
105
|
throw new Error(error.message ?? `Request failed: ${response.status}`);
|
|
@@ -150,9 +150,9 @@ var client = {
|
|
|
150
150
|
fetchSpec: async (name, options) => {
|
|
151
151
|
const format = options.format ?? "yaml";
|
|
152
152
|
const path12 = options.semver ? `/v1/specs/${name}/versions/${options.semver}/spec.${format}` : `/v1/specs/${name}/spec.${format}`;
|
|
153
|
-
const { text:
|
|
153
|
+
const { text: text3, headers } = await requestText("GET", path12);
|
|
154
154
|
return {
|
|
155
|
-
content:
|
|
155
|
+
content: text3,
|
|
156
156
|
resolvedVersion: headers.get("Grapity-Resolved-Version") ?? void 0
|
|
157
157
|
};
|
|
158
158
|
},
|
|
@@ -531,27 +531,43 @@ function formatInitSuccess(params) {
|
|
|
531
531
|
];
|
|
532
532
|
if (params.mode === "local") {
|
|
533
533
|
if (params.port) lines.push(` ${c.label("Port")} ${c.cyan(String(params.port))}`);
|
|
534
|
-
if (params.
|
|
534
|
+
if (params.database) lines.push(` ${c.label("Database")} ${c.primary(params.database)}`);
|
|
535
|
+
if (params.dbPath) lines.push(` ${c.label("Path")} ${c.dim(params.dbPath)}`);
|
|
536
|
+
if (params.postgresUrl) lines.push(` ${c.label("PostgreSQL")} ${c.dim(params.postgresUrl)}`);
|
|
535
537
|
lines.push("");
|
|
536
538
|
lines.push(` ${c.dim("\u203A")} Start the server with: ${c.primary("grapity serve")}`);
|
|
537
539
|
} else {
|
|
538
540
|
if (params.url) lines.push(` ${c.label("URL")} ${c.cyan(params.url)}`);
|
|
539
|
-
if (params.hasApiKey) lines.push(` ${c.label("API key")} ${c.dim("configured")}`);
|
|
540
541
|
lines.push("");
|
|
541
542
|
lines.push(` ${c.dim("\u203A")} Push a spec with: ${c.primary("grapity registry push ./openapi.yaml --name my-api")}`);
|
|
542
543
|
}
|
|
543
544
|
return lines.join("\n");
|
|
544
545
|
}
|
|
545
546
|
function formatServeConfig(params) {
|
|
546
|
-
const modeLabel = `local ${c.label("\xB7")} ${params.mode}`;
|
|
547
547
|
const lines = [
|
|
548
|
-
` ${c.label("Mode")} ${c.primary(
|
|
549
|
-
` ${c.label("Port")} ${c.cyan(String(params.port))}
|
|
548
|
+
` ${c.label("Mode")} ${c.primary("local")}`,
|
|
549
|
+
` ${c.label("Port")} ${c.cyan(String(params.port))}`,
|
|
550
|
+
` ${c.label("Database")} ${c.primary(params.database)}`
|
|
550
551
|
];
|
|
551
|
-
if (params.
|
|
552
|
-
|
|
552
|
+
if (params.database === "sqlite" && params.dbPath) {
|
|
553
|
+
lines.push(` ${c.label("Path")} ${c.dim(params.dbPath)}`);
|
|
554
|
+
}
|
|
555
|
+
if (params.database === "postgresql" && params.postgresUrl) {
|
|
556
|
+
lines.push(` ${c.label("PostgreSQL")} ${c.dim(maskPostgresUrl(params.postgresUrl))}`);
|
|
557
|
+
}
|
|
553
558
|
return lines.join("\n");
|
|
554
559
|
}
|
|
560
|
+
function maskPostgresUrl(url) {
|
|
561
|
+
try {
|
|
562
|
+
const parsed = new URL(url);
|
|
563
|
+
if (parsed.password) {
|
|
564
|
+
parsed.password = "***";
|
|
565
|
+
}
|
|
566
|
+
return parsed.toString();
|
|
567
|
+
} catch {
|
|
568
|
+
return url;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
555
571
|
function formatHubConfig(params) {
|
|
556
572
|
const lines = [
|
|
557
573
|
` ${c.label("Port")} ${c.cyan(String(params.port))}`,
|
|
@@ -744,18 +760,18 @@ var validateCommand = new Command2("validate").description("Validate a spec agai
|
|
|
744
760
|
import { Command as Command3 } from "commander";
|
|
745
761
|
var listCommand = new Command3("list").description("List all specs in the registry").option("--type <type>", "Filter by spec type").option("--owner <owner>", "Filter by owner").option("--tags <tags>", "Filter by tags (comma-separated)").action(async (options) => {
|
|
746
762
|
try {
|
|
747
|
-
const
|
|
763
|
+
const specs3 = await client.listSpecs({
|
|
748
764
|
type: options.type,
|
|
749
765
|
owner: options.owner,
|
|
750
766
|
tags: options.tags?.split(",")
|
|
751
767
|
});
|
|
752
|
-
if (
|
|
768
|
+
if (specs3.length === 0) {
|
|
753
769
|
console.log(formatEmptyState("No specs in the registry.", [
|
|
754
770
|
"Push one with: grapity registry push ./openapi.yaml --name my-api"
|
|
755
771
|
]));
|
|
756
772
|
return;
|
|
757
773
|
}
|
|
758
|
-
for (const spec of
|
|
774
|
+
for (const spec of specs3) {
|
|
759
775
|
console.log(formatSpec(spec));
|
|
760
776
|
}
|
|
761
777
|
} catch (err) {
|
|
@@ -1145,10 +1161,10 @@ function convertPathToKong(path12) {
|
|
|
1145
1161
|
}
|
|
1146
1162
|
return converted;
|
|
1147
1163
|
}
|
|
1148
|
-
function routeName(serviceName,
|
|
1164
|
+
function routeName(serviceName, index3, path12, methods) {
|
|
1149
1165
|
const cleanPath = path12.replace(/[^a-zA-Z0-9]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
1150
1166
|
const methodList = methods.join("-").toLowerCase();
|
|
1151
|
-
return `${serviceName}-${cleanPath}-${methodList}-${
|
|
1167
|
+
return `${serviceName}-${cleanPath}-${methodList}-${index3}`;
|
|
1152
1168
|
}
|
|
1153
1169
|
function generateDeckYaml(config, envName) {
|
|
1154
1170
|
const env = config.environments[envName];
|
|
@@ -1380,7 +1396,7 @@ import fs8 from "fs";
|
|
|
1380
1396
|
import os3 from "os";
|
|
1381
1397
|
import path8 from "path";
|
|
1382
1398
|
import yaml4 from "js-yaml";
|
|
1383
|
-
var initCommand = new Command18("init").description("Configure grapity registry (local or remote mode)").option("--local", "Use local mode (SQLite)").option("--remote", "Use remote mode (connect to a grapity server)").option("--url <url>", "Registry URL (for remote mode)").option("--
|
|
1399
|
+
var initCommand = new Command18("init").description("Configure grapity registry (local or remote mode)").option("--local", "Use local mode (SQLite or PostgreSQL)").option("--remote", "Use remote mode (connect to a grapity server)").option("--url <url>", "Registry URL (for remote mode)").option("--port <port>", "Port for local server (default: 3750)").option("--db <path-or-url>", "SQLite path or postgresql:// URL (for local mode)").action(async (options) => {
|
|
1384
1400
|
const configDir = path8.join(os3.homedir(), ".grapity");
|
|
1385
1401
|
const configPath = path8.join(configDir, "config.yaml");
|
|
1386
1402
|
let mode;
|
|
@@ -1398,7 +1414,7 @@ var initCommand = new Command18("init").description("Configure grapity registry
|
|
|
1398
1414
|
"missing flag",
|
|
1399
1415
|
"Select registry mode: use --local or --remote.",
|
|
1400
1416
|
[
|
|
1401
|
-
"--local Run a registry server on this machine
|
|
1417
|
+
"--local Run a registry server on this machine",
|
|
1402
1418
|
"--remote Connect to an existing grapity server"
|
|
1403
1419
|
]
|
|
1404
1420
|
)
|
|
@@ -1407,12 +1423,16 @@ var initCommand = new Command18("init").description("Configure grapity registry
|
|
|
1407
1423
|
}
|
|
1408
1424
|
const config = { mode };
|
|
1409
1425
|
if (mode === "local") {
|
|
1426
|
+
const dbValue = options.db ?? process.env.GRAPITY_DATABASE_URL;
|
|
1427
|
+
const database = dbValue && isPostgresqlUrl(dbValue) ? "postgresql" : "sqlite";
|
|
1410
1428
|
config.local = {
|
|
1411
1429
|
port: options.port ? parseInt(options.port, 10) : 3750,
|
|
1412
|
-
|
|
1430
|
+
database
|
|
1413
1431
|
};
|
|
1414
|
-
if (
|
|
1415
|
-
config.local.
|
|
1432
|
+
if (database === "postgresql") {
|
|
1433
|
+
config.local.postgresUrl = dbValue;
|
|
1434
|
+
} else {
|
|
1435
|
+
config.local.sqlitePath = dbValue ?? path8.join(os3.homedir(), ".grapity", "registry.db");
|
|
1416
1436
|
}
|
|
1417
1437
|
} else {
|
|
1418
1438
|
if (!options.url) {
|
|
@@ -1426,8 +1446,7 @@ var initCommand = new Command18("init").description("Configure grapity registry
|
|
|
1426
1446
|
process.exit(1);
|
|
1427
1447
|
}
|
|
1428
1448
|
config.remote = {
|
|
1429
|
-
url: options.url.replace(/\/$/, "")
|
|
1430
|
-
apiKey: options.apiKey
|
|
1449
|
+
url: options.url.replace(/\/$/, "")
|
|
1431
1450
|
};
|
|
1432
1451
|
}
|
|
1433
1452
|
if (!fs8.existsSync(configDir)) {
|
|
@@ -1440,9 +1459,10 @@ var initCommand = new Command18("init").description("Configure grapity registry
|
|
|
1440
1459
|
configPath,
|
|
1441
1460
|
mode,
|
|
1442
1461
|
port: config.local?.port,
|
|
1462
|
+
database: config.local?.database,
|
|
1443
1463
|
dbPath: config.local?.sqlitePath,
|
|
1444
|
-
|
|
1445
|
-
|
|
1464
|
+
postgresUrl: config.local?.postgresUrl,
|
|
1465
|
+
url: config.remote?.url
|
|
1446
1466
|
})
|
|
1447
1467
|
);
|
|
1448
1468
|
});
|
|
@@ -3090,9 +3110,9 @@ var RegistryService = class {
|
|
|
3090
3110
|
return { spec, version: version2, compatReport, isNewSpec };
|
|
3091
3111
|
}
|
|
3092
3112
|
async listSpecs(filters) {
|
|
3093
|
-
const
|
|
3113
|
+
const specs3 = await this.store.listSpecs(filters);
|
|
3094
3114
|
return Promise.all(
|
|
3095
|
-
|
|
3115
|
+
specs3.map(async (spec) => {
|
|
3096
3116
|
const latestVersion = await this.store.getLatestVersion(spec.name);
|
|
3097
3117
|
return { ...spec, latestVersion: latestVersion ?? void 0 };
|
|
3098
3118
|
})
|
|
@@ -3350,8 +3370,8 @@ var listRoute = new Hono3().get("/", async (c2) => {
|
|
|
3350
3370
|
const type = c2.req.query("type");
|
|
3351
3371
|
const owner = c2.req.query("owner");
|
|
3352
3372
|
const tags = c2.req.query("tags")?.split(",");
|
|
3353
|
-
const
|
|
3354
|
-
return c2.json({ data:
|
|
3373
|
+
const specs3 = await service.listSpecs({ type, owner, tags });
|
|
3374
|
+
return c2.json({ data: specs3 });
|
|
3355
3375
|
});
|
|
3356
3376
|
|
|
3357
3377
|
// src/registry/routes/get-spec.ts
|
|
@@ -3985,8 +4005,7 @@ function switchTab(tab) {
|
|
|
3985
4005
|
}
|
|
3986
4006
|
var welcomeRoute = new Hono12().get("/", (c2) => {
|
|
3987
4007
|
const config = c2.get("config");
|
|
3988
|
-
|
|
3989
|
-
return c2.html(buildPage(config.port, mode));
|
|
4008
|
+
return c2.html(buildPage(config.port, "local"));
|
|
3990
4009
|
});
|
|
3991
4010
|
|
|
3992
4011
|
// src/registry/routes/push-gateway-config.ts
|
|
@@ -4327,7 +4346,6 @@ var ingestGatewayLogRoute = new Hono18().post("/ingest/:provider/:environment",
|
|
|
4327
4346
|
await service.ingestLog(provider, environment, payload);
|
|
4328
4347
|
return c2.json({ status: "ok" }, 201);
|
|
4329
4348
|
} catch (err) {
|
|
4330
|
-
console.error("Gateway log ingest error:", err);
|
|
4331
4349
|
return c2.json({
|
|
4332
4350
|
error: "bad_request",
|
|
4333
4351
|
message: err instanceof Error ? err.message : "Invalid log payload",
|
|
@@ -4440,9 +4458,7 @@ function createApp(config, store) {
|
|
|
4440
4458
|
// src/registry/config.ts
|
|
4441
4459
|
var defaultConfig = {
|
|
4442
4460
|
port: 3750,
|
|
4443
|
-
database: "sqlite"
|
|
4444
|
-
sqlitePath: void 0,
|
|
4445
|
-
gracePeriodDays: 30
|
|
4461
|
+
database: "sqlite"
|
|
4446
4462
|
};
|
|
4447
4463
|
|
|
4448
4464
|
// src/registry/storage/sqlite.ts
|
|
@@ -4948,27 +4964,529 @@ var SQLiteSpecStore = class {
|
|
|
4948
4964
|
}
|
|
4949
4965
|
};
|
|
4950
4966
|
|
|
4967
|
+
// src/registry/storage/postgresql.ts
|
|
4968
|
+
import { Pool } from "pg";
|
|
4969
|
+
import { drizzle as drizzle2 } from "drizzle-orm/node-postgres";
|
|
4970
|
+
import { migrate as migrate2 } from "drizzle-orm/node-postgres/migrator";
|
|
4971
|
+
import { eq as eq2, and as and2, desc as desc2, sql as sql2 } from "drizzle-orm";
|
|
4972
|
+
|
|
4973
|
+
// src/registry/storage/schema-pg.ts
|
|
4974
|
+
import { pgTable, text as text2, timestamp, boolean, jsonb, index as index2, integer as integer2 } from "drizzle-orm/pg-core";
|
|
4975
|
+
var specs2 = pgTable("specs", {
|
|
4976
|
+
id: text2("id").primaryKey(),
|
|
4977
|
+
name: text2("name").notNull().unique(),
|
|
4978
|
+
type: text2("type", { enum: ["openapi", "asyncapi"] }).notNull(),
|
|
4979
|
+
description: text2("description"),
|
|
4980
|
+
owner: text2("owner"),
|
|
4981
|
+
sourceRepo: text2("source_repo"),
|
|
4982
|
+
tags: jsonb("tags").$type().default([]),
|
|
4983
|
+
createdAt: timestamp("created_at").notNull(),
|
|
4984
|
+
updatedAt: timestamp("updated_at").notNull()
|
|
4985
|
+
});
|
|
4986
|
+
var specVersions2 = pgTable("spec_versions", {
|
|
4987
|
+
id: text2("id").primaryKey(),
|
|
4988
|
+
specId: text2("spec_id").notNull().references(() => specs2.id),
|
|
4989
|
+
semver: text2("semver").notNull(),
|
|
4990
|
+
content: text2("content").notNull(),
|
|
4991
|
+
checksum: text2("checksum").notNull(),
|
|
4992
|
+
gitRef: text2("git_ref"),
|
|
4993
|
+
pushedBy: text2("pushed_by"),
|
|
4994
|
+
compatibility: jsonb("compatibility").$type(),
|
|
4995
|
+
previousVersion: text2("previous_version"),
|
|
4996
|
+
forceReason: text2("force_reason"),
|
|
4997
|
+
isPrerelease: boolean("is_prerelease").notNull().default(false),
|
|
4998
|
+
createdAt: timestamp("created_at").notNull()
|
|
4999
|
+
}, (table) => [
|
|
5000
|
+
index2("idx_spec_versions_spec_id").on(table.specId),
|
|
5001
|
+
index2("idx_spec_versions_semver").on(table.specId, table.semver)
|
|
5002
|
+
]);
|
|
5003
|
+
var auditLog2 = pgTable("audit_log", {
|
|
5004
|
+
id: text2("id").primaryKey(),
|
|
5005
|
+
action: text2("action", { enum: ["spec.push", "spec.push.force", "spec.delete"] }).notNull(),
|
|
5006
|
+
actor: text2("actor").notNull(),
|
|
5007
|
+
specName: text2("spec_name").notNull(),
|
|
5008
|
+
version: text2("version"),
|
|
5009
|
+
details: jsonb("details").$type(),
|
|
5010
|
+
createdAt: timestamp("created_at").notNull()
|
|
5011
|
+
}, (table) => [
|
|
5012
|
+
index2("idx_audit_log_spec_name").on(table.specName),
|
|
5013
|
+
index2("idx_audit_log_created_at").on(table.createdAt)
|
|
5014
|
+
]);
|
|
5015
|
+
var gatewayConfigs2 = pgTable("gateway_configs", {
|
|
5016
|
+
id: text2("id").primaryKey(),
|
|
5017
|
+
name: text2("name").notNull().unique(),
|
|
5018
|
+
provider: text2("provider", { enum: ["kong"] }).notNull(),
|
|
5019
|
+
specName: text2("spec_name").notNull(),
|
|
5020
|
+
specSemver: text2("spec_semver").notNull(),
|
|
5021
|
+
createdAt: timestamp("created_at").notNull(),
|
|
5022
|
+
updatedAt: timestamp("updated_at").notNull()
|
|
5023
|
+
});
|
|
5024
|
+
var gatewayConfigVersions2 = pgTable("gateway_config_versions", {
|
|
5025
|
+
id: text2("id").primaryKey(),
|
|
5026
|
+
gatewayConfigId: text2("gateway_config_id").notNull().references(() => gatewayConfigs2.id),
|
|
5027
|
+
routes: jsonb("routes").$type().notNull(),
|
|
5028
|
+
environments: jsonb("environments").$type().notNull(),
|
|
5029
|
+
callerIdentification: jsonb("caller_identification").$type(),
|
|
5030
|
+
content: text2("content").notNull(),
|
|
5031
|
+
checksum: text2("checksum").notNull(),
|
|
5032
|
+
pushedBy: text2("pushed_by"),
|
|
5033
|
+
createdAt: timestamp("created_at").notNull()
|
|
5034
|
+
}, (table) => [
|
|
5035
|
+
index2("idx_gateway_config_versions_config_id").on(table.gatewayConfigId)
|
|
5036
|
+
]);
|
|
5037
|
+
var httpLogs2 = pgTable("http_logs", {
|
|
5038
|
+
id: text2("id").primaryKey(),
|
|
5039
|
+
provider: text2("provider").notNull(),
|
|
5040
|
+
gatewayConfigName: text2("gateway_config_name").notNull(),
|
|
5041
|
+
environment: text2("environment").notNull(),
|
|
5042
|
+
method: text2("method").notNull(),
|
|
5043
|
+
path: text2("path").notNull(),
|
|
5044
|
+
routePath: text2("route_path"),
|
|
5045
|
+
status: integer2("status").notNull(),
|
|
5046
|
+
callerId: text2("caller_id"),
|
|
5047
|
+
callerSource: text2("caller_source"),
|
|
5048
|
+
callerConfidence: text2("caller_confidence").notNull(),
|
|
5049
|
+
occurredAt: timestamp("occurred_at").notNull(),
|
|
5050
|
+
createdAt: timestamp("created_at").notNull()
|
|
5051
|
+
}, (table) => [
|
|
5052
|
+
index2("idx_http_logs_config_env").on(table.gatewayConfigName, table.environment),
|
|
5053
|
+
index2("idx_http_logs_occurred_at").on(table.occurredAt),
|
|
5054
|
+
index2("idx_http_logs_caller").on(table.gatewayConfigName, table.environment, table.callerId)
|
|
5055
|
+
]);
|
|
5056
|
+
var provisions2 = pgTable("provisions", {
|
|
5057
|
+
id: text2("id").primaryKey(),
|
|
5058
|
+
gatewayConfigName: text2("gateway_config_name").notNull(),
|
|
5059
|
+
gatewayConfigVersion: text2("gateway_config_version").notNull(),
|
|
5060
|
+
environment: text2("environment").notNull(),
|
|
5061
|
+
provider: text2("provider", { enum: ["kong"] }).notNull(),
|
|
5062
|
+
synced: boolean("synced").notNull().default(false),
|
|
5063
|
+
actor: text2("actor").notNull(),
|
|
5064
|
+
details: jsonb("details").$type(),
|
|
5065
|
+
createdAt: timestamp("created_at").notNull()
|
|
5066
|
+
}, (table) => [
|
|
5067
|
+
index2("idx_provisions_config_name").on(table.gatewayConfigName),
|
|
5068
|
+
index2("idx_provisions_created_at").on(table.createdAt)
|
|
5069
|
+
]);
|
|
5070
|
+
|
|
5071
|
+
// src/registry/storage/postgresql.ts
|
|
5072
|
+
import { v4 as uuid6 } from "uuid";
|
|
5073
|
+
var MIGRATIONS_FOLDER2 = PG_MIGRATIONS_FOLDER;
|
|
5074
|
+
var PostgreSQLSpecStore = class {
|
|
5075
|
+
db;
|
|
5076
|
+
pool;
|
|
5077
|
+
constructor(postgresUrl) {
|
|
5078
|
+
this.pool = new Pool({ connectionString: postgresUrl });
|
|
5079
|
+
this.db = drizzle2(this.pool);
|
|
5080
|
+
}
|
|
5081
|
+
async migrate() {
|
|
5082
|
+
await migrate2(this.db, {
|
|
5083
|
+
migrationsFolder: MIGRATIONS_FOLDER2
|
|
5084
|
+
});
|
|
5085
|
+
}
|
|
5086
|
+
async end() {
|
|
5087
|
+
await this.pool.end();
|
|
5088
|
+
}
|
|
5089
|
+
async getSpec(name) {
|
|
5090
|
+
const rows = await this.db.select().from(specs2).where(eq2(specs2.name, name)).limit(1);
|
|
5091
|
+
if (rows.length === 0) return null;
|
|
5092
|
+
return this.mapSpecRow(rows[0]);
|
|
5093
|
+
}
|
|
5094
|
+
async getSpecVersion(name, semver) {
|
|
5095
|
+
const spec = await this.getSpec(name);
|
|
5096
|
+
if (!spec) return null;
|
|
5097
|
+
const rows = await this.db.select().from(specVersions2).where(and2(eq2(specVersions2.specId, spec.id), eq2(specVersions2.semver, semver))).limit(1);
|
|
5098
|
+
if (rows.length === 0) return null;
|
|
5099
|
+
return this.mapVersionRow(rows[0]);
|
|
5100
|
+
}
|
|
5101
|
+
async getLatestVersion(name) {
|
|
5102
|
+
const spec = await this.getSpec(name);
|
|
5103
|
+
if (!spec) return null;
|
|
5104
|
+
const rows = await this.db.select().from(specVersions2).where(eq2(specVersions2.specId, spec.id)).orderBy(desc2(specVersions2.createdAt), desc2(specVersions2.id)).limit(1);
|
|
5105
|
+
if (rows.length === 0) return null;
|
|
5106
|
+
return this.mapVersionRow(rows[0]);
|
|
5107
|
+
}
|
|
5108
|
+
async listSpecs(filters) {
|
|
5109
|
+
const conditions = [];
|
|
5110
|
+
if (filters?.type) conditions.push(eq2(specs2.type, filters.type));
|
|
5111
|
+
if (filters?.owner) conditions.push(eq2(specs2.owner, filters.owner));
|
|
5112
|
+
let rows = conditions.length > 0 ? await this.db.select().from(specs2).where(and2(...conditions)) : await this.db.select().from(specs2);
|
|
5113
|
+
if (filters?.tags && filters.tags.length > 0) {
|
|
5114
|
+
rows = rows.filter((row) => {
|
|
5115
|
+
const rowTags = row.tags ?? [];
|
|
5116
|
+
return filters.tags.every((tag) => rowTags.includes(tag));
|
|
5117
|
+
});
|
|
5118
|
+
}
|
|
5119
|
+
return rows.map((r) => this.mapSpecRow(r));
|
|
5120
|
+
}
|
|
5121
|
+
async listVersions(name, options) {
|
|
5122
|
+
const spec = await this.getSpec(name);
|
|
5123
|
+
if (!spec) return { versions: [], total: 0 };
|
|
5124
|
+
const limit = options?.limit ?? 10;
|
|
5125
|
+
const offset = options?.offset ?? 0;
|
|
5126
|
+
const [countRow] = await this.db.select({ count: sql2`count(*)` }).from(specVersions2).where(eq2(specVersions2.specId, spec.id));
|
|
5127
|
+
const total = Number(countRow?.count ?? 0);
|
|
5128
|
+
const rows = await this.db.select().from(specVersions2).where(eq2(specVersions2.specId, spec.id)).orderBy(desc2(specVersions2.createdAt), desc2(specVersions2.id)).limit(limit).offset(offset);
|
|
5129
|
+
return { versions: rows.map((r) => this.mapVersionRow(r)), total };
|
|
5130
|
+
}
|
|
5131
|
+
async pushSpecVersion(spec, version2) {
|
|
5132
|
+
const existingSpec = await this.getSpec(spec.name);
|
|
5133
|
+
if (!existingSpec) {
|
|
5134
|
+
await this.db.insert(specs2).values({
|
|
5135
|
+
id: spec.id,
|
|
5136
|
+
name: spec.name,
|
|
5137
|
+
type: spec.type,
|
|
5138
|
+
description: spec.description ?? null,
|
|
5139
|
+
owner: spec.owner ?? null,
|
|
5140
|
+
sourceRepo: spec.sourceRepo ?? null,
|
|
5141
|
+
tags: spec.tags ?? [],
|
|
5142
|
+
createdAt: spec.createdAt,
|
|
5143
|
+
updatedAt: spec.updatedAt
|
|
5144
|
+
});
|
|
5145
|
+
} else {
|
|
5146
|
+
await this.db.update(specs2).set({ updatedAt: /* @__PURE__ */ new Date() }).where(eq2(specs2.id, existingSpec.id));
|
|
5147
|
+
}
|
|
5148
|
+
const specId = existingSpec?.id ?? spec.id;
|
|
5149
|
+
await this.db.insert(specVersions2).values({
|
|
5150
|
+
id: version2.id,
|
|
5151
|
+
specId,
|
|
5152
|
+
semver: version2.semver,
|
|
5153
|
+
content: version2.content,
|
|
5154
|
+
checksum: version2.checksum,
|
|
5155
|
+
gitRef: version2.gitRef ?? null,
|
|
5156
|
+
pushedBy: version2.pushedBy ?? null,
|
|
5157
|
+
compatibility: version2.compatibility ?? null,
|
|
5158
|
+
previousVersion: version2.previousVersion ?? null,
|
|
5159
|
+
forceReason: version2.forceReason ?? null,
|
|
5160
|
+
isPrerelease: version2.isPrerelease,
|
|
5161
|
+
createdAt: version2.createdAt
|
|
5162
|
+
});
|
|
5163
|
+
return version2;
|
|
5164
|
+
}
|
|
5165
|
+
async deleteSpec(name) {
|
|
5166
|
+
const existingSpec = await this.getSpec(name);
|
|
5167
|
+
if (!existingSpec) return false;
|
|
5168
|
+
await this.db.delete(specVersions2).where(eq2(specVersions2.specId, existingSpec.id));
|
|
5169
|
+
await this.db.delete(specs2).where(eq2(specs2.id, existingSpec.id));
|
|
5170
|
+
return true;
|
|
5171
|
+
}
|
|
5172
|
+
async getCompatReport(name, semver) {
|
|
5173
|
+
const version2 = await this.getSpecVersion(name, semver);
|
|
5174
|
+
return version2?.compatibility ?? null;
|
|
5175
|
+
}
|
|
5176
|
+
async logAudit(action, actor, specName, version2, details) {
|
|
5177
|
+
await this.db.insert(auditLog2).values({
|
|
5178
|
+
id: uuid6(),
|
|
5179
|
+
action,
|
|
5180
|
+
actor,
|
|
5181
|
+
specName,
|
|
5182
|
+
version: version2 ?? null,
|
|
5183
|
+
details: details ?? null,
|
|
5184
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
5185
|
+
});
|
|
5186
|
+
}
|
|
5187
|
+
mapSpecRow(row) {
|
|
5188
|
+
return {
|
|
5189
|
+
id: row.id,
|
|
5190
|
+
name: row.name,
|
|
5191
|
+
type: row.type,
|
|
5192
|
+
description: row.description ?? void 0,
|
|
5193
|
+
owner: row.owner ?? void 0,
|
|
5194
|
+
sourceRepo: row.sourceRepo ?? void 0,
|
|
5195
|
+
tags: row.tags ?? [],
|
|
5196
|
+
createdAt: row.createdAt,
|
|
5197
|
+
updatedAt: row.updatedAt
|
|
5198
|
+
};
|
|
5199
|
+
}
|
|
5200
|
+
mapVersionRow(row) {
|
|
5201
|
+
return {
|
|
5202
|
+
id: row.id,
|
|
5203
|
+
specId: row.specId,
|
|
5204
|
+
semver: row.semver,
|
|
5205
|
+
content: row.content,
|
|
5206
|
+
checksum: row.checksum,
|
|
5207
|
+
gitRef: row.gitRef ?? void 0,
|
|
5208
|
+
pushedBy: row.pushedBy ?? void 0,
|
|
5209
|
+
compatibility: row.compatibility ?? void 0,
|
|
5210
|
+
previousVersion: row.previousVersion ?? void 0,
|
|
5211
|
+
forceReason: row.forceReason ?? void 0,
|
|
5212
|
+
isPrerelease: row.isPrerelease,
|
|
5213
|
+
createdAt: row.createdAt
|
|
5214
|
+
};
|
|
5215
|
+
}
|
|
5216
|
+
// GatewayConfigStore implementation
|
|
5217
|
+
async getGatewayConfig(name) {
|
|
5218
|
+
const rows = await this.db.select().from(gatewayConfigs2).where(eq2(gatewayConfigs2.name, name)).limit(1);
|
|
5219
|
+
if (rows.length === 0) return null;
|
|
5220
|
+
return this.mapGatewayConfigRow(rows[0]);
|
|
5221
|
+
}
|
|
5222
|
+
async listGatewayConfigs() {
|
|
5223
|
+
const rows = await this.db.select().from(gatewayConfigs2);
|
|
5224
|
+
return rows.map((r) => this.mapGatewayConfigRow(r));
|
|
5225
|
+
}
|
|
5226
|
+
async getGatewayConfigVersion(name, versionId) {
|
|
5227
|
+
const config = await this.getGatewayConfig(name);
|
|
5228
|
+
if (!config) return null;
|
|
5229
|
+
const rows = await this.db.select().from(gatewayConfigVersions2).where(and2(eq2(gatewayConfigVersions2.gatewayConfigId, config.id), eq2(gatewayConfigVersions2.id, versionId))).limit(1);
|
|
5230
|
+
if (rows.length === 0) return null;
|
|
5231
|
+
return this.mapGatewayConfigVersionRow(rows[0]);
|
|
5232
|
+
}
|
|
5233
|
+
async getLatestGatewayConfigVersion(name) {
|
|
5234
|
+
const config = await this.getGatewayConfig(name);
|
|
5235
|
+
if (!config) return null;
|
|
5236
|
+
const rows = await this.db.select().from(gatewayConfigVersions2).where(eq2(gatewayConfigVersions2.gatewayConfigId, config.id)).orderBy(desc2(gatewayConfigVersions2.createdAt), desc2(gatewayConfigVersions2.id)).limit(1);
|
|
5237
|
+
if (rows.length === 0) return null;
|
|
5238
|
+
return this.mapGatewayConfigVersionRow(rows[0]);
|
|
5239
|
+
}
|
|
5240
|
+
async listGatewayConfigVersions(name) {
|
|
5241
|
+
const config = await this.getGatewayConfig(name);
|
|
5242
|
+
if (!config) return [];
|
|
5243
|
+
const rows = await this.db.select().from(gatewayConfigVersions2).where(eq2(gatewayConfigVersions2.gatewayConfigId, config.id)).orderBy(desc2(gatewayConfigVersions2.createdAt), desc2(gatewayConfigVersions2.id)).limit(5);
|
|
5244
|
+
return rows.map((r) => this.mapGatewayConfigVersionRow(r));
|
|
5245
|
+
}
|
|
5246
|
+
async pushGatewayConfigVersion(config, version2) {
|
|
5247
|
+
const existingConfig = await this.getGatewayConfig(config.name);
|
|
5248
|
+
if (!existingConfig) {
|
|
5249
|
+
await this.db.insert(gatewayConfigs2).values({
|
|
5250
|
+
id: config.id,
|
|
5251
|
+
name: config.name,
|
|
5252
|
+
provider: config.provider,
|
|
5253
|
+
specName: config.specName,
|
|
5254
|
+
specSemver: config.specSemver,
|
|
5255
|
+
createdAt: config.createdAt,
|
|
5256
|
+
updatedAt: config.updatedAt
|
|
5257
|
+
});
|
|
5258
|
+
} else {
|
|
5259
|
+
await this.db.update(gatewayConfigs2).set({ updatedAt: /* @__PURE__ */ new Date(), specSemver: config.specSemver }).where(eq2(gatewayConfigs2.id, existingConfig.id));
|
|
5260
|
+
}
|
|
5261
|
+
const configId = existingConfig?.id ?? config.id;
|
|
5262
|
+
await this.db.insert(gatewayConfigVersions2).values({
|
|
5263
|
+
id: version2.id,
|
|
5264
|
+
gatewayConfigId: configId,
|
|
5265
|
+
routes: version2.routes,
|
|
5266
|
+
environments: version2.environments,
|
|
5267
|
+
callerIdentification: version2.callerIdentification ?? null,
|
|
5268
|
+
content: version2.content,
|
|
5269
|
+
checksum: version2.checksum,
|
|
5270
|
+
pushedBy: version2.pushedBy ?? null,
|
|
5271
|
+
createdAt: version2.createdAt
|
|
5272
|
+
});
|
|
5273
|
+
const versions = await this.db.select().from(gatewayConfigVersions2).where(eq2(gatewayConfigVersions2.gatewayConfigId, configId)).orderBy(desc2(gatewayConfigVersions2.createdAt), desc2(gatewayConfigVersions2.id));
|
|
5274
|
+
if (versions.length > 5) {
|
|
5275
|
+
const toDelete = versions.slice(5);
|
|
5276
|
+
for (const v of toDelete) {
|
|
5277
|
+
await this.db.delete(gatewayConfigVersions2).where(eq2(gatewayConfigVersions2.id, v.id));
|
|
5278
|
+
}
|
|
5279
|
+
}
|
|
5280
|
+
return version2;
|
|
5281
|
+
}
|
|
5282
|
+
async recordProvision(provision) {
|
|
5283
|
+
await this.db.insert(provisions2).values({
|
|
5284
|
+
id: provision.id,
|
|
5285
|
+
gatewayConfigName: provision.gatewayConfigName,
|
|
5286
|
+
gatewayConfigVersion: provision.gatewayConfigVersion,
|
|
5287
|
+
environment: provision.environment,
|
|
5288
|
+
provider: provision.provider,
|
|
5289
|
+
synced: provision.synced,
|
|
5290
|
+
actor: provision.actor,
|
|
5291
|
+
details: provision.details ?? null,
|
|
5292
|
+
createdAt: provision.createdAt
|
|
5293
|
+
});
|
|
5294
|
+
}
|
|
5295
|
+
async listProvisions(gatewayConfigName) {
|
|
5296
|
+
const rows = gatewayConfigName ? await this.db.select().from(provisions2).where(eq2(provisions2.gatewayConfigName, gatewayConfigName)).orderBy(desc2(provisions2.createdAt)) : await this.db.select().from(provisions2).orderBy(desc2(provisions2.createdAt));
|
|
5297
|
+
return rows.map((r) => ({
|
|
5298
|
+
id: r.id,
|
|
5299
|
+
gatewayConfigName: r.gatewayConfigName,
|
|
5300
|
+
gatewayConfigVersion: r.gatewayConfigVersion,
|
|
5301
|
+
environment: r.environment,
|
|
5302
|
+
provider: r.provider,
|
|
5303
|
+
synced: r.synced,
|
|
5304
|
+
actor: r.actor,
|
|
5305
|
+
details: r.details ?? void 0,
|
|
5306
|
+
createdAt: r.createdAt
|
|
5307
|
+
}));
|
|
5308
|
+
}
|
|
5309
|
+
mapGatewayConfigRow(row) {
|
|
5310
|
+
return {
|
|
5311
|
+
id: row.id,
|
|
5312
|
+
name: row.name,
|
|
5313
|
+
provider: row.provider,
|
|
5314
|
+
specName: row.specName,
|
|
5315
|
+
specSemver: row.specSemver,
|
|
5316
|
+
createdAt: row.createdAt,
|
|
5317
|
+
updatedAt: row.updatedAt
|
|
5318
|
+
};
|
|
5319
|
+
}
|
|
5320
|
+
mapGatewayConfigVersionRow(row) {
|
|
5321
|
+
return {
|
|
5322
|
+
id: row.id,
|
|
5323
|
+
gatewayConfigId: row.gatewayConfigId,
|
|
5324
|
+
routes: row.routes,
|
|
5325
|
+
environments: row.environments,
|
|
5326
|
+
callerIdentification: row.callerIdentification ?? void 0,
|
|
5327
|
+
content: row.content,
|
|
5328
|
+
checksum: row.checksum,
|
|
5329
|
+
pushedBy: row.pushedBy ?? void 0,
|
|
5330
|
+
createdAt: row.createdAt
|
|
5331
|
+
};
|
|
5332
|
+
}
|
|
5333
|
+
async recordGatewayLog(log) {
|
|
5334
|
+
await this.db.insert(httpLogs2).values({
|
|
5335
|
+
id: log.id,
|
|
5336
|
+
provider: log.provider,
|
|
5337
|
+
gatewayConfigName: log.gatewayConfigName,
|
|
5338
|
+
environment: log.environment,
|
|
5339
|
+
method: log.method,
|
|
5340
|
+
path: log.path,
|
|
5341
|
+
routePath: log.routePath ?? null,
|
|
5342
|
+
status: log.status,
|
|
5343
|
+
callerId: log.callerId ?? null,
|
|
5344
|
+
callerSource: log.callerSource ?? null,
|
|
5345
|
+
callerConfidence: log.callerConfidence,
|
|
5346
|
+
occurredAt: log.occurredAt,
|
|
5347
|
+
createdAt: log.createdAt
|
|
5348
|
+
});
|
|
5349
|
+
}
|
|
5350
|
+
async listGatewayLogs(filters) {
|
|
5351
|
+
const limit = filters.limit ?? 50;
|
|
5352
|
+
const offset = filters.offset ?? 0;
|
|
5353
|
+
let query = this.db.select().from(httpLogs2);
|
|
5354
|
+
const conditions = [];
|
|
5355
|
+
if (filters.gatewayConfigName) {
|
|
5356
|
+
conditions.push(eq2(httpLogs2.gatewayConfigName, filters.gatewayConfigName));
|
|
5357
|
+
}
|
|
5358
|
+
if (filters.environment) {
|
|
5359
|
+
conditions.push(eq2(httpLogs2.environment, filters.environment));
|
|
5360
|
+
}
|
|
5361
|
+
if (filters.path) {
|
|
5362
|
+
conditions.push(eq2(httpLogs2.path, filters.path));
|
|
5363
|
+
}
|
|
5364
|
+
if (filters.method) {
|
|
5365
|
+
conditions.push(eq2(httpLogs2.method, filters.method));
|
|
5366
|
+
}
|
|
5367
|
+
if (filters.status !== void 0) {
|
|
5368
|
+
conditions.push(eq2(httpLogs2.status, filters.status));
|
|
5369
|
+
}
|
|
5370
|
+
if (filters.from) {
|
|
5371
|
+
conditions.push(sql2`${httpLogs2.occurredAt} >= ${filters.from}`);
|
|
5372
|
+
}
|
|
5373
|
+
if (filters.to) {
|
|
5374
|
+
conditions.push(sql2`${httpLogs2.occurredAt} <= ${filters.to}`);
|
|
5375
|
+
}
|
|
5376
|
+
if (conditions.length > 0) {
|
|
5377
|
+
query = query.where(and2(...conditions));
|
|
5378
|
+
}
|
|
5379
|
+
const countResult = await this.db.select({ count: sql2`count(*)` }).from(httpLogs2).where(conditions.length > 0 ? and2(...conditions) : void 0);
|
|
5380
|
+
const total = countResult[0]?.count ?? 0;
|
|
5381
|
+
const rows = await query.orderBy(desc2(httpLogs2.occurredAt)).limit(limit).offset(offset);
|
|
5382
|
+
return {
|
|
5383
|
+
logs: rows.map((r) => ({
|
|
5384
|
+
id: r.id,
|
|
5385
|
+
provider: r.provider,
|
|
5386
|
+
gatewayConfigName: r.gatewayConfigName,
|
|
5387
|
+
environment: r.environment,
|
|
5388
|
+
method: r.method,
|
|
5389
|
+
path: r.path,
|
|
5390
|
+
routePath: r.routePath ?? void 0,
|
|
5391
|
+
status: r.status,
|
|
5392
|
+
callerId: r.callerId ?? void 0,
|
|
5393
|
+
callerSource: r.callerSource ?? void 0,
|
|
5394
|
+
callerConfidence: r.callerConfidence,
|
|
5395
|
+
occurredAt: r.occurredAt,
|
|
5396
|
+
createdAt: r.createdAt
|
|
5397
|
+
})),
|
|
5398
|
+
total
|
|
5399
|
+
};
|
|
5400
|
+
}
|
|
5401
|
+
async getGatewayLog(id) {
|
|
5402
|
+
const rows = await this.db.select().from(httpLogs2).where(eq2(httpLogs2.id, id)).limit(1);
|
|
5403
|
+
if (rows.length === 0) return null;
|
|
5404
|
+
const r = rows[0];
|
|
5405
|
+
return {
|
|
5406
|
+
id: r.id,
|
|
5407
|
+
provider: r.provider,
|
|
5408
|
+
gatewayConfigName: r.gatewayConfigName,
|
|
5409
|
+
environment: r.environment,
|
|
5410
|
+
method: r.method,
|
|
5411
|
+
path: r.path,
|
|
5412
|
+
routePath: r.routePath ?? void 0,
|
|
5413
|
+
status: r.status,
|
|
5414
|
+
callerId: r.callerId ?? void 0,
|
|
5415
|
+
callerSource: r.callerSource ?? void 0,
|
|
5416
|
+
callerConfidence: r.callerConfidence,
|
|
5417
|
+
occurredAt: r.occurredAt,
|
|
5418
|
+
createdAt: r.createdAt
|
|
5419
|
+
};
|
|
5420
|
+
}
|
|
5421
|
+
async getGatewayLogStats(_filters) {
|
|
5422
|
+
const conditions = [];
|
|
5423
|
+
if (_filters.gatewayConfigName) {
|
|
5424
|
+
conditions.push(eq2(httpLogs2.gatewayConfigName, _filters.gatewayConfigName));
|
|
5425
|
+
}
|
|
5426
|
+
if (_filters.environment) {
|
|
5427
|
+
conditions.push(eq2(httpLogs2.environment, _filters.environment));
|
|
5428
|
+
}
|
|
5429
|
+
if (_filters.from) {
|
|
5430
|
+
conditions.push(sql2`${httpLogs2.occurredAt} >= ${_filters.from}`);
|
|
5431
|
+
}
|
|
5432
|
+
if (_filters.to) {
|
|
5433
|
+
conditions.push(sql2`${httpLogs2.occurredAt} <= ${_filters.to}`);
|
|
5434
|
+
}
|
|
5435
|
+
const whereClause = conditions.length > 0 ? and2(...conditions) : void 0;
|
|
5436
|
+
const rows = await this.db.select({
|
|
5437
|
+
gatewayConfigName: httpLogs2.gatewayConfigName,
|
|
5438
|
+
environment: httpLogs2.environment,
|
|
5439
|
+
method: httpLogs2.method,
|
|
5440
|
+
routePath: httpLogs2.routePath,
|
|
5441
|
+
lastSeenAt: sql2`max(${httpLogs2.occurredAt})`,
|
|
5442
|
+
totalCalls: sql2`count(*)`,
|
|
5443
|
+
uniqueCallerIds: sql2`count(distinct ${httpLogs2.callerId})`
|
|
5444
|
+
}).from(httpLogs2).where(whereClause).groupBy(httpLogs2.gatewayConfigName, httpLogs2.environment, httpLogs2.method, httpLogs2.routePath);
|
|
5445
|
+
return rows.map((r) => ({
|
|
5446
|
+
gatewayConfigName: r.gatewayConfigName,
|
|
5447
|
+
environment: r.environment,
|
|
5448
|
+
method: r.method,
|
|
5449
|
+
routePath: r.routePath ?? "/",
|
|
5450
|
+
lastSeenAt: new Date(r.lastSeenAt),
|
|
5451
|
+
totalCalls: r.totalCalls,
|
|
5452
|
+
uniqueCallerIds: r.uniqueCallerIds
|
|
5453
|
+
}));
|
|
5454
|
+
}
|
|
5455
|
+
async deleteGatewayLogsOlderThan(days) {
|
|
5456
|
+
const cutoff = /* @__PURE__ */ new Date();
|
|
5457
|
+
cutoff.setDate(cutoff.getDate() - days);
|
|
5458
|
+
await this.db.delete(httpLogs2).where(sql2`${httpLogs2.occurredAt} < ${cutoff}`);
|
|
5459
|
+
}
|
|
5460
|
+
};
|
|
5461
|
+
|
|
4951
5462
|
// src/registry/serve.ts
|
|
4952
5463
|
async function startServer(userConfig) {
|
|
4953
5464
|
const config = { ...defaultConfig, ...userConfig };
|
|
4954
|
-
|
|
4955
|
-
|
|
4956
|
-
|
|
4957
|
-
|
|
4958
|
-
|
|
4959
|
-
|
|
5465
|
+
let store;
|
|
5466
|
+
if (config.database === "postgresql") {
|
|
5467
|
+
if (!config.postgresUrl) {
|
|
5468
|
+
throw new Error("PostgreSQL database requested but no postgresUrl provided.");
|
|
5469
|
+
}
|
|
5470
|
+
store = new PostgreSQLSpecStore(config.postgresUrl);
|
|
5471
|
+
} else {
|
|
5472
|
+
const sqlitePath = config.sqlitePath ?? path9.join(
|
|
5473
|
+
process.env.HOME || process.env.USERPROFILE || ".",
|
|
5474
|
+
".grapity",
|
|
5475
|
+
"registry.db"
|
|
5476
|
+
);
|
|
5477
|
+
const dir = path9.dirname(sqlitePath);
|
|
4960
5478
|
if (!fs9.existsSync(dir)) {
|
|
4961
5479
|
fs9.mkdirSync(dir, { recursive: true });
|
|
4962
5480
|
}
|
|
5481
|
+
store = new SQLiteSpecStore(sqlitePath);
|
|
4963
5482
|
}
|
|
4964
|
-
const store = new SQLiteSpecStore(config.sqlitePath);
|
|
4965
5483
|
await store.migrate();
|
|
4966
5484
|
const app = createApp(config, store);
|
|
4967
|
-
serve({
|
|
5485
|
+
const server = serve({
|
|
4968
5486
|
fetch: app.fetch,
|
|
4969
5487
|
port: config.port
|
|
4970
5488
|
});
|
|
4971
|
-
return app;
|
|
5489
|
+
return { app, store, server };
|
|
4972
5490
|
}
|
|
4973
5491
|
if (process.argv[1] === new URL(import.meta.url).pathname) {
|
|
4974
5492
|
startServer();
|
|
@@ -5036,28 +5554,63 @@ function getPackageVersion() {
|
|
|
5036
5554
|
return "unknown";
|
|
5037
5555
|
}
|
|
5038
5556
|
}
|
|
5557
|
+
function resolveServerConfig(cliOptions, cliConfig) {
|
|
5558
|
+
const envUrl = process.env.GRAPITY_DATABASE_URL;
|
|
5559
|
+
const dbValue = envUrl ?? cliOptions.db;
|
|
5560
|
+
if (dbValue) {
|
|
5561
|
+
if (isPostgresqlUrl(dbValue)) {
|
|
5562
|
+
return { port: cliOptions.port, database: "postgresql", postgresUrl: dbValue };
|
|
5563
|
+
}
|
|
5564
|
+
return { port: cliOptions.port, database: "sqlite", sqlitePath: dbValue };
|
|
5565
|
+
}
|
|
5566
|
+
if (cliConfig.mode === "local") {
|
|
5567
|
+
const local = cliConfig.local;
|
|
5568
|
+
if (local?.database === "postgresql") {
|
|
5569
|
+
if (!local.postgresUrl) {
|
|
5570
|
+
throw new Error(
|
|
5571
|
+
"PostgreSQL is configured but no postgresUrl is set. Run grapity init --local --db postgresql://... or set GRAPITY_DATABASE_URL."
|
|
5572
|
+
);
|
|
5573
|
+
}
|
|
5574
|
+
return { port: cliOptions.port, database: "postgresql", postgresUrl: local.postgresUrl };
|
|
5575
|
+
}
|
|
5576
|
+
return {
|
|
5577
|
+
port: cliOptions.port,
|
|
5578
|
+
database: "sqlite",
|
|
5579
|
+
sqlitePath: local?.sqlitePath ?? path11.join(os4.homedir(), ".grapity", "registry.db")
|
|
5580
|
+
};
|
|
5581
|
+
}
|
|
5582
|
+
return {
|
|
5583
|
+
port: cliOptions.port,
|
|
5584
|
+
database: "sqlite",
|
|
5585
|
+
sqlitePath: path11.join(os4.homedir(), ".grapity", "registry.db")
|
|
5586
|
+
};
|
|
5587
|
+
}
|
|
5039
5588
|
function createServeCommand(_version) {
|
|
5040
|
-
return new Command19("serve").description("Start the local grapity registry server").option("-p, --port <port>", "Port to listen on", "3750").option("--
|
|
5589
|
+
return new Command19("serve").description("Start the local grapity registry server").option("-p, --port <port>", "Port to listen on", "3750").option("--hub-port <port>", "Port for the developer portal (Hub)", "3000").option("--no-hub", "Skip starting the developer portal").option("--db <path-or-url>", "SQLite path or postgresql:// URL").action(async (options) => {
|
|
5041
5590
|
const port = parseInt(options.port, 10);
|
|
5042
5591
|
const hubPort = parseInt(options.hubPort, 10);
|
|
5043
5592
|
const startHub = options.hub !== false;
|
|
5044
|
-
const db = options.db;
|
|
5045
|
-
const auth = options.auth;
|
|
5046
|
-
const isPostgres = db?.startsWith("postgresql://");
|
|
5047
|
-
const dbMode = isPostgres ? "postgresql" : "sqlite";
|
|
5048
|
-
const dbPath = isPostgres ? void 0 : db ?? path11.join(os4.homedir(), ".grapity", "registry.db");
|
|
5049
5593
|
const version2 = getPackageVersion();
|
|
5594
|
+
const cliConfig = getConfig();
|
|
5595
|
+
let serverConfig;
|
|
5596
|
+
try {
|
|
5597
|
+
serverConfig = resolveServerConfig({ port, db: options.db }, cliConfig);
|
|
5598
|
+
} catch (err) {
|
|
5599
|
+
console.error(formatError("invalid config", err.message));
|
|
5600
|
+
process.exit(1);
|
|
5601
|
+
}
|
|
5050
5602
|
console.log(formatHeader("grapity Registry", `v${version2}`));
|
|
5051
5603
|
console.log("");
|
|
5052
|
-
console.log(
|
|
5604
|
+
console.log(
|
|
5605
|
+
formatServeConfig({
|
|
5606
|
+
port,
|
|
5607
|
+
database: serverConfig.database,
|
|
5608
|
+
dbPath: serverConfig.sqlitePath,
|
|
5609
|
+
postgresUrl: serverConfig.postgresUrl
|
|
5610
|
+
})
|
|
5611
|
+
);
|
|
5053
5612
|
console.log("");
|
|
5054
|
-
await startServer(
|
|
5055
|
-
port,
|
|
5056
|
-
database: dbMode,
|
|
5057
|
-
sqlitePath: dbPath,
|
|
5058
|
-
postgresUrl: isPostgres ? db : void 0,
|
|
5059
|
-
auth: auth === "none" ? { mode: "none" } : { mode: auth }
|
|
5060
|
-
});
|
|
5613
|
+
const { store } = await startServer(serverConfig);
|
|
5061
5614
|
console.log(formatReady(port));
|
|
5062
5615
|
if (startHub) {
|
|
5063
5616
|
console.log("");
|
|
@@ -5076,9 +5629,12 @@ function createServeCommand(_version) {
|
|
|
5076
5629
|
});
|
|
5077
5630
|
console.log(formatHubReady(hubPort));
|
|
5078
5631
|
}
|
|
5079
|
-
process.on("SIGINT", () => {
|
|
5632
|
+
process.on("SIGINT", async () => {
|
|
5080
5633
|
console.log("");
|
|
5081
5634
|
console.log(formatShutdown());
|
|
5635
|
+
if ("end" in store && typeof store.end === "function") {
|
|
5636
|
+
await store.end();
|
|
5637
|
+
}
|
|
5082
5638
|
process.exit(0);
|
|
5083
5639
|
});
|
|
5084
5640
|
});
|