querysub 0.460.0 → 0.461.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/package.json +2 -2
- package/src/-b-authorities/dnsAuthority.ts +23 -15
- package/src/-g-core-values/NodeCapabilities.ts +3 -0
- package/src/-h-path-value-serialize/PathValueSerializer.ts +11 -3
- package/src/0-path-value-core/PathRouter.ts +6 -0
- package/src/0-path-value-core/PathWatcher.ts +1 -1
- package/src/0-path-value-core/pathValueCore.ts +4 -7
- package/src/1-path-client/RemoteWatcher.ts +8 -3
- package/src/1-path-client/pathValueClientWatcher.ts +3 -0
- package/src/2-proxy/PathValueProxyWatcher.ts +1 -1
- package/src/2-proxy/TransactionDelayer.ts +1 -1
- package/src/3-path-functions/PathFunctionHelpers.ts +13 -8
- package/src/3-path-functions/PathFunctionRunner.ts +2 -0
- package/src/4-querysub/Querysub.ts +0 -1
- package/src/4-querysub/QuerysubController.ts +1 -7
- package/src/config.ts +6 -0
- package/src/config2.ts +7 -1
- package/src/deployManager/components/MachinePicker.tsx +40 -0
- package/src/deployManager/components/ServiceDetailPage.tsx +2 -5
- package/src/deployManager/components/ServicesListPage.tsx +2 -0
- package/src/deployManager/components/Tools.tsx +165 -0
- package/src/deployManager/setupMachineMain.ts +65 -23
- package/src/diagnostics/charts/Chart.tsx +240 -0
- package/src/diagnostics/grossStats/GrossStatsPage.tsx +48 -83
- package/src/diagnostics/logs/IndexedLogs/IndexedLogs.ts +3 -3
- package/src/diagnostics/logs/IndexedLogs/MCPIndexedLogs.ts +18 -3
- package/src/diagnostics/logs/IndexedLogs/MCPIndexedLogsEntry.ts +1 -0
- package/src/diagnostics/managementPages.tsx +58 -58
- package/src/diagnostics/misc-pages/DNSPage.tsx +344 -0
- package/test.ts +29 -170
- package/src/diagnostics/AuditLogPage.tsx +0 -147
- package/src/diagnostics/NodeConnectionsPage.tsx +0 -167
package/test.ts
CHANGED
|
@@ -1,197 +1,56 @@
|
|
|
1
1
|
import { chdir } from "process";
|
|
2
2
|
chdir("D:/repos/qs-cyoa/");
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
setIsPublic(
|
|
4
|
+
import { setIsPublic } from "./src/config";
|
|
5
|
+
setIsPublic(false);
|
|
6
6
|
|
|
7
7
|
import "./inject";
|
|
8
8
|
import { Querysub } from "./src/4-querysub/Querysub";
|
|
9
9
|
import { getLoggers2Async, LogDatum } from "./src/diagnostics/logs/diskLogger";
|
|
10
|
-
import { IndexedLogs } from "./src/diagnostics/logs/IndexedLogs/IndexedLogs";
|
|
11
10
|
import { SearchParams } from "./src/diagnostics/logs/IndexedLogs/BufferIndexHelpers";
|
|
12
|
-
import { formatDateTimeDetailed,
|
|
11
|
+
import { formatDateTimeDetailed, formatTime } from "socket-function/src/formatting/format";
|
|
13
12
|
|
|
14
|
-
// Pulled verbatim from the URLs the user shared.
|
|
15
13
|
const START_TIME = 1779598800000;
|
|
16
14
|
const END_TIME = 1779604200000;
|
|
17
15
|
const LIMIT = 1600;
|
|
18
|
-
const
|
|
19
|
-
const
|
|
16
|
+
const SEARCH = `wvupofthbgq & "__threadId":"1f72e0ea774fcc81"`;
|
|
17
|
+
const NODE_ID = "46456fa53a7392cf.a794fbcf7b104c68.querysubtest.com:44173";
|
|
20
18
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
logger: string;
|
|
24
|
-
datum: LogDatum;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
// Stable identity for cross-query comparison. `time` alone isn't unique (many
|
|
28
|
-
// entries share the same ms), so we fold in threadId + entry text.
|
|
29
|
-
function emitKey(e: Emit): string {
|
|
30
|
-
return `${e.time}|${e.datum.__threadId ?? ""}|${e.datum.__entry ?? ""}|${e.datum.param0 ?? ""}`;
|
|
31
|
-
}
|
|
19
|
+
async function main() {
|
|
20
|
+
await Querysub.hostService("test");
|
|
32
21
|
|
|
33
|
-
async function runQuery(label: string, searchText: string, limit: number = LIMIT): Promise<Emit[]> {
|
|
34
|
-
console.log(`\n=== ${label}: ${JSON.stringify(searchText)} (limit=${limit}) ===`);
|
|
35
22
|
let loggers = await getLoggers2Async();
|
|
36
|
-
let
|
|
37
|
-
{ name: "info", logger: loggers.infoLogs },
|
|
38
|
-
];
|
|
23
|
+
let infoLogs = loggers.infoLogs;
|
|
39
24
|
|
|
40
25
|
let params: SearchParams = {
|
|
41
26
|
startTime: START_TIME,
|
|
42
27
|
endTime: END_TIME,
|
|
43
|
-
limit,
|
|
44
|
-
findBuffer: Buffer.from(
|
|
28
|
+
limit: LIMIT,
|
|
29
|
+
findBuffer: Buffer.from(SEARCH, "utf8"),
|
|
45
30
|
searchFromStart: true,
|
|
46
|
-
|
|
31
|
+
forceReadProduction: true,
|
|
47
32
|
};
|
|
48
33
|
|
|
49
|
-
let
|
|
50
|
-
let
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
let earliest = perLoggerEmits.length > 0 ? Math.min(...perLoggerEmits.map(e => e.time)) : undefined;
|
|
63
|
-
let latest = perLoggerEmits.length > 0 ? Math.max(...perLoggerEmits.map(e => e.time)) : undefined;
|
|
64
|
-
console.log(
|
|
65
|
-
` [${name}] emits=${perLoggerEmits.length} ` +
|
|
66
|
-
`matchCount=${result.matchCount} ` +
|
|
67
|
-
`blocksChecked=${result.blockCheckedCount}/${result.totalBlockCount} ` +
|
|
68
|
-
`filesScanned=${result.backblazeFilesSearched}/${result.totalBackblazeFiles} ` +
|
|
69
|
-
`earliest=${earliest !== undefined ? formatDateTimeDetailed(earliest) : "—"} ` +
|
|
70
|
-
`latest=${latest !== undefined ? formatDateTimeDetailed(latest) : "—"} ` +
|
|
71
|
-
`time=${formatTime(Date.now() - loggerStart)}`
|
|
72
|
-
);
|
|
73
|
-
allEmits.push(...perLoggerEmits);
|
|
74
|
-
}));
|
|
75
|
-
|
|
76
|
-
console.log(` total emits=${allEmits.length} in ${formatTime(Date.now() - queryStart)}`);
|
|
77
|
-
|
|
78
|
-
// Sort + trim to limit (mirroring the client-side display).
|
|
79
|
-
allEmits.sort((a, b) => a.time - b.time);
|
|
80
|
-
if (allEmits.length > limit) allEmits.length = limit;
|
|
81
|
-
|
|
34
|
+
let emits: LogDatum[] = [];
|
|
35
|
+
let start = Date.now();
|
|
36
|
+
let result = await infoLogs.clientFind({
|
|
37
|
+
params,
|
|
38
|
+
nodeId: NODE_ID,
|
|
39
|
+
onResult: (match) => {
|
|
40
|
+
emits.push(match);
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
let elapsed = Date.now() - start;
|
|
44
|
+
let earliest = emits.length > 0 ? Math.min(...emits.map(e => e.time)) : undefined;
|
|
45
|
+
let latest = emits.length > 0 ? Math.max(...emits.map(e => e.time)) : undefined;
|
|
82
46
|
console.log(
|
|
83
|
-
`
|
|
84
|
-
`
|
|
85
|
-
`
|
|
47
|
+
`emits=${emits.length} matchCount=${result?.matchCount} ` +
|
|
48
|
+
`blocksChecked=${result?.blockCheckedCount}/${result?.totalBlockCount} ` +
|
|
49
|
+
`filesScanned=${result?.backblazeFilesSearched}/${result?.totalBackblazeFiles} ` +
|
|
50
|
+
`earliest=${earliest !== undefined ? formatDateTimeDetailed(earliest) : "—"} ` +
|
|
51
|
+
`latest=${latest !== undefined ? formatDateTimeDetailed(latest) : "—"} ` +
|
|
52
|
+
`time=${formatTime(elapsed)}`
|
|
86
53
|
);
|
|
87
|
-
return allEmits;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
async function main() {
|
|
91
|
-
await Querysub.hostService("test");
|
|
92
|
-
|
|
93
|
-
// Dump every info file in range — declared startTime / endTime — so we
|
|
94
|
-
// can spot files whose declared range disagrees with the entries inside.
|
|
95
|
-
let loggers = await getLoggers2Async();
|
|
96
|
-
let paths = await loggers.infoLogs.getPaths({
|
|
97
|
-
startTime: START_TIME,
|
|
98
|
-
endTime: END_TIME,
|
|
99
|
-
only: "public",
|
|
100
|
-
});
|
|
101
|
-
paths.sort((a, b) => a.startTime - b.startTime);
|
|
102
|
-
console.log(`\n=== INFO FILES IN RANGE (${paths.length}) — declared ranges ===`);
|
|
103
|
-
for (let p of paths) {
|
|
104
|
-
console.log(` start=${formatDateTimeDetailed(p.startTime)} end=${formatDateTimeDetailed(p.endTime)} logCount=${p.logCount ?? "?"} ${p.fullPath}`);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
let broad = await runQuery("BROAD", SEARCH_BROAD);
|
|
108
|
-
let narrow = await runQuery("NARROW", SEARCH_NARROW);
|
|
109
|
-
// Sanity: run broad again with a huge limit. If those 4 entries appear
|
|
110
|
-
// here but not in the limit=1600 run, the per-file `stopIterating` cap is
|
|
111
|
-
// skipping the block that contains them. If they're still missing, the
|
|
112
|
-
// bug is upstream (index pre-filter or block scanner missing them).
|
|
113
|
-
let broadHuge = await runQuery("BROAD_HUGE", SEARCH_BROAD, 1_000_000);
|
|
114
|
-
|
|
115
|
-
// The diagnostic: narrow ⊂ broad by definition. So every narrow result
|
|
116
|
-
// whose time falls inside broad's kept window MUST appear in broad. If any
|
|
117
|
-
// are missing, the broad scan dropped them — that's the bug.
|
|
118
|
-
if (broad.length === 0 || narrow.length === 0) {
|
|
119
|
-
console.log(`\nSkipping comparison: broad=${broad.length} narrow=${narrow.length}`);
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
let broadKeys = new Set(broad.map(emitKey));
|
|
124
|
-
let broadCutoff = broad[broad.length - 1].time;
|
|
125
|
-
let broadEarliest = broad[0].time;
|
|
126
|
-
|
|
127
|
-
console.log(`\n=== COMPARE ===`);
|
|
128
|
-
console.log(`broad window: [${formatDateTimeDetailed(broadEarliest)}, ${formatDateTimeDetailed(broadCutoff)}]`);
|
|
129
|
-
|
|
130
|
-
let narrowInWindow = narrow.filter(n => n.time <= broadCutoff);
|
|
131
|
-
let missing = narrowInWindow.filter(n => !broadKeys.has(emitKey(n)));
|
|
132
|
-
|
|
133
|
-
console.log(`narrow total: ${narrow.length}`);
|
|
134
|
-
console.log(`narrow within broad window (<= broad cutoff): ${narrowInWindow.length}`);
|
|
135
|
-
console.log(`narrow missing from broad kept top-K: ${missing.length}`);
|
|
136
|
-
|
|
137
|
-
if (missing.length > 0) {
|
|
138
|
-
console.log(`\nFirst ${Math.min(20, missing.length)} missing entries (these prove broad dropped them):`);
|
|
139
|
-
for (let m of missing.slice(0, 20)) {
|
|
140
|
-
console.log(
|
|
141
|
-
` time=${formatDateTimeDetailed(m.time)} ` +
|
|
142
|
-
`logger=${m.logger} ` +
|
|
143
|
-
`entry=${(m.datum.__entry ?? "").slice(0, 80)} ` +
|
|
144
|
-
`param0=${String(m.datum.param0 ?? "").slice(0, 80)}`
|
|
145
|
-
);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Group missing by logger so we can tell which scan dropped them.
|
|
149
|
-
let byLogger = new Map<string, number>();
|
|
150
|
-
for (let m of missing) byLogger.set(m.logger, (byLogger.get(m.logger) ?? 0) + 1);
|
|
151
|
-
console.log(`\nMissing by logger:`);
|
|
152
|
-
for (let [k, v] of byLogger) console.log(` ${k}: ${formatNumber(v)}`);
|
|
153
|
-
} else {
|
|
154
|
-
console.log(`\nNo missing entries — broad correctly contains all narrow results in its window.`);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// For each missing entry, identify the file whose declared range *should*
|
|
158
|
-
// cover its time, and find the file whose declared range *doesn't* cover
|
|
159
|
-
// it (the bug indicator).
|
|
160
|
-
if (missing.length > 0) {
|
|
161
|
-
let missingTimes = missing.map(m => m.time);
|
|
162
|
-
let minMissing = Math.min(...missingTimes);
|
|
163
|
-
let maxMissing = Math.max(...missingTimes);
|
|
164
|
-
console.log(`\nMissing entry times span: [${formatDateTimeDetailed(minMissing)}, ${formatDateTimeDetailed(maxMissing)}]`);
|
|
165
|
-
console.log(`Files whose declared range overlaps [${formatDateTimeDetailed(minMissing)}, ${formatDateTimeDetailed(maxMissing)}]:`);
|
|
166
|
-
for (let p of paths) {
|
|
167
|
-
if (p.endTime < minMissing || p.startTime > maxMissing) continue;
|
|
168
|
-
console.log(` start=${formatDateTimeDetailed(p.startTime)} end=${formatDateTimeDetailed(p.endTime)} ${p.fullPath}`);
|
|
169
|
-
}
|
|
170
|
-
// Also: any file whose declared startTime > broad cutoff (and so would
|
|
171
|
-
// be isSourceRelevant-pruned) — these are candidates for the bug.
|
|
172
|
-
console.log(`Files whose declared startTime > broad cutoff (${formatDateTimeDetailed(broadCutoff)}) — these would be pruned by isSourceRelevant once broad fills:`);
|
|
173
|
-
for (let p of paths) {
|
|
174
|
-
if (p.startTime > broadCutoff) {
|
|
175
|
-
let overlap = p.startTime <= maxMissing && p.endTime >= minMissing ? " <- OVERLAPS MISSING" : "";
|
|
176
|
-
console.log(` start=${formatDateTimeDetailed(p.startTime)} end=${formatDateTimeDetailed(p.endTime)} ${p.fullPath}${overlap}`);
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// Check whether broad-with-huge-limit catches the missing entries.
|
|
182
|
-
let broadHugeKeys = new Set(broadHuge.map(emitKey));
|
|
183
|
-
let stillMissingFromHuge = narrow.filter(n => !broadHugeKeys.has(emitKey(n)));
|
|
184
|
-
console.log(`\n=== BROAD_HUGE check ===`);
|
|
185
|
-
console.log(`narrow missing from broad_huge: ${stillMissingFromHuge.length}`);
|
|
186
|
-
if (stillMissingFromHuge.length > 0) {
|
|
187
|
-
console.log(`First ${Math.min(20, stillMissingFromHuge.length)} still-missing:`);
|
|
188
|
-
for (let m of stillMissingFromHuge.slice(0, 20)) {
|
|
189
|
-
console.log(` time=${formatDateTimeDetailed(m.time)} param0=${String(m.datum.param0 ?? "").slice(0, 60)}`);
|
|
190
|
-
}
|
|
191
|
-
console.log(`-> bug is NOT (only) the per-file cap; the scanner / index pre-filter is dropping entries even without the cap.`);
|
|
192
|
-
} else {
|
|
193
|
-
console.log(`-> all narrow entries are in broad_huge; the per-file stopIterating cap is the culprit for the limit=1600 miss.`);
|
|
194
|
-
}
|
|
195
54
|
}
|
|
196
55
|
|
|
197
56
|
main().catch(e => console.error((e as Error).stack ?? e))
|
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
import { SocketFunction } from "socket-function/SocketFunction";
|
|
2
|
-
import { qreact } from "../4-dom/qreact";
|
|
3
|
-
import { assertIsManagementUser } from "./managementPages";
|
|
4
|
-
import { css } from "typesafecss";
|
|
5
|
-
import { getSyncedController } from "../library-components/SyncedController";
|
|
6
|
-
import { DebugLog, DebugLogController, getLogHistoryEquals, getLogHistoryIncludes } from "../0-path-value-core/auditLogs";
|
|
7
|
-
import { remoteWatcher } from "../1-path-client/RemoteWatcher";
|
|
8
|
-
import { getBrowserUrlNode, getOwnNodeId } from "../-f-node-discovery/NodeDiscovery";
|
|
9
|
-
import { nextId, sort } from "socket-function/src/misc";
|
|
10
|
-
import { t } from "../2-proxy/schema2";
|
|
11
|
-
import { InputLabelURL } from "../library-components/InputLabel";
|
|
12
|
-
import { URLParam } from "../library-components/URLParam";
|
|
13
|
-
import { Table } from "../5-diagnostics/Table";
|
|
14
|
-
import { ObjectDisplay } from "./logs/ObjectDisplay";
|
|
15
|
-
import { PathRouter } from "../0-path-value-core/PathRouter";
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
TODO:
|
|
19
|
-
1) Support watching multiple paths at once
|
|
20
|
-
2) Show extra metadata for the paths
|
|
21
|
-
- Current value (raw, as in, don't sync it)
|
|
22
|
-
- If it is synced
|
|
23
|
-
- The authority of it
|
|
24
|
-
- An expander which shows a table of the full history of values, with valid state, etc
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
type DebugLogWithSource = DebugLog & {
|
|
28
|
-
sourceType: "local" | "remote";
|
|
29
|
-
sourceNodeId: string;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
let path = new URLParam("path", "");
|
|
33
|
-
let includeDescendants = new URLParam("includeDescendants", false);
|
|
34
|
-
export class AuditLogPage extends qreact.Component {
|
|
35
|
-
loadId = nextId();
|
|
36
|
-
state = t.state({
|
|
37
|
-
loadedId: t.string
|
|
38
|
-
});
|
|
39
|
-
render() {
|
|
40
|
-
let logs: DebugLogWithSource[] = [];
|
|
41
|
-
let loaded = this.loadId === this.state.loadedId;
|
|
42
|
-
if (loaded) {
|
|
43
|
-
let obj = auditLogController(getBrowserUrlNode()).getPathHistory({
|
|
44
|
-
path: path.value,
|
|
45
|
-
includeDescendants: includeDescendants.value,
|
|
46
|
-
});
|
|
47
|
-
if (obj) {
|
|
48
|
-
logs = obj.fullLogs;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
return (
|
|
52
|
-
<div class={css.pad2(10).vbox(10).fillWidth}>
|
|
53
|
-
<InputLabelURL
|
|
54
|
-
label="Path"
|
|
55
|
-
url={path}
|
|
56
|
-
fillWidth
|
|
57
|
-
onChange={() => this.loadId = nextId()}
|
|
58
|
-
/>
|
|
59
|
-
<InputLabelURL
|
|
60
|
-
label="Include Descendants"
|
|
61
|
-
url={includeDescendants}
|
|
62
|
-
checkbox
|
|
63
|
-
onChange={() => this.loadId = nextId()}
|
|
64
|
-
/>
|
|
65
|
-
{!loaded && <button onClick={() => this.state.loadedId = this.loadId}>Load</button>}
|
|
66
|
-
<Table
|
|
67
|
-
rows={logs}
|
|
68
|
-
columns={{
|
|
69
|
-
type: {},
|
|
70
|
-
time: {},
|
|
71
|
-
sourceType: {},
|
|
72
|
-
sourceNodeId: {},
|
|
73
|
-
values: {
|
|
74
|
-
formatter(value) {
|
|
75
|
-
return <ObjectDisplay value={value} />;
|
|
76
|
-
},
|
|
77
|
-
},
|
|
78
|
-
}}
|
|
79
|
-
/>
|
|
80
|
-
</div>
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
class AuditLogControllerBase {
|
|
87
|
-
public async getPathHistory(config: {
|
|
88
|
-
path: string;
|
|
89
|
-
includeDescendants?: boolean;
|
|
90
|
-
}) {
|
|
91
|
-
let { path, includeDescendants } = config;
|
|
92
|
-
let authorityNodeId = remoteWatcher.getExistingWatchRemoteNodeId(path);
|
|
93
|
-
if (!authorityNodeId) {
|
|
94
|
-
authorityNodeId = await PathRouter.getReadyAuthority(path)?.nodeId;
|
|
95
|
-
}
|
|
96
|
-
let logs: DebugLog[] = [];
|
|
97
|
-
let remoteLogs: DebugLog[] = [];
|
|
98
|
-
if (includeDescendants) {
|
|
99
|
-
// Also look for the parent, so we can capture keys() syncs as well
|
|
100
|
-
// (this should give us child paths too, because that's how includes works)
|
|
101
|
-
logs = getLogHistoryIncludes(path);
|
|
102
|
-
if (authorityNodeId) {
|
|
103
|
-
remoteLogs = await DebugLogController.nodes[authorityNodeId].getLogHistoryEquals(path);
|
|
104
|
-
}
|
|
105
|
-
} else {
|
|
106
|
-
logs = getLogHistoryEquals(path);
|
|
107
|
-
if (authorityNodeId) {
|
|
108
|
-
remoteLogs = await DebugLogController.nodes[authorityNodeId].getLogHistoryEquals(path);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
let fullLogs: DebugLogWithSource[] = [];
|
|
113
|
-
let ownNodeId = getOwnNodeId();
|
|
114
|
-
for (let log of logs) {
|
|
115
|
-
fullLogs.push({
|
|
116
|
-
...log,
|
|
117
|
-
sourceType: "local",
|
|
118
|
-
sourceNodeId: ownNodeId,
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
if (authorityNodeId) {
|
|
122
|
-
for (let log of remoteLogs) {
|
|
123
|
-
fullLogs.push({
|
|
124
|
-
...log,
|
|
125
|
-
sourceType: "remote",
|
|
126
|
-
sourceNodeId: authorityNodeId,
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
sort(fullLogs, x => x.time);
|
|
131
|
-
return {
|
|
132
|
-
fullLogs,
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
export const AuditLogController = SocketFunction.register(
|
|
138
|
-
"AuditLogController-bf798faf-c803-4add-ac2c-99d0f44e753a",
|
|
139
|
-
new AuditLogControllerBase(),
|
|
140
|
-
() => ({
|
|
141
|
-
getPathHistory: {},
|
|
142
|
-
}),
|
|
143
|
-
() => ({
|
|
144
|
-
hooks: [assertIsManagementUser],
|
|
145
|
-
})
|
|
146
|
-
);
|
|
147
|
-
export const auditLogController = getSyncedController(AuditLogController);
|
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
import { SocketFunction } from "socket-function/SocketFunction";
|
|
2
|
-
import { qreact } from "../4-dom/qreact";
|
|
3
|
-
import { assertIsManagementUser } from "./managementPages";
|
|
4
|
-
import { css } from "typesafecss";
|
|
5
|
-
import { getSyncedController } from "../library-components/SyncedController";
|
|
6
|
-
import { getAllNodeIds, getBrowserUrlNode } from "../-f-node-discovery/NodeDiscovery";
|
|
7
|
-
import { debugGetAllCallFactories } from "socket-function/src/nodeCache";
|
|
8
|
-
import { debugNodeId } from "../-c-identity/IdentityController";
|
|
9
|
-
import { fastHash } from "../misc/hash";
|
|
10
|
-
import { NodeCapabilitiesController } from "../-g-core-values/NodeCapabilities";
|
|
11
|
-
import { sort } from "socket-function/src/misc";
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
function getColorForNodeId(friendlyNodeId: string): string {
|
|
16
|
-
let hash = fastHash(friendlyNodeId);
|
|
17
|
-
let hue = hash % 360;
|
|
18
|
-
return `hsl(${hue}, 70%, 60%)`;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export class NodeConnectionsPage extends qreact.Component {
|
|
22
|
-
render() {
|
|
23
|
-
let browserNode = getBrowserUrlNode();
|
|
24
|
-
let controller = nodeConnectionsController(browserNode);
|
|
25
|
-
|
|
26
|
-
let nodeIds = controller.getAllNodeIds();
|
|
27
|
-
if (!nodeIds) {
|
|
28
|
-
return <div class={css.pad2(10)}>Loading...</div>;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
let connectableNodes: Array<{ nodeId: string; friendlyNodeId: string; entryPoint: string | undefined; connections: Array<{ nodeId: string; friendlyNodeId: string }> }> = [];
|
|
32
|
-
let nonConnectableNodes: string[] = [];
|
|
33
|
-
|
|
34
|
-
for (let nodeId of nodeIds) {
|
|
35
|
-
try {
|
|
36
|
-
let result = controller.getNodeConnections_forBrowser(nodeId);
|
|
37
|
-
if (result) {
|
|
38
|
-
let friendlyNodeId = debugNodeId(nodeId);
|
|
39
|
-
let entryPoint: string | undefined;
|
|
40
|
-
try {
|
|
41
|
-
entryPoint = controller.getEntryPoint_forBrowser(nodeId);
|
|
42
|
-
} catch (e) {
|
|
43
|
-
entryPoint = undefined;
|
|
44
|
-
}
|
|
45
|
-
connectableNodes.push({
|
|
46
|
-
nodeId,
|
|
47
|
-
friendlyNodeId,
|
|
48
|
-
entryPoint,
|
|
49
|
-
connections: result.connections,
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
} catch (e) {
|
|
53
|
-
nonConnectableNodes.push(nodeId);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return (
|
|
58
|
-
<div class={css.pad2(10).vbox(20).fillWidth}>
|
|
59
|
-
<h1>Connectable Nodes</h1>
|
|
60
|
-
<div class={css.hbox(20).wrap.fillWidth}>
|
|
61
|
-
{connectableNodes.map(node => {
|
|
62
|
-
let color = getColorForNodeId(node.friendlyNodeId);
|
|
63
|
-
return (
|
|
64
|
-
<div class={css.vbox(5).pad2(10).maxHeight(400).overflowAuto.bord(1, { h: 0, s: 0, l: 80 })}>
|
|
65
|
-
<div class={css.vbox(3)}>
|
|
66
|
-
{node.entryPoint && (
|
|
67
|
-
<div class={css.fontSize(14).fontWeight("bold")}>
|
|
68
|
-
{node.entryPoint}
|
|
69
|
-
</div>
|
|
70
|
-
)}
|
|
71
|
-
<div class={css.hbox(5)}>
|
|
72
|
-
<span class={css.background(color).pad2(4, 2).fontWeight("bold")}>
|
|
73
|
-
{node.friendlyNodeId}
|
|
74
|
-
</span>
|
|
75
|
-
<span class={css.color("gray").fontSize(12)}>
|
|
76
|
-
({node.nodeId})
|
|
77
|
-
</span>
|
|
78
|
-
</div>
|
|
79
|
-
</div>
|
|
80
|
-
<div class={css.vbox(3).paddingLeft(20)}>
|
|
81
|
-
{node.connections.length === 0 && (
|
|
82
|
-
<div class={css.color("gray")}>No connections</div>
|
|
83
|
-
)}
|
|
84
|
-
{node.connections.map(conn => {
|
|
85
|
-
let connColor = getColorForNodeId(conn.friendlyNodeId);
|
|
86
|
-
return (
|
|
87
|
-
<div class={css.hbox(5)}>
|
|
88
|
-
<span>→ Connected to:</span>
|
|
89
|
-
<span class={css.background(connColor).pad2(4, 2).fontWeight("bold")}>
|
|
90
|
-
{conn.friendlyNodeId}
|
|
91
|
-
</span>
|
|
92
|
-
<span class={css.color("gray").fontSize(12)}>
|
|
93
|
-
({conn.nodeId})
|
|
94
|
-
</span>
|
|
95
|
-
</div>
|
|
96
|
-
);
|
|
97
|
-
})}
|
|
98
|
-
</div>
|
|
99
|
-
</div>
|
|
100
|
-
);
|
|
101
|
-
})}
|
|
102
|
-
</div>
|
|
103
|
-
|
|
104
|
-
<div class={css.vbox(10).fillWidth}>
|
|
105
|
-
<h1>Non-Connectable Nodes</h1>
|
|
106
|
-
<div class={css.vbox(5).pad2(10).maxHeight(600).overflowAuto.bord(1, { h: 0, s: 0, l: 80 })}>
|
|
107
|
-
{nonConnectableNodes.map(nodeId => {
|
|
108
|
-
let friendlyNodeId = debugNodeId(nodeId);
|
|
109
|
-
return (
|
|
110
|
-
<div class={css.hbox(5)}>
|
|
111
|
-
<span class={css.background("lightgray").pad2(4, 2).fontWeight("bold")}>
|
|
112
|
-
{friendlyNodeId}
|
|
113
|
-
</span>
|
|
114
|
-
<span class={css.color("gray").fontSize(12)}>
|
|
115
|
-
({nodeId})
|
|
116
|
-
</span>
|
|
117
|
-
<span class={css.color("gray")}>- Can't connect</span>
|
|
118
|
-
</div>
|
|
119
|
-
);
|
|
120
|
-
})}
|
|
121
|
-
</div>
|
|
122
|
-
</div>
|
|
123
|
-
</div>
|
|
124
|
-
);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
class NodeConnectionsControllerBase {
|
|
129
|
-
public async getNodeConnections() {
|
|
130
|
-
let factories = debugGetAllCallFactories();
|
|
131
|
-
let connections = factories.filter(x => x.isConnected).map(factory => ({
|
|
132
|
-
nodeId: factory.nodeId,
|
|
133
|
-
friendlyNodeId: debugNodeId(factory.nodeId),
|
|
134
|
-
}));
|
|
135
|
-
return { connections };
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
public async getNodeConnections_forBrowser(nodeId: string) {
|
|
139
|
-
let result = await NodeConnectionsController.nodes[nodeId].getNodeConnections();
|
|
140
|
-
sort(result.connections, x => x.friendlyNodeId);
|
|
141
|
-
return result;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
public async getEntryPoint_forBrowser(nodeId: string) {
|
|
145
|
-
return (await NodeCapabilitiesController.nodes[nodeId].getMetadata()).entryPoint;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
public async getAllNodeIds() {
|
|
149
|
-
return await getAllNodeIds();
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
export const NodeConnectionsController = SocketFunction.register(
|
|
154
|
-
"NodeConnectionsController-8f3e2a1b-4c5d-4f2e-9a3b-7c8d1e2f3a4b",
|
|
155
|
-
new NodeConnectionsControllerBase(),
|
|
156
|
-
() => ({
|
|
157
|
-
getNodeConnections: {},
|
|
158
|
-
getNodeConnections_forBrowser: {},
|
|
159
|
-
getEntryPoint_forBrowser: {},
|
|
160
|
-
getAllNodeIds: {},
|
|
161
|
-
}),
|
|
162
|
-
() => ({
|
|
163
|
-
hooks: [assertIsManagementUser],
|
|
164
|
-
})
|
|
165
|
-
);
|
|
166
|
-
|
|
167
|
-
export const nodeConnectionsController = getSyncedController(NodeConnectionsController);
|