@neat.is/core 0.3.8 → 0.4.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/{chunk-7TYESDAI.js → chunk-4V23KYOP.js} +33 -4
- package/dist/chunk-4V23KYOP.js.map +1 -0
- package/dist/{chunk-V4TU7OKZ.js → chunk-IXIFJKMM.js} +2 -2
- package/dist/{chunk-LQ3JFBTX.js → chunk-N6RPINEJ.js} +5 -4
- package/dist/{chunk-LQ3JFBTX.js.map → chunk-N6RPINEJ.js.map} +1 -1
- package/dist/{chunk-CZ3T6TE2.js → chunk-UPW4CMOH.js} +120 -28
- package/dist/chunk-UPW4CMOH.js.map +1 -0
- package/dist/cli.cjs +916 -108
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.cts +2 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +765 -73
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +153 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -4
- package/dist/neatd.cjs +153 -31
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +3 -3
- package/dist/{otel-grpc-S3AENOZ6.js → otel-grpc-GGZHR7VM.js} +3 -3
- package/dist/server.cjs +282 -160
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +5 -4
- package/dist/server.js.map +1 -1
- package/package.json +2 -2
- package/dist/chunk-7TYESDAI.js.map +0 -1
- package/dist/chunk-CZ3T6TE2.js.map +0 -1
- /package/dist/{chunk-V4TU7OKZ.js.map → chunk-IXIFJKMM.js.map} +0 -0
- /package/dist/{otel-grpc-S3AENOZ6.js.map → otel-grpc-GGZHR7VM.js.map} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -55,6 +55,7 @@ function mountBearerAuth(app, opts) {
|
|
|
55
55
|
if (opts.trustProxy) return;
|
|
56
56
|
const expected = Buffer.from(opts.token, "utf8");
|
|
57
57
|
const suffixes = [...DEFAULT_UNAUTH_SUFFIXES, ...opts.extraUnauthenticatedSuffixes ?? []];
|
|
58
|
+
const publicRead = opts.publicRead === true;
|
|
58
59
|
app.addHook("preHandler", (req, reply, done) => {
|
|
59
60
|
const path37 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
|
|
60
61
|
for (const suffix of suffixes) {
|
|
@@ -63,6 +64,10 @@ function mountBearerAuth(app, opts) {
|
|
|
63
64
|
return;
|
|
64
65
|
}
|
|
65
66
|
}
|
|
67
|
+
if (publicRead && PUBLIC_READ_METHODS.has(req.method)) {
|
|
68
|
+
done();
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
66
71
|
const header = req.headers.authorization;
|
|
67
72
|
if (typeof header !== "string" || !header.startsWith("Bearer ")) {
|
|
68
73
|
void reply.code(401).send({ error: "unauthorized" });
|
|
@@ -76,16 +81,21 @@ function mountBearerAuth(app, opts) {
|
|
|
76
81
|
done();
|
|
77
82
|
});
|
|
78
83
|
}
|
|
84
|
+
function parseBoolEnv(v) {
|
|
85
|
+
if (!v) return false;
|
|
86
|
+
return v === "true" || v === "1";
|
|
87
|
+
}
|
|
79
88
|
function readAuthEnv(env = process.env) {
|
|
80
89
|
const t = env.NEAT_AUTH_TOKEN;
|
|
81
90
|
const ot = env.NEAT_OTEL_TOKEN;
|
|
82
91
|
return {
|
|
83
92
|
authToken: t && t.length > 0 ? t : void 0,
|
|
84
93
|
otelToken: ot && ot.length > 0 ? ot : t && t.length > 0 ? t : void 0,
|
|
85
|
-
trustProxy: env.NEAT_AUTH_PROXY === "true"
|
|
94
|
+
trustProxy: env.NEAT_AUTH_PROXY === "true",
|
|
95
|
+
publicRead: parseBoolEnv(env.NEAT_PUBLIC_READ)
|
|
86
96
|
};
|
|
87
97
|
}
|
|
88
|
-
var import_node_crypto, LOOPBACK_HOSTS, BindAuthorityError, DEFAULT_UNAUTH_SUFFIXES;
|
|
98
|
+
var import_node_crypto, LOOPBACK_HOSTS, BindAuthorityError, PUBLIC_READ_METHODS, DEFAULT_UNAUTH_SUFFIXES;
|
|
89
99
|
var init_auth = __esm({
|
|
90
100
|
"src/auth.ts"() {
|
|
91
101
|
"use strict";
|
|
@@ -105,7 +115,13 @@ var init_auth = __esm({
|
|
|
105
115
|
this.name = "BindAuthorityError";
|
|
106
116
|
}
|
|
107
117
|
};
|
|
108
|
-
|
|
118
|
+
PUBLIC_READ_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD", "OPTIONS"]);
|
|
119
|
+
DEFAULT_UNAUTH_SUFFIXES = [
|
|
120
|
+
"/health",
|
|
121
|
+
"/healthz",
|
|
122
|
+
"/readyz",
|
|
123
|
+
"/api/config"
|
|
124
|
+
];
|
|
109
125
|
}
|
|
110
126
|
});
|
|
111
127
|
|
|
@@ -308,6 +324,15 @@ function isoFromUnixNano(nanos) {
|
|
|
308
324
|
return void 0;
|
|
309
325
|
}
|
|
310
326
|
}
|
|
327
|
+
function pickEnv(spanAttrs, resourceAttrs) {
|
|
328
|
+
for (const attrs of [spanAttrs, resourceAttrs]) {
|
|
329
|
+
for (const key of [ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT]) {
|
|
330
|
+
const v = attrs[key];
|
|
331
|
+
if (typeof v === "string" && v.length > 0) return v;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return ENV_FALLBACK;
|
|
335
|
+
}
|
|
311
336
|
function parseOtlpRequest(body) {
|
|
312
337
|
const out = [];
|
|
313
338
|
for (const rs of body.resourceSpans ?? []) {
|
|
@@ -327,6 +352,7 @@ function parseOtlpRequest(body) {
|
|
|
327
352
|
endTimeUnixNano: span.endTimeUnixNano ?? "0",
|
|
328
353
|
startTimeIso: isoFromUnixNano(span.startTimeUnixNano),
|
|
329
354
|
durationNanos: durationNanos(span.startTimeUnixNano, span.endTimeUnixNano),
|
|
355
|
+
env: pickEnv(attrs, resourceAttrs),
|
|
330
356
|
attributes: attrs,
|
|
331
357
|
dbSystem: typeof attrs["db.system"] === "string" ? attrs["db.system"] : void 0,
|
|
332
358
|
dbName: typeof attrs["db.name"] === "string" ? attrs["db.name"] : void 0,
|
|
@@ -473,7 +499,7 @@ function logSpanHandler(span) {
|
|
|
473
499
|
`otel: ${span.service} ${span.name} parent=${parent} status=${status2}${db}`
|
|
474
500
|
);
|
|
475
501
|
}
|
|
476
|
-
var import_node_path35, import_node_url2, import_fastify2, import_protobufjs, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody;
|
|
502
|
+
var import_node_path35, import_node_url2, import_fastify2, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody;
|
|
477
503
|
var init_otel = __esm({
|
|
478
504
|
"src/otel.ts"() {
|
|
479
505
|
"use strict";
|
|
@@ -483,6 +509,9 @@ var init_otel = __esm({
|
|
|
483
509
|
import_fastify2 = __toESM(require("fastify"), 1);
|
|
484
510
|
import_protobufjs = __toESM(require("protobufjs"), 1);
|
|
485
511
|
init_auth();
|
|
512
|
+
ENV_ATTR_CANONICAL = "deployment.environment.name";
|
|
513
|
+
ENV_ATTR_COMPAT = "deployment.environment";
|
|
514
|
+
ENV_FALLBACK = "unknown";
|
|
486
515
|
exportTraceServiceRequestType = null;
|
|
487
516
|
exportTraceServiceResponseType = null;
|
|
488
517
|
cachedProtobufResponseBody = null;
|
|
@@ -1579,52 +1608,64 @@ function cacheSpanService(span, now) {
|
|
|
1579
1608
|
if (!span.traceId || !span.spanId) return;
|
|
1580
1609
|
const key = parentSpanKey(span.traceId, span.spanId);
|
|
1581
1610
|
parentSpanCache.delete(key);
|
|
1582
|
-
parentSpanCache.set(key, {
|
|
1611
|
+
parentSpanCache.set(key, {
|
|
1612
|
+
service: span.service,
|
|
1613
|
+
env: span.env ?? "unknown",
|
|
1614
|
+
expiresAt: now + PARENT_SPAN_CACHE_TTL_MS
|
|
1615
|
+
});
|
|
1583
1616
|
while (parentSpanCache.size > PARENT_SPAN_CACHE_SIZE) {
|
|
1584
1617
|
const oldest = parentSpanCache.keys().next().value;
|
|
1585
1618
|
if (!oldest) break;
|
|
1586
1619
|
parentSpanCache.delete(oldest);
|
|
1587
1620
|
}
|
|
1588
1621
|
}
|
|
1589
|
-
function
|
|
1622
|
+
function lookupParentSpan(traceId, parentSpanId, now) {
|
|
1590
1623
|
const entry = parentSpanCache.get(parentSpanKey(traceId, parentSpanId));
|
|
1591
1624
|
if (!entry) return null;
|
|
1592
1625
|
if (entry.expiresAt <= now) {
|
|
1593
1626
|
parentSpanCache.delete(parentSpanKey(traceId, parentSpanId));
|
|
1594
1627
|
return null;
|
|
1595
1628
|
}
|
|
1596
|
-
return entry.service;
|
|
1629
|
+
return { service: entry.service, env: entry.env };
|
|
1597
1630
|
}
|
|
1598
|
-
function resolveServiceId(graph, host) {
|
|
1599
|
-
const
|
|
1600
|
-
if (graph.hasNode(
|
|
1601
|
-
|
|
1631
|
+
function resolveServiceId(graph, host, env) {
|
|
1632
|
+
const envTagged = (0, import_types3.serviceId)(host, env);
|
|
1633
|
+
if (graph.hasNode(envTagged)) return envTagged;
|
|
1634
|
+
const envLess = (0, import_types3.serviceId)(host);
|
|
1635
|
+
if (envLess !== envTagged && graph.hasNode(envLess)) return envLess;
|
|
1636
|
+
let sameEnv = null;
|
|
1637
|
+
let envLessMatch = null;
|
|
1638
|
+
let anyMatch = null;
|
|
1602
1639
|
graph.forEachNode((id, attrs) => {
|
|
1603
|
-
if (
|
|
1640
|
+
if (sameEnv) return;
|
|
1604
1641
|
const a = attrs;
|
|
1605
1642
|
if (a.type !== import_types3.NodeType.ServiceNode) return;
|
|
1606
|
-
|
|
1607
|
-
|
|
1643
|
+
const matchesByName = a.name === host;
|
|
1644
|
+
const matchesByAlias = a.aliases ? a.aliases.includes(host) : false;
|
|
1645
|
+
if (!matchesByName && !matchesByAlias) return;
|
|
1646
|
+
const nodeEnv = a.env ?? "unknown";
|
|
1647
|
+
if (nodeEnv === env) {
|
|
1648
|
+
sameEnv = id;
|
|
1608
1649
|
return;
|
|
1609
1650
|
}
|
|
1610
|
-
if (
|
|
1611
|
-
|
|
1612
|
-
}
|
|
1651
|
+
if (nodeEnv === "unknown" && !envLessMatch) envLessMatch = id;
|
|
1652
|
+
else if (!anyMatch) anyMatch = id;
|
|
1613
1653
|
});
|
|
1614
|
-
return
|
|
1654
|
+
return sameEnv ?? envLessMatch ?? anyMatch;
|
|
1615
1655
|
}
|
|
1616
1656
|
function frontierIdFor(host) {
|
|
1617
1657
|
return (0, import_types3.frontierId)(host);
|
|
1618
1658
|
}
|
|
1619
|
-
function ensureServiceNode(graph, serviceName) {
|
|
1620
|
-
const id = (0, import_types3.serviceId)(serviceName);
|
|
1659
|
+
function ensureServiceNode(graph, serviceName, env) {
|
|
1660
|
+
const id = (0, import_types3.serviceId)(serviceName, env);
|
|
1621
1661
|
if (graph.hasNode(id)) return id;
|
|
1622
1662
|
const node = {
|
|
1623
1663
|
id,
|
|
1624
1664
|
type: import_types3.NodeType.ServiceNode,
|
|
1625
1665
|
name: serviceName,
|
|
1626
1666
|
language: "unknown",
|
|
1627
|
-
discoveredVia: "otel"
|
|
1667
|
+
discoveredVia: "otel",
|
|
1668
|
+
...env !== "unknown" ? { env } : {}
|
|
1628
1669
|
};
|
|
1629
1670
|
graph.addNode(id, node);
|
|
1630
1671
|
return id;
|
|
@@ -1770,7 +1811,7 @@ function buildErrorEventForReceiver(span) {
|
|
|
1770
1811
|
...span.exception?.type ? { exceptionType: span.exception.type } : {},
|
|
1771
1812
|
...span.exception?.stacktrace ? { exceptionStacktrace: span.exception.stacktrace } : {},
|
|
1772
1813
|
...Object.keys(attrs).length > 0 ? { attributes: attrs } : {},
|
|
1773
|
-
affectedNode: (0, import_types3.serviceId)(span.service)
|
|
1814
|
+
affectedNode: (0, import_types3.serviceId)(span.service, span.env)
|
|
1774
1815
|
};
|
|
1775
1816
|
}
|
|
1776
1817
|
function makeErrorSpanWriter(errorsPath) {
|
|
@@ -1784,7 +1825,8 @@ function makeErrorSpanWriter(errorsPath) {
|
|
|
1784
1825
|
async function handleSpan(ctx, span) {
|
|
1785
1826
|
const ts = span.startTimeIso ?? nowIso(ctx);
|
|
1786
1827
|
const nowMs = ctx.now ? ctx.now() : Date.now();
|
|
1787
|
-
const
|
|
1828
|
+
const env = span.env ?? "unknown";
|
|
1829
|
+
const sourceId = ensureServiceNode(ctx.graph, span.service, env);
|
|
1788
1830
|
const isError = span.statusCode === 2;
|
|
1789
1831
|
cacheSpanService(span, nowMs);
|
|
1790
1832
|
let affectedNode = sourceId;
|
|
@@ -1807,7 +1849,7 @@ async function handleSpan(ctx, span) {
|
|
|
1807
1849
|
const host = pickAddress(span);
|
|
1808
1850
|
let resolvedViaAddress = false;
|
|
1809
1851
|
if (host && host !== span.service) {
|
|
1810
|
-
const targetId = resolveServiceId(ctx.graph, host);
|
|
1852
|
+
const targetId = resolveServiceId(ctx.graph, host, env);
|
|
1811
1853
|
if (targetId && targetId !== sourceId) {
|
|
1812
1854
|
upsertObservedEdge(
|
|
1813
1855
|
ctx.graph,
|
|
@@ -1834,9 +1876,9 @@ async function handleSpan(ctx, span) {
|
|
|
1834
1876
|
}
|
|
1835
1877
|
}
|
|
1836
1878
|
if (!resolvedViaAddress && span.parentSpanId) {
|
|
1837
|
-
const
|
|
1838
|
-
if (
|
|
1839
|
-
const parentId = ensureServiceNode(ctx.graph,
|
|
1879
|
+
const parent = lookupParentSpan(span.traceId, span.parentSpanId, nowMs);
|
|
1880
|
+
if (parent && parent.service !== span.service) {
|
|
1881
|
+
const parentId = ensureServiceNode(ctx.graph, parent.service, parent.env);
|
|
1840
1882
|
upsertObservedEdge(
|
|
1841
1883
|
ctx.graph,
|
|
1842
1884
|
import_types3.EdgeType.CALLS,
|
|
@@ -2032,6 +2074,28 @@ async function readErrorEvents(errorsPath) {
|
|
|
2032
2074
|
throw err;
|
|
2033
2075
|
}
|
|
2034
2076
|
}
|
|
2077
|
+
function mergeSnapshot(graph, snapshot) {
|
|
2078
|
+
const exported = snapshot.graph;
|
|
2079
|
+
let nodesAdded = 0;
|
|
2080
|
+
let edgesAdded = 0;
|
|
2081
|
+
for (const node of exported.nodes ?? []) {
|
|
2082
|
+
if (graph.hasNode(node.key)) continue;
|
|
2083
|
+
if (!node.attributes) continue;
|
|
2084
|
+
graph.addNode(node.key, node.attributes);
|
|
2085
|
+
nodesAdded++;
|
|
2086
|
+
}
|
|
2087
|
+
for (const edge of exported.edges ?? []) {
|
|
2088
|
+
const attrs = edge.attributes;
|
|
2089
|
+
if (!attrs) continue;
|
|
2090
|
+
const id = edge.key ?? attrs.id;
|
|
2091
|
+
if (!id) continue;
|
|
2092
|
+
if (graph.hasEdge(id)) continue;
|
|
2093
|
+
if (!graph.hasNode(edge.source) || !graph.hasNode(edge.target)) continue;
|
|
2094
|
+
graph.addEdgeWithKey(id, edge.source, edge.target, attrs);
|
|
2095
|
+
edgesAdded++;
|
|
2096
|
+
}
|
|
2097
|
+
return { nodesAdded, edgesAdded };
|
|
2098
|
+
}
|
|
2035
2099
|
|
|
2036
2100
|
// src/extract/services.ts
|
|
2037
2101
|
init_cjs_shims();
|
|
@@ -2428,6 +2492,18 @@ async function expandWorkspaceGlobs(scanPath, globs) {
|
|
|
2428
2492
|
}
|
|
2429
2493
|
return [...found];
|
|
2430
2494
|
}
|
|
2495
|
+
function detectJsFramework(pkg) {
|
|
2496
|
+
const deps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
2497
|
+
if (deps["next"] !== void 0) return "next";
|
|
2498
|
+
if (deps["remix"] !== void 0) return "remix";
|
|
2499
|
+
for (const k of Object.keys(deps)) {
|
|
2500
|
+
if (k.startsWith("@remix-run/")) return "remix";
|
|
2501
|
+
}
|
|
2502
|
+
if (deps["@sveltejs/kit"] !== void 0) return "sveltekit";
|
|
2503
|
+
if (deps["nuxt"] !== void 0) return "nuxt";
|
|
2504
|
+
if (deps["astro"] !== void 0) return "astro";
|
|
2505
|
+
return void 0;
|
|
2506
|
+
}
|
|
2431
2507
|
async function discoverNodeService(scanPath, dir) {
|
|
2432
2508
|
const pkgPath = import_node_path8.default.join(dir, "package.json");
|
|
2433
2509
|
if (!await exists(pkgPath)) return null;
|
|
@@ -2439,6 +2515,7 @@ async function discoverNodeService(scanPath, dir) {
|
|
|
2439
2515
|
return null;
|
|
2440
2516
|
}
|
|
2441
2517
|
if (!pkg.name) return null;
|
|
2518
|
+
const framework = detectJsFramework(pkg);
|
|
2442
2519
|
const node = {
|
|
2443
2520
|
id: (0, import_types5.serviceId)(pkg.name),
|
|
2444
2521
|
type: import_types5.NodeType.ServiceNode,
|
|
@@ -2447,7 +2524,8 @@ async function discoverNodeService(scanPath, dir) {
|
|
|
2447
2524
|
version: pkg.version,
|
|
2448
2525
|
dependencies: pkg.dependencies ?? {},
|
|
2449
2526
|
repoPath: import_node_path8.default.relative(scanPath, dir),
|
|
2450
|
-
...pkg.engines?.node ? { nodeEngine: pkg.engines.node } : {}
|
|
2527
|
+
...pkg.engines?.node ? { nodeEngine: pkg.engines.node } : {},
|
|
2528
|
+
...framework ? { framework } : {}
|
|
2451
2529
|
};
|
|
2452
2530
|
return { pkg, dir, node };
|
|
2453
2531
|
}
|
|
@@ -4230,7 +4308,7 @@ init_cjs_shims();
|
|
|
4230
4308
|
var import_node_fs18 = require("fs");
|
|
4231
4309
|
var import_node_path31 = __toESM(require("path"), 1);
|
|
4232
4310
|
var import_types19 = require("@neat.is/types");
|
|
4233
|
-
var SCHEMA_VERSION =
|
|
4311
|
+
var SCHEMA_VERSION = 4;
|
|
4234
4312
|
function migrateV1ToV2(payload) {
|
|
4235
4313
|
const nodes = payload.graph.nodes;
|
|
4236
4314
|
if (Array.isArray(nodes)) {
|
|
@@ -4242,6 +4320,9 @@ function migrateV1ToV2(payload) {
|
|
|
4242
4320
|
}
|
|
4243
4321
|
return { ...payload, schemaVersion: 2 };
|
|
4244
4322
|
}
|
|
4323
|
+
function migrateV3ToV4(payload) {
|
|
4324
|
+
return { ...payload, schemaVersion: 4 };
|
|
4325
|
+
}
|
|
4245
4326
|
function migrateV2ToV3(payload) {
|
|
4246
4327
|
const edges = payload.graph.edges;
|
|
4247
4328
|
if (Array.isArray(edges)) {
|
|
@@ -4290,6 +4371,9 @@ async function loadGraphFromDisk(graph, outPath) {
|
|
|
4290
4371
|
if (payload.schemaVersion === 2) {
|
|
4291
4372
|
payload = migrateV2ToV3(payload);
|
|
4292
4373
|
}
|
|
4374
|
+
if (payload.schemaVersion === 3) {
|
|
4375
|
+
payload = migrateV3ToV4(payload);
|
|
4376
|
+
}
|
|
4293
4377
|
if (payload.schemaVersion !== SCHEMA_VERSION) {
|
|
4294
4378
|
throw new Error(
|
|
4295
4379
|
`persist: unsupported snapshot schemaVersion ${payload.schemaVersion} (expected ${SCHEMA_VERSION})`
|
|
@@ -5201,6 +5285,35 @@ function registerRoutes(scope, ctx) {
|
|
|
5201
5285
|
}
|
|
5202
5286
|
}
|
|
5203
5287
|
);
|
|
5288
|
+
scope.post("/snapshot", async (req, reply) => {
|
|
5289
|
+
const proj = resolveProject(registry, req, reply);
|
|
5290
|
+
if (!proj) return;
|
|
5291
|
+
const body = req.body;
|
|
5292
|
+
if (!body || typeof body !== "object" || !body.snapshot) {
|
|
5293
|
+
return reply.code(400).send({ error: "request body must be { snapshot: <persisted-graph> }" });
|
|
5294
|
+
}
|
|
5295
|
+
const snap = body.snapshot;
|
|
5296
|
+
if (typeof snap.schemaVersion !== "number" || snap.schemaVersion !== SCHEMA_VERSION) {
|
|
5297
|
+
return reply.code(400).send({
|
|
5298
|
+
error: `unsupported snapshot schemaVersion ${snap.schemaVersion} (expected ${SCHEMA_VERSION})`
|
|
5299
|
+
});
|
|
5300
|
+
}
|
|
5301
|
+
try {
|
|
5302
|
+
const result = mergeSnapshot(proj.graph, snap);
|
|
5303
|
+
return {
|
|
5304
|
+
project: proj.name,
|
|
5305
|
+
nodesAdded: result.nodesAdded,
|
|
5306
|
+
edgesAdded: result.edgesAdded,
|
|
5307
|
+
nodeCount: proj.graph.order,
|
|
5308
|
+
edgeCount: proj.graph.size
|
|
5309
|
+
};
|
|
5310
|
+
} catch (err) {
|
|
5311
|
+
return reply.code(400).send({
|
|
5312
|
+
error: "snapshot merge failed",
|
|
5313
|
+
details: err.message
|
|
5314
|
+
});
|
|
5315
|
+
}
|
|
5316
|
+
});
|
|
5204
5317
|
scope.post("/graph/scan", async (req, reply) => {
|
|
5205
5318
|
const proj = resolveProject(registry, req, reply);
|
|
5206
5319
|
if (!proj) return;
|
|
@@ -5294,7 +5407,15 @@ function registerRoutes(scope, ctx) {
|
|
|
5294
5407
|
async function buildApi(opts) {
|
|
5295
5408
|
const app = (0, import_fastify.default)({ logger: false });
|
|
5296
5409
|
await app.register(import_cors.default, { origin: true });
|
|
5297
|
-
mountBearerAuth(app, {
|
|
5410
|
+
mountBearerAuth(app, {
|
|
5411
|
+
token: opts.authToken,
|
|
5412
|
+
trustProxy: opts.trustProxy,
|
|
5413
|
+
publicRead: opts.publicRead
|
|
5414
|
+
});
|
|
5415
|
+
app.get("/api/config", async () => ({
|
|
5416
|
+
publicRead: opts.publicRead === true,
|
|
5417
|
+
authProxy: opts.trustProxy === true
|
|
5418
|
+
}));
|
|
5298
5419
|
const startedAt = opts.startedAt ?? Date.now();
|
|
5299
5420
|
const registry = buildLegacyRegistry(opts);
|
|
5300
5421
|
const legacyErrorsExplicit = !opts.projects && opts.errorsPath !== void 0;
|
|
@@ -5578,7 +5699,8 @@ async function startDaemon(opts = {}) {
|
|
|
5578
5699
|
restApp = await buildApi({
|
|
5579
5700
|
projects: registry,
|
|
5580
5701
|
authToken: auth.authToken,
|
|
5581
|
-
trustProxy: auth.trustProxy
|
|
5702
|
+
trustProxy: auth.trustProxy,
|
|
5703
|
+
publicRead: auth.publicRead
|
|
5582
5704
|
});
|
|
5583
5705
|
restAddress = await restApp.listen({ port: restPort, host });
|
|
5584
5706
|
console.log(`neatd: REST listening on ${restAddress}`);
|