querysub 0.155.0 → 0.157.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/cloudflareHelpers.ts +2 -1
- package/src/-b-authorities/dnsAuthority.ts +2 -2
- package/src/-f-node-discovery/NodeDiscovery.ts +1 -2
- package/src/0-path-value-core/archiveLocks/ArchiveLocks2.ts +33 -49
- package/src/2-proxy/archiveMoveHarness.ts +4 -6
- package/src/2-proxy/garbageCollection.ts +9 -0
- package/src/3-path-functions/pathFunctionLoader.ts +10 -10
- package/src/4-deploy/edgeNodes.ts +3 -2
- package/src/4-querysub/Querysub.ts +6 -4
- package/src/archiveapps/archiveMergeEntry.tsx +1 -0
- package/src/config.ts +1 -1
- package/src/diagnostics/errorLogs/ErrorLogController.ts +0 -1
- package/src/diagnostics/errorLogs/LogTimeSelector.tsx +0 -1
- package/src/diagnostics/managementPages.tsx +16 -6
- package/src/user-implementation/loginEmail.tsx +3 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "querysub",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.157.0",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"note1": "note on node-forge fork, see https://github.com/digitalbazaar/forge/issues/744 for details",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"node-forge": "https://github.com/sliftist/forge#e618181b469b07bdc70b968b0391beb8ef5fecd6",
|
|
25
25
|
"pako": "^2.1.0",
|
|
26
26
|
"preact": "^10.11.3",
|
|
27
|
-
"socket-function": "^0.
|
|
27
|
+
"socket-function": "^0.89.0",
|
|
28
28
|
"terser": "^5.31.0",
|
|
29
29
|
"typesafecss": "^0.6.3",
|
|
30
30
|
"yaml": "^2.5.0",
|
|
@@ -3,7 +3,8 @@ import { getArchives } from "../-a-archives/archives";
|
|
|
3
3
|
import { lazy } from "socket-function/src/caching";
|
|
4
4
|
import { getStorageDir, getSubFolder } from "../fs";
|
|
5
5
|
import fs from "fs";
|
|
6
|
-
|
|
6
|
+
import { isNodeTrue } from "socket-function/src/misc";
|
|
7
|
+
export const keys = isNodeTrue() && getArchives("keys");
|
|
7
8
|
|
|
8
9
|
export const getCloudflareCreds = lazy(async (): Promise<{ key: string; email: string }> => {
|
|
9
10
|
let credsJSON = await keys.get("cloudflare.json");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import os from "os";
|
|
2
2
|
import * as fs from "fs";
|
|
3
3
|
import { cache, lazy } from "socket-function/src/caching";
|
|
4
|
-
import { isNode } from "socket-function/src/misc";
|
|
4
|
+
import { isNode, isNodeTrue } from "socket-function/src/misc";
|
|
5
5
|
import { httpsRequest } from "../https";
|
|
6
6
|
import { getStorageDir } from "../fs";
|
|
7
7
|
import { SocketFunction } from "socket-function/SocketFunction";
|
|
@@ -16,7 +16,7 @@ const DNS_TTLSeconds = {
|
|
|
16
16
|
"A": 60,
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
-
export const keys = getArchives("keys");
|
|
19
|
+
export const keys = isNodeTrue() && getArchives("keys");
|
|
20
20
|
|
|
21
21
|
export const hasDNSWritePermissions = lazy(async () => {
|
|
22
22
|
if (!isNode()) return false;
|
|
@@ -243,13 +243,12 @@ export async function triggerNodeChange() {
|
|
|
243
243
|
}));
|
|
244
244
|
}
|
|
245
245
|
|
|
246
|
-
let seenNodesForEvilDNS = new Set<string>();
|
|
247
246
|
async function syncArchives() {
|
|
248
247
|
if (isServer()) {
|
|
249
248
|
// Make sure we are present
|
|
250
249
|
await writeHeartbeat();
|
|
251
250
|
let nodeIds = await archives().find("");
|
|
252
|
-
console.log(green(`Syncing archives`), { nodeIds });
|
|
251
|
+
console.log(green(`Syncing node ids from archives`), { nodeIds });
|
|
253
252
|
setNodeIds(nodeIds);
|
|
254
253
|
} else {
|
|
255
254
|
if (isNoNetwork() || !isNode()) {
|
|
@@ -18,7 +18,6 @@ import { logNodeStateStats, logNodeStats } from "../../-0-hooks/hooks";
|
|
|
18
18
|
/** Clean up old files after a while */
|
|
19
19
|
const DEAD_CREATE_THRESHOLD = timeInHour * 12;
|
|
20
20
|
const ARCHIVE_PROPAGATION_TIME = 5000;
|
|
21
|
-
const LOG = false;
|
|
22
21
|
// NOTE: There isn't a reason for transaction to not apply. If applying throws, we throw,
|
|
23
22
|
// and can throw infinite times, but if it doesn't throw, it SHOULD be applied. We retry
|
|
24
23
|
// a few times though, just in case the storage system was having some issues. After enough
|
|
@@ -309,9 +308,7 @@ class TransactionLocker {
|
|
|
309
308
|
}
|
|
310
309
|
public async createConfirm(key: string) {
|
|
311
310
|
let path = this.getConfirmKey(key);
|
|
312
|
-
|
|
313
|
-
console.log(`Creating confirmation for ${key}`);
|
|
314
|
-
}
|
|
311
|
+
diskLog(`Creating confirmation for ${key}`);
|
|
315
312
|
await this.storage.setValue(path, Buffer.from(""));
|
|
316
313
|
return path;
|
|
317
314
|
}
|
|
@@ -363,9 +360,10 @@ class TransactionLocker {
|
|
|
363
360
|
delete: ellipsize(deletes.map(a => debugFileInfo(a.key)).join(","), 50),
|
|
364
361
|
});
|
|
365
362
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
363
|
+
diskLog(`Writing transaction`, {
|
|
364
|
+
name,
|
|
365
|
+
ops: transaction.ops.length,
|
|
366
|
+
});
|
|
369
367
|
|
|
370
368
|
let key = `transaction_${name}.transaction`;
|
|
371
369
|
let strippedTransaction: Transaction = { ops: transaction.ops.map(a => ({ ...a, value: undefined })) };
|
|
@@ -490,10 +488,8 @@ class TransactionLocker {
|
|
|
490
488
|
while (true) {
|
|
491
489
|
let result = await tryToRead();
|
|
492
490
|
if (result) {
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
console.log(`Read data state in ${formatTime(timeToRead)}`);
|
|
496
|
-
}
|
|
491
|
+
let timeToRead = Date.now() - startTime;
|
|
492
|
+
diskLog(`Read data state in ${formatTime(timeToRead)}`);
|
|
497
493
|
return result;
|
|
498
494
|
}
|
|
499
495
|
}
|
|
@@ -532,20 +528,20 @@ class TransactionLocker {
|
|
|
532
528
|
let rawLookup = new Set(Array.from(rawDataFiles).map(a => a.file));
|
|
533
529
|
// If any creates are not confirmed, it must not have been applied
|
|
534
530
|
if (transaction.ops.some(a => a.type === "create" && rawLookup.has(a.key) && !confirmedKeys.has(a.key))) {
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
}
|
|
541
|
-
}
|
|
531
|
+
diskLog(`Transaction not applied (has pending confirmations of creates)`, {
|
|
532
|
+
keys: transaction.ops
|
|
533
|
+
.filter(a => a.type === "create" && rawLookup.has(a.key) && !confirmedKeys.has(a.key))
|
|
534
|
+
.map(a => a.key)
|
|
535
|
+
});
|
|
542
536
|
return false;
|
|
543
537
|
}
|
|
544
538
|
// If any deletes still exist, it must not have been applied
|
|
545
539
|
if (transaction.ops.some(a => a.type === "delete" && confirmedKeys.has(a.key))) {
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
540
|
+
diskLog(`Transaction not applied (has pending deletes)`, {
|
|
541
|
+
keys: transaction.ops
|
|
542
|
+
.filter(a => a.type === "delete" && confirmedKeys.has(a.key))
|
|
543
|
+
.map(a => a.key)
|
|
544
|
+
});
|
|
549
545
|
return false;
|
|
550
546
|
}
|
|
551
547
|
return true;
|
|
@@ -554,9 +550,9 @@ class TransactionLocker {
|
|
|
554
550
|
let createCount = transaction.ops.filter(a => a.type === "create").length;
|
|
555
551
|
let deleteCount = transaction.ops.filter(a => a.type === "delete").length;
|
|
556
552
|
let lockedFiles = transaction.lockedFilesMustEqual?.length;
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
}
|
|
553
|
+
diskLog(`Applying transaction with ${createCount} creates and ${deleteCount} deletes. ${lockedFiles !== undefined && `Lock state depends on ${lockedFiles} files` || ""}`, {
|
|
554
|
+
transactions: transaction.ops.map(x => JSON.stringify(x)),
|
|
555
|
+
});
|
|
560
556
|
logNodeStats(`archives|TΔ Apply`, formatNumber, 1);
|
|
561
557
|
let opsRemaining = transaction.ops.slice();
|
|
562
558
|
// NOTE: Order doesn't matter here. If anything is reading the values
|
|
@@ -580,9 +576,9 @@ class TransactionLocker {
|
|
|
580
576
|
};
|
|
581
577
|
await Promise.all(list(CONCURRENT_WRITE_COUNT).map(runThread));
|
|
582
578
|
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
}
|
|
579
|
+
diskLog(`Applied transaction with ${createCount} creates and ${deleteCount} deletes. ${lockedFiles !== undefined && `Lock state depends on ${lockedFiles} files` || ""}`, {
|
|
580
|
+
transactions: transaction.ops.map(x => JSON.stringify(x)),
|
|
581
|
+
});
|
|
586
582
|
}
|
|
587
583
|
|
|
588
584
|
/** Only returns data files (no transaction files, or confirmations).
|
|
@@ -646,9 +642,7 @@ class TransactionLocker {
|
|
|
646
642
|
let threshold = activeT.createTime + this.storage.propagationTime;
|
|
647
643
|
if (Date.now() < threshold) {
|
|
648
644
|
let waitTime = threshold - Date.now();
|
|
649
|
-
|
|
650
|
-
console.log(`Waiting ${formatTime(waitTime)} for transaction ${activeT.seqNum} to settle.`);
|
|
651
|
-
}
|
|
645
|
+
diskLog(`Waiting ${formatTime(waitTime)} for transaction ${activeT.seqNum} to settle.`);
|
|
652
646
|
await new Promise(resolve => setTimeout(resolve, waitTime));
|
|
653
647
|
return this.getFilesBase();
|
|
654
648
|
}
|
|
@@ -688,9 +682,7 @@ class TransactionLocker {
|
|
|
688
682
|
// triggered (without this check), and deletes all of our files...
|
|
689
683
|
let unconfirmedOldFiles2 = veryOldFiles.filter(a => !doubleCheckLookup.has(a) && doubleCheckDataFiles.has(a.file));
|
|
690
684
|
console.warn(red(`Deleted ${unconfirmedOldFiles2.length} very old unconfirmed files`));
|
|
691
|
-
|
|
692
|
-
logNodeStats(`archives|TΔ Delete Old Rejected File`, formatNumber, unconfirmedOldFiles2.length);
|
|
693
|
-
}
|
|
685
|
+
diskLog(`archives|TΔ Delete Old Rejected File`, formatNumber, unconfirmedOldFiles2.length);
|
|
694
686
|
// At the point the file was very old when we started reading, not part of the active transaction.
|
|
695
687
|
for (let file of unconfirmedOldFiles2) {
|
|
696
688
|
await this.deleteDataFile(file.file, `old unconfirmed file (${getOwnNodeId()}, ${process.argv[1]})`);
|
|
@@ -706,10 +698,8 @@ class TransactionLocker {
|
|
|
706
698
|
let oldEnoughConfirms = dataState.rawDataFiles.filter(x => x.file.endsWith(".confirm") && x.createTime < oldThreshold);
|
|
707
699
|
let deprecatedFiles = oldEnoughConfirms.filter(a => !usedConfirmations.has(a.file));
|
|
708
700
|
if (deprecatedFiles.length > 0) {
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
logNodeStats(`archives|TΔ Delete Deprecated Confirm`, formatNumber, deprecatedFiles.length);
|
|
712
|
-
}
|
|
701
|
+
console.warn(red(`Deleted ${deprecatedFiles.length} / ${oldEnoughConfirms.length} confirmations, for not having corresponding data files`));
|
|
702
|
+
logNodeStats(`archives|TΔ Delete Deprecated Confirm`, formatNumber, deprecatedFiles.length);
|
|
713
703
|
for (let file of deprecatedFiles) {
|
|
714
704
|
await this.storage.deleteKey(file.file);
|
|
715
705
|
}
|
|
@@ -744,12 +734,10 @@ class TransactionLocker {
|
|
|
744
734
|
|
|
745
735
|
/** If any deleted files were deleted by other transactions, then we will be rejected. */
|
|
746
736
|
public async addTransaction(transaction: Transaction): Promise<"accepted" | "rejected"> {
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
console.log(blue(`Starting transaction with ${creates} creates and ${dels} deletes, ${formatNumber(createBytes)}B`));
|
|
752
|
-
}
|
|
737
|
+
let dels = transaction.ops.filter(a => a.type === "delete").length;
|
|
738
|
+
let creates = transaction.ops.filter(a => a.type === "create").length;
|
|
739
|
+
let createBytes = transaction.ops.map(a => a.type === "create" && a.value?.length || 0).reduce((a, b) => a + b, 0);
|
|
740
|
+
diskLog(`Starting transaction with ${creates} creates and ${dels} deletes, ${formatNumber(createBytes)}B`);
|
|
753
741
|
transaction = { ...transaction, ops: transaction.ops.slice() };
|
|
754
742
|
function normalizePath(path: string) {
|
|
755
743
|
// Replace duplicate slashes with a single slash
|
|
@@ -775,9 +763,7 @@ class TransactionLocker {
|
|
|
775
763
|
let beforeData = await this.getFilesBase();
|
|
776
764
|
if (!this.isTransactionValid(transaction, beforeData.dataFiles, beforeData.rawDataFiles)) {
|
|
777
765
|
logNodeStats(`archives|TΔ Rejected`, formatNumber, 1);
|
|
778
|
-
|
|
779
|
-
console.log(red(`Finished transaction with rejection, ${transaction.ops.length} ops`));
|
|
780
|
-
}
|
|
766
|
+
diskLog(`Finished transaction with rejection, ${transaction.ops.length} ops`);
|
|
781
767
|
return "rejected";
|
|
782
768
|
}
|
|
783
769
|
|
|
@@ -786,9 +772,7 @@ class TransactionLocker {
|
|
|
786
772
|
let afterData = await this.getFilesBase();
|
|
787
773
|
if (this.wasTransactionApplied(transaction, afterData.dataFiles, afterData.rawDataFiles)) {
|
|
788
774
|
logNodeStats(`archives|TΔ Accepted`, formatNumber, 1);
|
|
789
|
-
|
|
790
|
-
console.log(green(`Finished transaction with ${transaction.ops.length} ops`));
|
|
791
|
-
}
|
|
775
|
+
diskLog(`Finished transaction with ${transaction.ops.length} ops`);
|
|
792
776
|
return "accepted";
|
|
793
777
|
}
|
|
794
778
|
}
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
// yarn typenode src\framework-beta\archiveMoveBase\archiveMoveHarness.ts --nonetwork
|
|
4
|
-
|
|
5
1
|
import { formatNumber, formatTime } from "socket-function/src/formatting/format";
|
|
6
2
|
import { blue, green, red } from "socket-function/src/formatting/logColors";
|
|
7
3
|
import { measureBlock, measureWrap } from "socket-function/src/profiling/measure";
|
|
@@ -22,6 +18,7 @@ import debugbreak from "debugbreak";
|
|
|
22
18
|
let lastFileWrites = new Map<string, string[]>();
|
|
23
19
|
|
|
24
20
|
export async function runArchiveMover(config: {
|
|
21
|
+
debugName: string;
|
|
25
22
|
// Defaults to getOurAuthorities
|
|
26
23
|
// NOTE: Runs once per authority, as we want to keep data as sharded as possible
|
|
27
24
|
authorities?: AuthorityPath[];
|
|
@@ -307,10 +304,11 @@ export async function runArchiveMover(config: {
|
|
|
307
304
|
|| lastResult === "aborted" && green("noop")
|
|
308
305
|
|| red("rejected")
|
|
309
306
|
);
|
|
310
|
-
console.log(`ArchiveMover ${config.runMover.name} on ${authorityDir} in ${formatTime(time)}. Result = ${resultFormat}.`);
|
|
307
|
+
console.log(`ArchiveMover ${config.debugName ?? config.runMover.name} on ${authorityDir} in ${formatTime(time)}. Result = ${resultFormat}.`);
|
|
311
308
|
if (lastResult === "accepted" && !abortedDueToInsufficientReduction) {
|
|
312
309
|
console.log(
|
|
313
|
-
`
|
|
310
|
+
`ArchiveMover free committed ${config.debugName ?? config.runMover.name} on ${authorityDir}`
|
|
311
|
+
+ ` { files: ${formatNumber(inputFiles)}, values: ${formatNumber(inputValueCount)}, bytes: ${formatNumber(inputBytes)} }`
|
|
314
312
|
+ "\n=>\n"
|
|
315
313
|
+ ` { files: ${formatNumber(outputFiles)}, values: ${formatNumber(outputValueCount)}, bytes: ${formatNumber(outputBytes)} }`
|
|
316
314
|
,
|
|
@@ -542,6 +542,7 @@ export async function runAliveCheckerIteration(config?: {
|
|
|
542
542
|
|
|
543
543
|
let lockWrites: PathValue[] = [];
|
|
544
544
|
await runArchiveMover({
|
|
545
|
+
debugName: type,
|
|
545
546
|
outputType: type,
|
|
546
547
|
readLiveData: example.unsafeLiveReads,
|
|
547
548
|
force: config?.force,
|
|
@@ -719,7 +720,15 @@ export async function runAliveCheckerIteration(config?: {
|
|
|
719
720
|
}
|
|
720
721
|
|
|
721
722
|
let allFrees = new Set(freeLists.flat().flat().map(x => x.value));
|
|
723
|
+
let allValues = values;
|
|
722
724
|
values = values.filter(x => !allFrees.has(x));
|
|
725
|
+
if (allValues.length !== values.length) {
|
|
726
|
+
console.log("Freeding values due to garbage collection", {
|
|
727
|
+
before: allValues.length,
|
|
728
|
+
after: values.length,
|
|
729
|
+
freed: allValues.length - values.length,
|
|
730
|
+
});
|
|
731
|
+
}
|
|
723
732
|
|
|
724
733
|
// NOTE: We don't notify any servers about this. We will eventually have to write
|
|
725
734
|
// audit code to periodically check against the disk to get updates.
|
|
@@ -23,6 +23,7 @@ import { getControllerNodeId, getControllerNodeIdList } from "../-g-core-values/
|
|
|
23
23
|
import { sha256 } from "js-sha256";
|
|
24
24
|
import os from "os";
|
|
25
25
|
import { formatTime } from "socket-function/src/formatting/format";
|
|
26
|
+
import path from "path";
|
|
26
27
|
|
|
27
28
|
export type LoadFunctionSpec = {
|
|
28
29
|
gitURL: string;
|
|
@@ -169,7 +170,7 @@ let moduleResolver = async (spec: {
|
|
|
169
170
|
|
|
170
171
|
// Remove any previous attempt to sync it
|
|
171
172
|
if (fs.existsSync(repoPath)) {
|
|
172
|
-
await fs.promises.
|
|
173
|
+
await fs.promises.rmdir(repoPath, { recursive: true });
|
|
173
174
|
}
|
|
174
175
|
// Clone it
|
|
175
176
|
await executeCommand("git", ["clone", gitURL, repoPath]);
|
|
@@ -178,6 +179,14 @@ let moduleResolver = async (spec: {
|
|
|
178
179
|
// Yarn install
|
|
179
180
|
await executeCommand("yarn", ["install"], { cwd: repoPath });
|
|
180
181
|
|
|
182
|
+
// Delete querysub, and replace it with a symlink. Otherwise the synchronization code
|
|
183
|
+
// will run again, and a lot of setup code will run again, etc, and nothing will work correctly.
|
|
184
|
+
let querysubPath = repoPath + "node_modules/querysub";
|
|
185
|
+
await fs.promises.rmdir(querysubPath, { recursive: true });
|
|
186
|
+
|
|
187
|
+
let actualQuerysubPath = path.resolve("./node_modules/querysub");
|
|
188
|
+
await fs.promises.symlink(actualQuerysubPath, querysubPath, "junction");
|
|
189
|
+
|
|
181
190
|
// Mark it as loaded. If we don't reach this point we will move the folder and try again next time
|
|
182
191
|
await fs.promises.writeFile(repoPath + loadTimeIndicatorFileName, Date.now() + "");
|
|
183
192
|
|
|
@@ -186,15 +195,6 @@ let moduleResolver = async (spec: {
|
|
|
186
195
|
});
|
|
187
196
|
}
|
|
188
197
|
|
|
189
|
-
let querysubPath = repoPath + "node_modules/querysub";
|
|
190
|
-
if (fs.existsSync(querysubPath)) {
|
|
191
|
-
// By moving querysub, it forces the repo to use the parent querysub. This is nice, as it even
|
|
192
|
-
// works when this code is required clientside!
|
|
193
|
-
// - Recursive deletes are slow, but moving is fast, so just move it. This repos don't update anyways
|
|
194
|
-
// (we have one per hash), so it's not this will fill up with moved files.
|
|
195
|
-
await fs.promises.rename(querysubPath, querysubPath + "_" + Date.now());
|
|
196
|
-
}
|
|
197
|
-
|
|
198
198
|
return repoPath;
|
|
199
199
|
};
|
|
200
200
|
|
|
@@ -7,7 +7,7 @@ import { getArchivesBackblazePublic } from "../-a-archives/archivesBackBlaze";
|
|
|
7
7
|
import { nestArchives } from "../-a-archives/archives";
|
|
8
8
|
import { SocketFunction } from "socket-function/SocketFunction";
|
|
9
9
|
import { runInSerial, runInfinitePoll, runInfinitePollCallAtStart } from "socket-function/src/batching";
|
|
10
|
-
import { compare, compareArray, timeInMinute } from "socket-function/src/misc";
|
|
10
|
+
import { compare, compareArray, isNodeTrue, timeInMinute } from "socket-function/src/misc";
|
|
11
11
|
import { cacheLimited, lazy } from "socket-function/src/caching";
|
|
12
12
|
import { canHaveChildren } from "socket-function/src/types";
|
|
13
13
|
import { shutdown } from "../diagnostics/periodic";
|
|
@@ -29,7 +29,7 @@ import { startEdgeNotifier } from "./edgeClientWatcher";
|
|
|
29
29
|
const UPDATE_POLL_INTERVAL = timeInMinute;
|
|
30
30
|
const DEAD_NODE_COUNT_THRESHOLD = 15;
|
|
31
31
|
|
|
32
|
-
const edgeNodeStorage = nestArchives("edgenodes/", getArchivesBackblazePublic(getDomain()));
|
|
32
|
+
const edgeNodeStorage = isNodeTrue() && nestArchives("edgenodes/", getArchivesBackblazePublic(getDomain()));
|
|
33
33
|
const edgeNodeIndexFile = "edge-nodes-index.json";
|
|
34
34
|
|
|
35
35
|
const getEdgeNodeConfig = cacheLimited(10000, async (fileName: string): Promise<EdgeNodeConfig | undefined> => {
|
|
@@ -206,6 +206,7 @@ async function runEdgeNodesAliveCheck() {
|
|
|
206
206
|
for (let [fileName, count] of Array.from(deadCounts)) {
|
|
207
207
|
if (count < DEAD_NODE_COUNT_THRESHOLD) continue;
|
|
208
208
|
console.log(`Node is dead. Removing from edgeNodes.`, { fileName, count });
|
|
209
|
+
deadCounts.delete(fileName);
|
|
209
210
|
await edgeNodeStorage.del(fileName);
|
|
210
211
|
}
|
|
211
212
|
}
|
|
@@ -43,7 +43,6 @@ import { measureBlock, measureFnc } from "socket-function/src/profiling/measure"
|
|
|
43
43
|
import { delay } from "socket-function/src/batching";
|
|
44
44
|
import { MaybePromise } from "socket-function/src/types";
|
|
45
45
|
import { devDebugbreak, getDomain, isDevDebugbreak, isDynamicallyLoading, isNoNetwork, isPublic } from "../config";
|
|
46
|
-
import { hookErrors } from "../diagnostics/errorLogs/hookErrors";
|
|
47
46
|
import { Schema2, Schema2T, t } from "../2-proxy/schema2";
|
|
48
47
|
import { CALL_PERMISSIONS_KEY } from "./permissionsShared";
|
|
49
48
|
import { isDynamicModule } from "../3-path-functions/pathFunctionLoader";
|
|
@@ -953,9 +952,12 @@ setImmediate(() => {
|
|
|
953
952
|
});
|
|
954
953
|
});
|
|
955
954
|
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
});
|
|
955
|
+
if (isNode()) {
|
|
956
|
+
setImmediate(async () => {
|
|
957
|
+
const { hookErrors } = await import("../diagnostics/errorLogs/hookErrors");
|
|
958
|
+
hookErrors();
|
|
959
|
+
});
|
|
960
|
+
}
|
|
959
961
|
|
|
960
962
|
setImmediate(async () => {
|
|
961
963
|
// Import, so it registers addStatPeriodic
|
package/src/config.ts
CHANGED
|
@@ -19,7 +19,6 @@ import { atomic, doAtomicWrites } from "../../2-proxy/PathValueProxyWatcher";
|
|
|
19
19
|
import { isClient } from "../../config2";
|
|
20
20
|
import { ControllerPick, SocketRegistered } from "socket-function/SocketFunctionTypes";
|
|
21
21
|
|
|
22
|
-
|
|
23
22
|
const NOTIFY_HISTORY = timeInDay * 7;
|
|
24
23
|
// Only error / fatal are tracked, for now... we might also add warns?
|
|
25
24
|
const logIssueNotifyTypes: LogType[] = ["error", "fatal"];
|
|
@@ -14,7 +14,6 @@ import { ButtonSelector } from "../../library-components/ButtonSelector";
|
|
|
14
14
|
import { Icon } from "../../library-components/icons";
|
|
15
15
|
import { MeasuredDiv } from "../../library-components/MeasuredDiv";
|
|
16
16
|
|
|
17
|
-
|
|
18
17
|
type RangeSize = "lastYear" | "lastMonth" | "lastWeek" | "lastDay" | "lastHour";
|
|
19
18
|
export let logNowTime = Date.now();
|
|
20
19
|
const logFilterStart = new URLParam<number | RangeSize>("logFilterStartTime", "lastWeek");
|
|
@@ -18,7 +18,6 @@ import { getNextTime } from "../0-path-value-core/pathValueCore";
|
|
|
18
18
|
import { encodeArgs } from "../3-path-functions/PathFunctionHelpers";
|
|
19
19
|
import { CallSpec, getCurrentCall, getCurrentCallAllowUndefined, overrideCurrentCall } from "../3-path-functions/PathFunctionRunner";
|
|
20
20
|
import { createLazyComponent } from "../library-components/LazyComponent";
|
|
21
|
-
import { LogNotify } from "./errorLogs/LogNotify";
|
|
22
21
|
import { isTrusted } from "../-d-trust/NetworkTrust2";
|
|
23
22
|
import { devDebugbreak, getDomain } from "../config";
|
|
24
23
|
import { getCallWrites } from "../4-querysub/querysubPrediction";
|
|
@@ -27,7 +26,9 @@ import * as hooks from "../-0-hooks/hooks";
|
|
|
27
26
|
import { addComponentUI } from "../5-diagnostics/qreactDebug";
|
|
28
27
|
import { addComponentButton } from "../5-diagnostics/qreactDebug";
|
|
29
28
|
import { closeAllModals } from "../5-diagnostics/Modal";
|
|
29
|
+
import { delay } from "socket-function/src/batching";
|
|
30
30
|
|
|
31
|
+
const LogNotify = createLazyComponent(() => import("./errorLogs/LogNotify"))("LogNotify");
|
|
31
32
|
|
|
32
33
|
export const managementPageURL = new URLParam("managementpage", "");
|
|
33
34
|
export const showingManagementURL = new URLParam("showingmanagement", false);
|
|
@@ -134,14 +135,21 @@ export async function registerManagementPages2(config: {
|
|
|
134
135
|
__schema = config.schema;
|
|
135
136
|
|
|
136
137
|
setImmediate(async () => {
|
|
137
|
-
if (!isNode() &&
|
|
138
|
+
if (!isNode() &&
|
|
139
|
+
!await ManagementController.nodes[getBrowserUrlNode()].isManagementUser()
|
|
140
|
+
&& new URLSearchParams(window.location.search).get("dropto") !== "user"
|
|
141
|
+
) return;
|
|
142
|
+
|
|
143
|
+
// Wait, so the import system knows the modules are async imports
|
|
144
|
+
await delay(0);
|
|
145
|
+
|
|
146
|
+
const ComponentSyncStats = createLazyComponent(() => import("./misc-pages/ComponentSyncStats"))("ComponentSyncStats");
|
|
138
147
|
|
|
139
|
-
const { filtersURL } = await import("./misc-pages/archiveViewerShared");
|
|
140
|
-
const { watchValueType } = await import("./misc-pages/LocalWatchViewer");
|
|
141
|
-
const { ComponentSyncStats } = await import("./misc-pages/ComponentSyncStats");
|
|
142
148
|
addComponentButton({
|
|
143
149
|
title: "View in Path Navigator",
|
|
144
|
-
callback(component) {
|
|
150
|
+
async callback(component) {
|
|
151
|
+
const { filtersURL } = await import("./misc-pages/archiveViewerShared");
|
|
152
|
+
const { watchValueType } = await import("./misc-pages/LocalWatchViewer");
|
|
145
153
|
filtersURL.value[nextId()] = { column: "path", match: component.debugName, type: "and" };
|
|
146
154
|
managementPageURL.value = "LocalWatchViewer";
|
|
147
155
|
watchValueType.value = "component";
|
|
@@ -168,6 +176,8 @@ export async function registerManagementPages2(config: {
|
|
|
168
176
|
}
|
|
169
177
|
|
|
170
178
|
if (isServer()) {
|
|
179
|
+
// Wait, so the import system knows the modules are async imports
|
|
180
|
+
await delay(0);
|
|
171
181
|
for (let page of inputPages) {
|
|
172
182
|
// NOTE: If we split this into a module for component/controller, we need to make sure we
|
|
173
183
|
// import both serverside, so we can whitelist them for import clientside.
|
|
@@ -114,11 +114,11 @@ export function generateLoginEmail(config: {
|
|
|
114
114
|
<td {...{ "nowrap": true }} style={{ paddingLeft: "40px", }}></td>
|
|
115
115
|
</tr></tbody></table>
|
|
116
116
|
<table><tbody><tr><td style={{ paddingTop: "20px" }}></td></tr></tbody></table>
|
|
117
|
-
{line(<div style={{ color: "rgb(140, 140, 140)", width: "100%" }}><b>Do NOT share or forward this email
|
|
117
|
+
{line(<div style={{ color: "rgb(140, 140, 140)", width: "100%" }}><b>Do NOT share or forward this email to anyone.</b></div>)}
|
|
118
118
|
<table><tbody><tr><td style={{ paddingTop: "10px" }}></td></tr></tbody></table>
|
|
119
119
|
{line(<div style={{ color: "rgb(140, 140, 140)", width: "100%" }}>
|
|
120
|
-
This link will only work
|
|
121
|
-
This link can only be used one time,
|
|
120
|
+
This link will only work in the current device and browser.
|
|
121
|
+
This link can only be used one time, and will expire at {new Date(config.timeoutTime).toLocaleString()}.
|
|
122
122
|
</div>)}
|
|
123
123
|
</>
|
|
124
124
|
);
|