querysub 0.129.0 → 0.131.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 +1 -1
- package/src/-0-hooks/hooks.ts +16 -1
- package/src/-f-node-discovery/NodeDiscovery.ts +1 -1
- package/src/3-path-functions/PathFunctionRunner.ts +2 -2
- package/src/4-deploy/deployMain.ts +1 -1
- package/src/4-deploy/deploySchema.ts +1 -33
- package/src/4-deploy/edgeBootstrap.ts +45 -15
- package/src/4-deploy/edgeClientWatcher.tsx +187 -0
- package/src/4-deploy/edgeNodes.ts +22 -38
- package/src/4-querysub/Querysub.ts +3 -0
- package/src/library-components/URLParam.ts +12 -8
package/package.json
CHANGED
package/src/-0-hooks/hooks.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import type { EdgeNodeConfig } from "../4-deploy/edgeNodes";
|
|
2
|
+
import type { ExtraMetadata } from "../5-diagnostics/nodeMetadata";
|
|
3
|
+
|
|
1
4
|
// Hooks, to allow function implementations to be declared after their first call
|
|
2
5
|
// (so static calls work).
|
|
3
6
|
|
|
@@ -89,7 +92,6 @@ export const onTimeProfile = createHookFunction<
|
|
|
89
92
|
) => void
|
|
90
93
|
>("onTimeProfile");
|
|
91
94
|
|
|
92
|
-
import type { ExtraMetadata } from "../5-diagnostics/nodeMetadata";
|
|
93
95
|
export const registerNodeMetadata = createHookFunction<
|
|
94
96
|
(
|
|
95
97
|
metadata: ExtraMetadata
|
|
@@ -116,3 +118,16 @@ export const logNodeStateStats = createHookFunction<
|
|
|
116
118
|
export const isManagementUser = createHookFunctionReturn<
|
|
117
119
|
() => Promise<boolean>
|
|
118
120
|
>("isManagementUser");
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
declare global {
|
|
125
|
+
var BOOTED_EDGE_NODE: EdgeNodeConfig | undefined;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
export function getBootedEdgeNode(): EdgeNodeConfig {
|
|
130
|
+
let edgeNode = globalThis.BOOTED_EDGE_NODE;
|
|
131
|
+
if (!edgeNode) throw new Error(`No edge node booted? This should be impossible.`);
|
|
132
|
+
return edgeNode;
|
|
133
|
+
}
|
|
@@ -23,7 +23,7 @@ import dns from "dns/promises";
|
|
|
23
23
|
import { isDefined } from "../misc";
|
|
24
24
|
import { diskLog, noDiskLogPrefix } from "../diagnostics/logs/diskLogger";
|
|
25
25
|
import { getDebuggerUrl } from "../diagnostics/listenOnDebugger";
|
|
26
|
-
import { getBootedEdgeNode } from "
|
|
26
|
+
import { getBootedEdgeNode } from "../-0-hooks/hooks";
|
|
27
27
|
|
|
28
28
|
let HEARTBEAT_INTERVAL = timeInMinute * 2;
|
|
29
29
|
// Interval which we check other heartbeats
|
|
@@ -665,9 +665,9 @@ export async function preloadFunctions(specs: FunctionSpec[]) {
|
|
|
665
665
|
let nodeIds = await getControllerNodeIdList(FunctionPreloadController);
|
|
666
666
|
await Promise.allSettled(nodeIds.map(async nodeId => {
|
|
667
667
|
let controller = FunctionPreloadController.nodes[nodeId.nodeId];
|
|
668
|
-
console.log(blue(`Preloading functions on ${nodeId}`));
|
|
668
|
+
console.log(blue(`Preloading functions on ${String(nodeId)}`));
|
|
669
669
|
await errorToUndefined(controller.preloadFunctions(specs));
|
|
670
|
-
console.log(blue(`Finished preloading functions on ${nodeId}`));
|
|
670
|
+
console.log(blue(`Finished preloading functions on ${String(nodeId)}`));
|
|
671
671
|
}));
|
|
672
672
|
}
|
|
673
673
|
|
|
@@ -38,36 +38,4 @@ export function setLiveDeployedHash(config: {
|
|
|
38
38
|
deploySchema()[getDomain()].deploy.history[nextId()] = hashObj;
|
|
39
39
|
},
|
|
40
40
|
});
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
//todonext
|
|
44
|
-
// - Function to dynamically deploy edge service based on hash (try on old hashes)
|
|
45
|
-
// - Watch live hash, and when it changes, deploy new edge service
|
|
46
|
-
// - Add flag to deploy script which sets it (with all the flags, so we can just deploy it)
|
|
47
|
-
// - Update edge node code to pass live hash, and try to connect to a service with that
|
|
48
|
-
|
|
49
|
-
//todonext
|
|
50
|
-
/*
|
|
51
|
-
Force client refresh mechanism / update notification ON `yarn deploy`
|
|
52
|
-
- deployonlycode / deployonlyui / deploynotifyforced
|
|
53
|
-
- OH! Actually...
|
|
54
|
-
- This HAS to use a synced value!
|
|
55
|
-
- Add a new synced value which specifies the UI hash!
|
|
56
|
-
- THEN! Have front-end servers dynamically launch this services with this hash when it's set (loading the code and then serving the specific path, with the hash, etc)
|
|
57
|
-
- Also, serve this to clients!
|
|
58
|
-
- Clients will watch this value (normally, like any other value), and notify the user when it is out of date, letting them ignore it, but eventually forcing a refresh
|
|
59
|
-
- AND, support preloading it
|
|
60
|
-
- So our order will be
|
|
61
|
-
(git push)
|
|
62
|
-
preload functions
|
|
63
|
-
preload UI
|
|
64
|
-
atomically set function hashes + ui hashes
|
|
65
|
-
clients are notified they should refresh
|
|
66
|
-
new clients use services with the latest hash (which servers are already hosting)
|
|
67
|
-
functions will run this hash
|
|
68
|
-
- Ah, also... add a flag which tells us NOT to use the service HTML cache
|
|
69
|
-
- Remove it when used
|
|
70
|
-
- Required on refresh, otherwise we might refresh and use an outdated service
|
|
71
|
-
|
|
72
|
-
ALSO, have FunctionRunner watch the hash using the get/watch mechanism (for preloading, as it will dynamically load the code depending on what is really deployed anyways...)
|
|
73
|
-
*/
|
|
41
|
+
}
|
|
@@ -3,23 +3,17 @@ import { isServer } from "../config2";
|
|
|
3
3
|
import { EdgeNodeConfig, EdgeNodesIndex } from "./edgeNodes";
|
|
4
4
|
import { timeInMinute, timeInSecond } from "socket-function/src/misc";
|
|
5
5
|
import { measureBlock } from "socket-function/src/profiling/measure";
|
|
6
|
+
import { URLParam } from "../library-components/URLParam";
|
|
6
7
|
|
|
7
8
|
module.hotreload = true;
|
|
8
9
|
module.noserverhotreload = false;
|
|
9
10
|
|
|
11
|
+
const liveHashOverrideParam = "liveHashOverride";
|
|
12
|
+
export const liveHashOverrideURL = new URLParam(liveHashOverrideParam, undefined as undefined | {
|
|
13
|
+
liveHash: string;
|
|
14
|
+
time: number;
|
|
15
|
+
});
|
|
10
16
|
|
|
11
|
-
declare global {
|
|
12
|
-
var BOOTED_EDGE_NODE: EdgeNodeConfig | undefined;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
export function getBootedEdgeNode(): EdgeNodeConfig {
|
|
17
|
-
if (isServer()) throw new Error(`getBootedEdgeNode is not available on the server`);
|
|
18
|
-
|
|
19
|
-
let edgeNode = globalThis.BOOTED_EDGE_NODE;
|
|
20
|
-
if (!edgeNode) throw new Error(`No edge node booted? This should be impossible.`);
|
|
21
|
-
return edgeNode;
|
|
22
|
-
}
|
|
23
17
|
|
|
24
18
|
let getCachedConfig = cache(async (url: string): Promise<EdgeNodesIndex | undefined> => {
|
|
25
19
|
setTimeout(() => {
|
|
@@ -217,6 +211,26 @@ async function edgeNodeFunction(config: {
|
|
|
217
211
|
|
|
218
212
|
let cachedConfig = config.cachedConfig;
|
|
219
213
|
|
|
214
|
+
let liveHashOverride = "";
|
|
215
|
+
let liveHashOverrideExpiryTime = 0;
|
|
216
|
+
|
|
217
|
+
const liveHashOverrideParam2: typeof liveHashOverrideParam = "liveHashOverride";
|
|
218
|
+
let searchParams = new URLSearchParams(document.location.search);
|
|
219
|
+
let liveHashOverrideObj = searchParams.get(liveHashOverrideParam2);
|
|
220
|
+
// JUST use it on the first loop, so if we can't find any matches, we ignore it.
|
|
221
|
+
if (liveHashOverrideObj) {
|
|
222
|
+
try {
|
|
223
|
+
let liveHashOverrideObject = JSON.parse(liveHashOverrideObj);
|
|
224
|
+
liveHashOverrideExpiryTime = liveHashOverrideObject.time + 1000 * 30;
|
|
225
|
+
liveHashOverride = liveHashOverrideObject.liveHash;
|
|
226
|
+
} catch (e: any) {
|
|
227
|
+
console.error(`Error parsing liveHashOverride ${liveHashOverride}: ${e.stack}`);
|
|
228
|
+
}
|
|
229
|
+
// Remove it from the search params
|
|
230
|
+
searchParams.delete(liveHashOverrideParam2);
|
|
231
|
+
document.location.search = searchParams.toString();
|
|
232
|
+
}
|
|
233
|
+
|
|
220
234
|
let progressUI = createProgressUI();
|
|
221
235
|
progressUI.setMessage("Finding available server...");
|
|
222
236
|
while (true) {
|
|
@@ -226,9 +240,12 @@ async function edgeNodeFunction(config: {
|
|
|
226
240
|
} catch (e) {
|
|
227
241
|
console.error(e);
|
|
228
242
|
}
|
|
229
|
-
progressUI.setMessage("No available servers, retrying
|
|
230
|
-
|
|
231
|
-
|
|
243
|
+
progressUI.setMessage("No available servers, retrying soon");
|
|
244
|
+
let time = 1000 * 15;
|
|
245
|
+
if (Date.now() < liveHashOverrideExpiryTime) {
|
|
246
|
+
time = 1000 * 3;
|
|
247
|
+
}
|
|
248
|
+
await new Promise(resolve => setTimeout(resolve, time));
|
|
232
249
|
}
|
|
233
250
|
progressUI.stop();
|
|
234
251
|
|
|
@@ -260,6 +277,16 @@ async function edgeNodeFunction(config: {
|
|
|
260
277
|
async function getEdgeNodeConfig(): Promise<EdgeNodeConfig> {
|
|
261
278
|
let edgeIndex = cachedConfig || await (await fetch(config.edgeNodeConfigURL)).json() as EdgeNodesIndex;
|
|
262
279
|
cachedConfig = undefined;
|
|
280
|
+
let liveHashForced = false;
|
|
281
|
+
if (Date.now() < liveHashOverrideExpiryTime) {
|
|
282
|
+
liveHashForced = true;
|
|
283
|
+
edgeIndex.liveHash = liveHashOverride;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// export const liveHashOverrideURL = new URLParam(liveHashOverrideParam, undefined as undefined | {
|
|
287
|
+
// liveHash: string;
|
|
288
|
+
// time: number;
|
|
289
|
+
// });
|
|
263
290
|
|
|
264
291
|
console.group(`Found edge nodes`);
|
|
265
292
|
|
|
@@ -309,6 +336,9 @@ async function edgeNodeFunction(config: {
|
|
|
309
336
|
|
|
310
337
|
let liveNodes = edgeNodes.filter(x => x.gitHash === edgeIndex.liveHash);
|
|
311
338
|
if (liveNodes.length === 0) {
|
|
339
|
+
if (liveHashForced) {
|
|
340
|
+
throw new Error(`Could not find any live nodes (${edgeIndex.liveHash}), and the live hash is forced.`);
|
|
341
|
+
}
|
|
312
342
|
let latestHash = edgeNodes[0].gitHash;
|
|
313
343
|
console.warn(`Could not find any live nodes (${edgeIndex.liveHash}), falling back to latest hash: ${latestHash}`);
|
|
314
344
|
liveNodes = edgeNodes.filter(x => x.gitHash === latestHash);
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { SocketFunction } from "socket-function/SocketFunction";
|
|
2
|
+
import { Querysub } from "../4-querysub/QuerysubController";
|
|
3
|
+
import { deploySchema } from "./deploySchema";
|
|
4
|
+
import { getDomain } from "../config";
|
|
5
|
+
import { throttleFunction, timeInMinute } from "socket-function/src/misc";
|
|
6
|
+
import { isNode } from "typesafecss";
|
|
7
|
+
import { logErrors, timeoutToError } from "../errors";
|
|
8
|
+
import { blue, red } from "socket-function/src/formatting/logColors";
|
|
9
|
+
import { showModal } from "../5-diagnostics/Modal";
|
|
10
|
+
import { qreact } from "../4-dom/qreact";
|
|
11
|
+
import { liveHashOverrideURL } from "./edgeBootstrap";
|
|
12
|
+
import { css } from "typesafecss";
|
|
13
|
+
import { formatTime } from "socket-function/src/formatting/format";
|
|
14
|
+
import { delay } from "socket-function/src/batching";
|
|
15
|
+
|
|
16
|
+
export function startEdgeNotifier() {
|
|
17
|
+
SocketFunction.expose(EdgeNotifierController);
|
|
18
|
+
let lastHash = "";
|
|
19
|
+
Querysub.createWatcher(() => {
|
|
20
|
+
let liveHash = deploySchema()[getDomain()].deploy.live.hash;
|
|
21
|
+
if (!Querysub.isAllSynced()) return;
|
|
22
|
+
if (liveHash === lastHash) return;
|
|
23
|
+
let refreshThresholdTime = deploySchema()[getDomain()].deploy.live.refreshThresholdTime;
|
|
24
|
+
lastHash = liveHash;
|
|
25
|
+
void notifyClients(liveHash, refreshThresholdTime);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let watchingClientNodes = new Set<string>();
|
|
30
|
+
const notifyClients = throttleFunction(100, async function notifyClients(liveHash: string, refreshThresholdTime: number) {
|
|
31
|
+
await Promise.allSettled(Array.from(watchingClientNodes).map(async clientNodeId => {
|
|
32
|
+
try {
|
|
33
|
+
await timeoutToError(5000, EdgeNotifierClientController.nodes[clientNodeId].onLiveHashChange(liveHash, refreshThresholdTime), () => new Error(`Timeout for watch updates on node ${clientNodeId}`));
|
|
34
|
+
} catch (e) {
|
|
35
|
+
console.log(blue(`Client unwatching liveHash ${clientNodeId}`), e);
|
|
36
|
+
watchingClientNodes.delete(clientNodeId);
|
|
37
|
+
}
|
|
38
|
+
}));
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// Track current notification state
|
|
42
|
+
let currentNotification: { close: () => void } | null = null;
|
|
43
|
+
let curHash = "";
|
|
44
|
+
function onLiveHashChange(liveHash: string, refreshThresholdTime: number) {
|
|
45
|
+
let notifyIntervals = [0, 0.1, 0.5, 1];
|
|
46
|
+
console.log(blue(`Client liveHash changed ${liveHash}`));
|
|
47
|
+
let skipFirst = false;
|
|
48
|
+
if (currentNotification) {
|
|
49
|
+
currentNotification.close();
|
|
50
|
+
currentNotification = null;
|
|
51
|
+
skipFirst = true;
|
|
52
|
+
}
|
|
53
|
+
curHash = liveHash;
|
|
54
|
+
|
|
55
|
+
// Start notification loop
|
|
56
|
+
void (async () => {
|
|
57
|
+
// Show notifications at intervals
|
|
58
|
+
for (let i = 0; i < notifyIntervals.length; i++) {
|
|
59
|
+
// Don't show if a newer notification is active
|
|
60
|
+
if (curHash !== liveHash) return;
|
|
61
|
+
|
|
62
|
+
let waitDuration = (notifyIntervals[i + 1] - notifyIntervals[i]) * refreshThresholdTime;
|
|
63
|
+
if (i >= notifyIntervals.length - 1 && waitDuration <= 30 * 1000) continue;
|
|
64
|
+
|
|
65
|
+
// Update the URL override for manual refreshes
|
|
66
|
+
Querysub.localCommit(() => {
|
|
67
|
+
liveHashOverrideURL.value = { liveHash, time: Date.now() };
|
|
68
|
+
});
|
|
69
|
+
if (!skipFirst) {
|
|
70
|
+
// Show notification modal
|
|
71
|
+
currentNotification = {
|
|
72
|
+
close: showModal({
|
|
73
|
+
content: (
|
|
74
|
+
<div
|
|
75
|
+
title={`Live Hash: ${liveHash}`}
|
|
76
|
+
className={
|
|
77
|
+
css.vbox(10).pad(20)
|
|
78
|
+
.hsla(0, 0, 0, 0.8)
|
|
79
|
+
.position("fixed")
|
|
80
|
+
.bottom(10)
|
|
81
|
+
.right(10)
|
|
82
|
+
.zIndex(1000)
|
|
83
|
+
}
|
|
84
|
+
onClick={() => {
|
|
85
|
+
if (currentNotification) {
|
|
86
|
+
currentNotification.close();
|
|
87
|
+
currentNotification = null;
|
|
88
|
+
}
|
|
89
|
+
}}
|
|
90
|
+
>
|
|
91
|
+
<div className={css.vbox(10).maxWidth(250)}>
|
|
92
|
+
<h3 className={css.margin(0)}>Server Update Available</h3>
|
|
93
|
+
<div>The server has been updated. Please refresh the page to ensure you don't experience incompatibility issues.</div>
|
|
94
|
+
</div>
|
|
95
|
+
<div className={css.hbox(10).justifyContent("flex-end")}>
|
|
96
|
+
<button
|
|
97
|
+
className={css.pad(8, 16).hsl(200, 50, 50).color("white").pointer}
|
|
98
|
+
onClick={() => {
|
|
99
|
+
Querysub.localCommit(() => {
|
|
100
|
+
liveHashOverrideURL.value = { liveHash, time: Date.now() };
|
|
101
|
+
});
|
|
102
|
+
window.location.reload();
|
|
103
|
+
}}
|
|
104
|
+
>
|
|
105
|
+
Refresh Now
|
|
106
|
+
</button>
|
|
107
|
+
<button
|
|
108
|
+
className={css.pad(8, 16).pointer}
|
|
109
|
+
onClick={() => {
|
|
110
|
+
if (currentNotification) {
|
|
111
|
+
currentNotification.close();
|
|
112
|
+
currentNotification = null;
|
|
113
|
+
}
|
|
114
|
+
}}
|
|
115
|
+
>
|
|
116
|
+
Dismiss
|
|
117
|
+
</button>
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
)
|
|
121
|
+
}).close
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
skipFirst = false;
|
|
125
|
+
|
|
126
|
+
console.log(red(`Notify again in ${formatTime(waitDuration)}`));
|
|
127
|
+
await delay(waitDuration);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
console.log(red(`Force refresh after 30 seconds`));
|
|
131
|
+
await delay(30 * 1000);
|
|
132
|
+
|
|
133
|
+
// Only force refresh if this is still the current notification
|
|
134
|
+
if (curHash === liveHash) {
|
|
135
|
+
window.location.reload();
|
|
136
|
+
}
|
|
137
|
+
})();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const EdgeNotifierClientController = SocketFunction.register(
|
|
141
|
+
"EdgeNotifierClientController-ed122e41-6ad2-4161-9492-a3f8414bd4f0",
|
|
142
|
+
new class EdgeNotifierClient {
|
|
143
|
+
public async onLiveHashChange(liveHash: string, refreshThresholdTime: number) {
|
|
144
|
+
onLiveHashChange(liveHash, refreshThresholdTime);
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
() => ({
|
|
148
|
+
onLiveHashChange: {},
|
|
149
|
+
}),
|
|
150
|
+
() => ({}),
|
|
151
|
+
{
|
|
152
|
+
noAutoExpose: true,
|
|
153
|
+
}
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
if (!isNode()) {
|
|
157
|
+
setImmediate(() => {
|
|
158
|
+
void Querysub.optionalStartupWait().finally(() => {
|
|
159
|
+
logErrors(EdgeNotifierController.nodes[SocketFunction.browserNodeId()].watchUpdates());
|
|
160
|
+
});
|
|
161
|
+
SocketFunction.expose(EdgeNotifierClientController);
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
class EdgeNotifierControllerBase {
|
|
166
|
+
public async watchUpdates() {
|
|
167
|
+
let nodeId = SocketFunction.getCaller().nodeId;
|
|
168
|
+
console.log(blue(`Client watching liveHash ${nodeId}`));
|
|
169
|
+
watchingClientNodes.add(nodeId);
|
|
170
|
+
Querysub.onNextDisconnect(nodeId, () => {
|
|
171
|
+
watchingClientNodes.delete(nodeId);
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const EdgeNotifierController = SocketFunction.register(
|
|
177
|
+
"EdgeNotifierController-b11fbbf0-ef5e-4ba3-80ae-d5b1b8124f2d",
|
|
178
|
+
new EdgeNotifierControllerBase(),
|
|
179
|
+
() => ({
|
|
180
|
+
watchUpdates: {},
|
|
181
|
+
}),
|
|
182
|
+
() => ({}),
|
|
183
|
+
{
|
|
184
|
+
noAutoExpose: true,
|
|
185
|
+
}
|
|
186
|
+
);
|
|
187
|
+
|
|
@@ -18,17 +18,19 @@ import path from "path";
|
|
|
18
18
|
import debugbreak from "debugbreak";
|
|
19
19
|
import { requiresNetworkTrustHook } from "../-d-trust/NetworkTrust2";
|
|
20
20
|
import { getControllerNodeIdList } from "../-g-core-values/NodeCapabilities";
|
|
21
|
-
import { blue } from "socket-function/src/formatting/logColors";
|
|
21
|
+
import { blue, magenta } from "socket-function/src/formatting/logColors";
|
|
22
22
|
import { errorToUndefined } from "../errors";
|
|
23
23
|
import { deploySchema } from "./deploySchema";
|
|
24
24
|
import { proxyWatcher } from "../2-proxy/PathValueProxyWatcher";
|
|
25
25
|
import { Querysub } from "../4-querysub/QuerysubController";
|
|
26
26
|
import { onEdgeNodesChanged } from "./edgeBootstrap";
|
|
27
|
+
import { startEdgeNotifier } from "./edgeClientWatcher";
|
|
27
28
|
|
|
28
29
|
const UPDATE_POLL_INTERVAL = timeInMinute;
|
|
29
30
|
const DEAD_NODE_COUNT_THRESHOLD = 15;
|
|
30
31
|
|
|
31
32
|
const edgeNodeStorage = nestArchives("edgenodes/", getArchivesBackblazePublic(getDomain()));
|
|
33
|
+
const edgeNodeIndexFile = "edge-nodes-index.json";
|
|
32
34
|
|
|
33
35
|
const getEdgeNodeConfig = cacheLimited(10000, async (fileName: string): Promise<EdgeNodeConfig | undefined> => {
|
|
34
36
|
let edgeNodeConfig = await edgeNodeStorage.get(fileName);
|
|
@@ -48,7 +50,7 @@ function getNodeIdFromPath(path: string) {
|
|
|
48
50
|
// NOTE: We update this in the update loop
|
|
49
51
|
const getEdgeNodesIndex = lazy(async (): Promise<EdgeNodesIndex> => {
|
|
50
52
|
await startUpdateLoop();
|
|
51
|
-
let edgeNodesIndex = await edgeNodeStorage.get(
|
|
53
|
+
let edgeNodesIndex = await edgeNodeStorage.get(edgeNodeIndexFile);
|
|
52
54
|
let liveHash = await Querysub.commitSynced(() => {
|
|
53
55
|
return deploySchema()[getDomain()].deploy.live.hash;
|
|
54
56
|
});
|
|
@@ -174,8 +176,7 @@ const loadEntryPointsByHash = runInSerial(async function loadEntryPointsByHash(c
|
|
|
174
176
|
};
|
|
175
177
|
|
|
176
178
|
await edgeNodeStorage.set(getNextNodePath(), Buffer.from(JSON.stringify(edgeNodeConfig)));
|
|
177
|
-
|
|
178
|
-
SocketFunction.expose(EdgeNodeController);
|
|
179
|
+
console.log(magenta(`Deployed edge node`), edgeNodeConfig);
|
|
179
180
|
|
|
180
181
|
await updateEdgeNodesFile();
|
|
181
182
|
|
|
@@ -188,12 +189,14 @@ export const getEdgeNodeConfigURL = lazy(async () => {
|
|
|
188
189
|
subdomain: `edge`,
|
|
189
190
|
domain: getDomain(),
|
|
190
191
|
});
|
|
191
|
-
return await getURL(
|
|
192
|
+
return await getURL(edgeNodeIndexFile);
|
|
192
193
|
});
|
|
193
194
|
|
|
194
195
|
const startUpdateLoop = lazy(async () => {
|
|
195
196
|
await getEdgeNodeConfigURL();
|
|
196
197
|
await runInfinitePollCallAtStart(UPDATE_POLL_INTERVAL, updateLoop);
|
|
198
|
+
SocketFunction.expose(EdgeNodeController);
|
|
199
|
+
startEdgeNotifier();
|
|
197
200
|
});
|
|
198
201
|
|
|
199
202
|
async function updateLoop() {
|
|
@@ -219,10 +222,7 @@ async function runEdgeNodesAliveCheck() {
|
|
|
219
222
|
}
|
|
220
223
|
}
|
|
221
224
|
async function updateEdgeNodesFile() {
|
|
222
|
-
let prevEdgeNodeFile = await edgeNodeStorage.get(
|
|
223
|
-
let liveHash = await Querysub.commitSynced(() => {
|
|
224
|
-
return deploySchema()[getDomain()].deploy.live.hash;
|
|
225
|
-
});
|
|
225
|
+
let prevEdgeNodeFile = await edgeNodeStorage.get(edgeNodeIndexFile);
|
|
226
226
|
let prevEdgeNodeIndex: EdgeNodesIndex = {
|
|
227
227
|
edgeNodes: [],
|
|
228
228
|
liveHash: "",
|
|
@@ -232,25 +232,29 @@ async function updateEdgeNodesFile() {
|
|
|
232
232
|
prevEdgeNodeIndex = JSON.parse(prevEdgeNodeFile.toString());
|
|
233
233
|
}
|
|
234
234
|
} catch (e) {
|
|
235
|
-
console.error(
|
|
235
|
+
console.error(`Failed to parse ${edgeNodeIndexFile}`, { error: e, prevEdgeNodeFile });
|
|
236
236
|
}
|
|
237
|
+
|
|
237
238
|
getEdgeNodesIndex.set(Promise.resolve(prevEdgeNodeIndex));
|
|
238
239
|
|
|
239
240
|
let edgeNodeFiles = await edgeNodeStorage.find("node", { type: "files" });
|
|
240
241
|
let edgeNodeNodeIds = edgeNodeFiles.map(getNodeIdFromPath);
|
|
241
242
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
}
|
|
243
|
+
let liveHash = await Querysub.commitSynced(() => {
|
|
244
|
+
return deploySchema()[getDomain()].deploy.live.hash;
|
|
245
|
+
});
|
|
246
246
|
|
|
247
247
|
let diff = !!compareArray(edgeNodeNodeIds.sort(), prevEdgeNodeIndex.edgeNodes.map(x => x.nodeId).sort());
|
|
248
|
+
if (diff) {
|
|
249
|
+
console.log(`Detected edgeNodes changed. Updating ${edgeNodeIndexFile}`, { edgeNodeNodeIds, liveHash });
|
|
250
|
+
}
|
|
248
251
|
if (liveHash !== prevEdgeNodeIndex.liveHash) {
|
|
249
252
|
diff = true;
|
|
253
|
+
console.log(`Detected live hash changed. Updating ${edgeNodeIndexFile}`, { liveHash, prevHash: prevEdgeNodeIndex.liveHash });
|
|
250
254
|
}
|
|
251
255
|
if (!diff) return;
|
|
252
256
|
|
|
253
|
-
|
|
257
|
+
|
|
254
258
|
|
|
255
259
|
let newEdgeNodeIndex: EdgeNodesIndex = {
|
|
256
260
|
edgeNodes: [],
|
|
@@ -262,16 +266,16 @@ async function updateEdgeNodesFile() {
|
|
|
262
266
|
newEdgeNodeIndex.edgeNodes.push(edgeNodeConfig);
|
|
263
267
|
}
|
|
264
268
|
|
|
265
|
-
await edgeNodeStorage.set(
|
|
269
|
+
await edgeNodeStorage.set(edgeNodeIndexFile, Buffer.from(JSON.stringify(newEdgeNodeIndex)));
|
|
266
270
|
}
|
|
267
271
|
|
|
268
272
|
export async function preloadUI(hash: string) {
|
|
269
273
|
let nodeIds = await getControllerNodeIdList(EdgeNodeController);
|
|
270
274
|
await Promise.allSettled(nodeIds.map(async nodeId => {
|
|
271
275
|
let controller = EdgeNodeController.nodes[nodeId.nodeId];
|
|
272
|
-
console.log(blue(`Preloading UI on ${nodeId}, hash: ${hash}`));
|
|
276
|
+
console.log(blue(`Preloading UI on ${String(nodeId)}, hash: ${hash}`));
|
|
273
277
|
await errorToUndefined(controller.preloadUI({ hash }));
|
|
274
|
-
console.log(blue(`Finished preloading UI on ${nodeId}`));
|
|
278
|
+
console.log(blue(`Finished preloading UI on ${String(nodeId)}`));
|
|
275
279
|
}));
|
|
276
280
|
}
|
|
277
281
|
|
|
@@ -306,23 +310,3 @@ const EdgeNodeController = SocketFunction.register(
|
|
|
306
310
|
}
|
|
307
311
|
);
|
|
308
312
|
|
|
309
|
-
// async function main() {
|
|
310
|
-
// await loadEntryPointsByHash({
|
|
311
|
-
// host: "127-0-0-1.querysub.com:1111",
|
|
312
|
-
// entryPaths: ["./src/browser.ts"],
|
|
313
|
-
// hash: "7b1eb4934d5bdfe6d0f7076049c4b7decad4e645",
|
|
314
|
-
// });
|
|
315
|
-
// }
|
|
316
|
-
// main().catch(console.error).finally(() => process.exit());
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
//todonext
|
|
321
|
-
// 3) Verify the deploy code works on the live site
|
|
322
|
-
// - I think it isn't? But maybe we just need to do-update again
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
// 7) Client live hash watch code + notify + refresh + flag to ignore the baked in live hash + edge node
|
|
326
|
-
// - Test by pushing, deploying, and then we should immediately see a notify to refresh
|
|
327
|
-
|
|
328
|
-
// 8) VERIFY writing and permissions still works
|
|
@@ -290,6 +290,9 @@ export class Querysub {
|
|
|
290
290
|
public static isAllSynced = Querysub.allSynced;
|
|
291
291
|
public static isAnyUnsynced = Querysub.anyUnsynced;
|
|
292
292
|
|
|
293
|
+
public static onNextDisconnect = SocketFunction.onNextDisconnect;
|
|
294
|
+
public static isNodeConnected = SocketFunction.isNodeConnected;
|
|
295
|
+
|
|
293
296
|
public static pathHasAnyWatchers(get: () => unknown) {
|
|
294
297
|
return clientWatcher.pathHasAnyWatchers(getProxyPath(get));
|
|
295
298
|
}
|
|
@@ -63,11 +63,13 @@ export function createURLSync<T>(urlKey: string, defaultValue: T, config?: {
|
|
|
63
63
|
if (config?.storage === "localStorage") {
|
|
64
64
|
localStorageKeys.add(urlKey);
|
|
65
65
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
66
|
+
setImmediate(() => {
|
|
67
|
+
Querysub.localCommit(() => {
|
|
68
|
+
data().defaults[urlKey] = defaultValue;
|
|
69
|
+
if (!(urlKey in loadSearchCache)) {
|
|
70
|
+
data().params[urlKey] = deepCloneJSON(defaultValue);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
71
73
|
});
|
|
72
74
|
function deleteKeys(obj: any) {
|
|
73
75
|
if (!canHaveChildren(obj)) return;
|
|
@@ -222,9 +224,11 @@ function watchURLForUpdates() {
|
|
|
222
224
|
}
|
|
223
225
|
|
|
224
226
|
if (!isNode()) {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
227
|
+
setImmediate(() => {
|
|
228
|
+
loadParamsFromURL();
|
|
229
|
+
watchURLForUpdates();
|
|
230
|
+
syncStateToURL();
|
|
231
|
+
});
|
|
228
232
|
}
|
|
229
233
|
|
|
230
234
|
export function parseSearchString(search: string): { [key: string]: unknown } {
|