querysub 0.260.0 → 0.262.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.
@@ -1,4 +1,4 @@
1
- import { isNodeTrue, timeInMinute } from "socket-function/src/misc";
1
+ import { isNodeTrue, list, timeInMinute, timeInSecond } from "socket-function/src/misc";
2
2
  import { nestArchives } from "../-a-archives/archives";
3
3
  import { getArchivesBackblaze } from "../-a-archives/archivesBackBlaze";
4
4
  import { getDomain } from "../config";
@@ -10,7 +10,7 @@ import { getSyncedController } from "../library-components/SyncedController";
10
10
  import { requiresNetworkTrustHook } from "../-d-trust/NetworkTrust2";
11
11
  import { extractType } from "../misc/extractType";
12
12
  import { getOwnMachineId } from "../-a-auth/certs";
13
- import { runInSerial, runInfinitePollCallAtStart } from "socket-function/src/batching";
13
+ import { delay, runInSerial, runInfinitePollCallAtStart } from "socket-function/src/batching";
14
14
  import { lazy } from "socket-function/src/caching";
15
15
  import { errorToUndefinedSilent } from "../errors";
16
16
  import { commitAndPush, getGitRefInfo, getGitRefLive, getGitURLLive, getGitUncommitted, getLatestRefOnUpstreamBranch, setGitRef } from "../4-deploy/git";
@@ -19,6 +19,9 @@ import path from "path";
19
19
  import fs from "fs";
20
20
  import { runPromise } from "../functional/runCommand";
21
21
  import { isNode } from "typesafecss";
22
+ import { DeployProgress, deployFunctions, deployGetFunctions } from "../4-deploy/deployFunctions";
23
+ import { FunctionSpec, functionSchema } from "../3-path-functions/PathFunctionRunner";
24
+ import { Querysub } from "../4-querysub/QuerysubController";
22
25
 
23
26
  const SERVICE_FOLDER_NAME = "machine-services";
24
27
  export const SERVICE_FOLDER = `${SERVICE_FOLDER_NAME}/`;
@@ -288,8 +291,73 @@ export class MachineServiceControllerBase {
288
291
  message: commitMessage,
289
292
  });
290
293
  }
291
- }
292
294
 
295
+ public async getPendingFunctions() {
296
+ return await deployGetFunctions();
297
+ }
298
+ public async getLiveFunctions() {
299
+ return await Querysub.commitAsync(() => {
300
+ let base = functionSchema()[getDomain()].PathFunctionRunner;
301
+ let functions: FunctionSpec[] = [];
302
+ for (let moduleObj of Object.values(base)) {
303
+ for (let functionObj of Object.values(moduleObj.Sources)) {
304
+ if (!functionObj) continue;
305
+ functions.push(functionObj);
306
+ }
307
+ }
308
+ return functions;
309
+ });
310
+ }
311
+
312
+ public async deployFunctions(config: {
313
+ functionSpecs: FunctionSpec[];
314
+ notifyRefreshDelay: number;
315
+ deployOnlyCode?: boolean;
316
+ deployOnlyUI?: boolean;
317
+ }) {
318
+ let caller = SocketFunction.getCaller();
319
+ const onProgress: DeployProgress = (config) => {
320
+ void DeployProgressController.nodes[caller.nodeId].onDeployProgress(config);
321
+ };
322
+ await deployFunctions({
323
+ functionSpecs: config.functionSpecs,
324
+ notifyRefreshDelay: config.notifyRefreshDelay,
325
+ deployOnlyCode: config.deployOnlyCode,
326
+ deployOnlyUI: config.deployOnlyUI,
327
+ progress: onProgress,
328
+ });
329
+ }
330
+ }
331
+ let deployWatchers = new Set<DeployProgress>();
332
+ class DeployProgressControllerBase {
333
+ async onDeployProgress(config: { section: string; progress: number; }) {
334
+ for (let deployWatcher of deployWatchers) {
335
+ deployWatcher(config);
336
+ }
337
+ }
338
+ }
339
+ const DeployProgressController = SocketFunction.register(
340
+ " DeployProgressController-9827a7e2-6ace-4156-b623-b4d1a7bab066",
341
+ () => new DeployProgressControllerBase(),
342
+ () => ({
343
+ onDeployProgress: {},
344
+ }),
345
+ );
346
+ export async function deployFunctionsWithProgress(config: {
347
+ functionSpecs: FunctionSpec[];
348
+ notifyRefreshDelay: number;
349
+ deployOnlyCode?: boolean;
350
+ deployOnlyUI?: boolean;
351
+ onProgress: DeployProgress;
352
+ }) {
353
+ const { onProgress, ...rest } = config;
354
+ deployWatchers.add(onProgress);
355
+ try {
356
+ await MachineServiceController(SocketFunction.browserNodeId()).deployFunctions.promise(rest);
357
+ } finally {
358
+ deployWatchers.delete(onProgress);
359
+ }
360
+ }
293
361
 
294
362
  export const MachineServiceController = getSyncedController(
295
363
  SocketFunction.register(
@@ -311,6 +379,9 @@ export const MachineServiceController = getSyncedController(
311
379
  getGitRefInfo: {},
312
380
  commitPushService: {},
313
381
  commitPushAndPublishQuerysub: {},
382
+ getPendingFunctions: {},
383
+ deployFunctions: {},
384
+ getLiveFunctions: {},
314
385
  }),
315
386
  () => ({
316
387
  hooks: [assertIsManagementUser],
@@ -326,6 +397,7 @@ export const MachineServiceController = getSyncedController(
326
397
  deleteServiceConfig: ["ServiceConfig", "ServiceConfigList"],
327
398
  commitPushService: ["gitInfo", "ServiceConfig"],
328
399
  commitPushAndPublishQuerysub: ["gitInfo", "ServiceConfig"],
400
+ deployFunctions: ["gitInfo"],
329
401
  },
330
402
  reads: {
331
403
  getMachineList: ["MachineInfoList"],
@@ -333,6 +405,8 @@ export const MachineServiceController = getSyncedController(
333
405
  getServiceList: ["ServiceConfigList"],
334
406
  getServiceConfig: ["ServiceConfig"],
335
407
  getGitInfo: ["gitInfo"],
408
+ getPendingFunctions: ["gitInfo"],
409
+ getLiveFunctions: ["gitInfo"],
336
410
  }
337
411
  }
338
412
  );
@@ -1,29 +1,32 @@
1
- Our test node isn't appearing. What? Ugh...
2
- - AND I think nodes are never being removed long term? Ugh...
3
1
 
4
- 7.1) Verify quick node removal work by having a repeatedly crashing service, verifying the node exists, and then goes away on crash.
2
+ 8) Fix deploy user notification issue, where the refresh button doesn't work?
3
+ - For application level updates
4
+ - Just an application commit, then deploy the service, then the refresh button should show up, and work
5
5
 
6
6
 
7
- 8) Fix deploy user notification issue, where the refresh button doesn't work?
7
+ What about cloudflare registration?
8
+ 8) Use a special service for the HTTP bootstrapper, and then have 2 others that are on other ports
9
+ - Find a way to prevent the bootstrapper from being picked as the backend as well, so we don't need to update it!
10
+
11
+ --bootstraponly is added, and should work?
8
12
 
9
13
  9) Rolling service updates
10
14
  - Add rollingWindow to the definition
11
- - Show it in the services list as well,
12
- - Keep the oldest service alive while we update
15
+ - Set for public facing services (but not for scripts)
16
+ - PathValueServer - 10 minutes
17
+ - FunctionRunner - 10 minutes
18
+ - HTTP - 4 hours
19
+ - Show the rolling time in the update buttons (both list and the save button on the details page), so we know it will be a rolling update
20
+ - Keep the oldest service alive when we update
13
21
  - Tracked per serviceId
22
+ - Update the rolling time, so if we remove the rolling window we kill the old service immediately
14
23
  - Notify when servers are outdated
15
24
  - In HTTP server, notify users, in the same way we notify for hash updates, that they will need to switch servers
16
- - Verify this update works with a relatively low rolling update window, ensuring it force refreshes before the server actually restarts.
17
-
18
- 10) Add RAM and cpu count to the machine info, as well as to the machine list page
19
- - ALSO, disk size, and disk utilization?
20
- - Yeah... I mean, we have similar metrics for nodes, but... I think nodes and machines will always have some overlap, and both are useful, and have their own distinct uses (top down vs bottom up, where top down is easy to identify that a problem exists, but not where it is, where as bottom up has too much detail and the problem might only exist due to many nodes, but if you see a problem it's easier to find where it is... and some problems might not exist at a machine level too, such as a single core being maxed out, or high latency of requests, or a specific resource being slow, etc, etc)
25
+ - Use the node registration to know the nodeId to talk to, and allow servers to register to get told when they are outdated (and how long before they are shutdown).
26
+ - Verify this update works with a relatively low rolling update window, ensuring it force refreshes before the server actually restarts.
21
27
 
22
- 9) Replace yarn deploy with a page as well
23
- - Only when running locally (and a super user, obviously)
24
- - Can commit and push (just the main repo, not querysub, of course)
25
- - Warn if querysub has changes, and link to the services page
26
- - Another tab on the machines page? (it's annoying, but... we could rename the title from machines to deploy again?)
27
- - Show a nice list of all the changed files and functions
28
- - Give better progress, so we can see what we're waiting on, and how long we've been waiting
29
- - ALSO, add a button which we can add to our main header, which will show if we have unpushed changes, and which links to the deploy page
28
+ 10) Add RAM total, ram % used, cpu count, CPU %, disk size, disk % used to machine info
29
+ - Show this all in the list page, with nice bars?
30
+ - Filled, but the total size will also depend on the maximum (to a degree), so it's very nice.
31
+ - A bar, with a label, or something. The AI can give us a few options
32
+ (Yes, it overlaps with node metrics, but that's okay, sometimes we want to look at machines, other times services, and the types of errors that show up in either changes).
@@ -1,6 +1,6 @@
1
1
  import { URLParam } from "../library-components/URLParam";
2
2
 
3
- export type ViewType = "services" | "machines" | "service-detail" | "machine-detail";
3
+ export type ViewType = "services" | "machines" | "service-detail" | "machine-detail" | "deploy";
4
4
 
5
5
  export const currentViewParam = new URLParam<ViewType>("machineview", "machines");
6
6
  export const selectedServiceIdParam = new URLParam("serviceId", "");
@@ -85,7 +85,13 @@ export class NodeViewer extends qreact.Component {
85
85
  let time = Date.now();
86
86
  let data: NodeData = { nodeId };
87
87
  try {
88
+ if (nodeId.includes("b3a757")) {
89
+ debugger;
90
+ }
88
91
  data.table = await controller.getMiscInfo(nodeId);
92
+ if (nodeId.includes("b3a757")) {
93
+ debugger;
94
+ }
89
95
  data.live_isReadReady = await controller.live_isReadReady(nodeId);
90
96
  data.live_exposedControllers = await controller.getExposedControllers(nodeId);
91
97
  data.live_entryPoint = await controller.live_getEntryPoint(nodeId);
@@ -99,8 +105,14 @@ export class NodeViewer extends qreact.Component {
99
105
  } else {
100
106
  data.devToolsURL = nodeId;
101
107
  }
108
+ if (nodeId.includes("b3a757")) {
109
+ debugger;
110
+ }
102
111
  } catch (e: any) {
103
112
  data.apiError = "Error: " + e.stack;
113
+ if (nodeId.includes("b3a757")) {
114
+ debugger;
115
+ }
104
116
  }
105
117
  time = Date.now() - time;
106
118
  data.loadTime = time;
@@ -144,8 +156,11 @@ export class NodeViewer extends qreact.Component {
144
156
  return <div>Verifying access...</div>;
145
157
  }
146
158
  let nodeDatas = Object.values(this.state.nodes);
159
+ let deadCount = 0;
147
160
  if (!showNotReadyNodes.value) {
148
- nodeDatas = nodeDatas.filter(x => x.live_isReadReady);
161
+ let ready = nodeDatas.filter(x => x.live_isReadReady);
162
+ deadCount = nodeDatas.length - ready.length;
163
+ nodeDatas = ready;
149
164
  }
150
165
 
151
166
  function mergeTables(tables: TableType[]): TableType {
@@ -240,7 +255,7 @@ export class NodeViewer extends qreact.Component {
240
255
  this.populateNodeInfos.reset();
241
256
  this.populateNodeInfos();
242
257
  }}>Refresh</Button>
243
- <InputLabelURL label="Show Not Ready" checkbox url={showNotReadyNodes} />
258
+ <InputLabelURL label={`Show Not Ready (${deadCount})`} checkbox url={showNotReadyNodes} />
244
259
  {this.state.error && <div class={errorMessage}>{this.state.error}</div>}
245
260
  </div>
246
261
  <div class={css.vbox(10)}>
@@ -1,5 +1,5 @@
1
1
  import preact from "preact";
2
- import { css } from "typesafecss";
2
+ import { css, isNode } from "typesafecss";
3
3
  import { URLParam, parseSearchString, encodeSearchString } from "./URLParam";
4
4
  import { qreact } from "../4-dom/qreact";
5
5
 
@@ -97,14 +97,8 @@ export class ATag extends qreact.Component<ATagProps> {
97
97
  // NOTE: Calculate the href dynamically, to prevent pages with a lot of links that
98
98
  // reference frequently changing values from resulting in a huge number of DOM changes
99
99
  // every time a value changes.
100
- let urlObj = new URL(document.location.href);
101
- let params = parseSearchString(urlObj.search);
102
100
  let resolvedValues = typeof values === "function" ? values() : values;
103
- for (let value of resolvedValues || []) {
104
- params[value.param.urlKey] = value.value;
105
- }
106
- urlObj.search = encodeSearchString(params);
107
- e.currentTarget.href = urlObj.toString();
101
+ e.currentTarget.href = createLink(resolvedValues || []);
108
102
  props.onMouseOver?.(e);
109
103
  }}
110
104
  onKeyDown={e => {
@@ -120,4 +114,15 @@ export class ATag extends qreact.Component<ATagProps> {
120
114
  }
121
115
  }
122
116
  export const Anchor = ATag;
123
- export const Link = ATag;
117
+ export const Link = ATag;
118
+
119
+ export function createLink(values: URLOverride[]) {
120
+ if (isNode()) throw new Error(`Create link is not implemented yet on node.`);
121
+ let urlObj = new URL(document.location.href);
122
+ let params = parseSearchString(urlObj.search);
123
+ for (let value of values) {
124
+ params[value.param.urlKey] = value.value;
125
+ }
126
+ urlObj.search = encodeSearchString(params);
127
+ return urlObj.toString();
128
+ }
@@ -1,6 +1,6 @@
1
1
  import { qreact } from "../4-dom/qreact";
2
2
  import { formatTime, formatVeryNiceDateTime } from "socket-function/src/formatting/format";
3
3
 
4
- export function formatTimeJSX(time: number) {
4
+ export function formatDateJSX(time: number) {
5
5
  return <span title={formatVeryNiceDateTime(time)}>{formatTime(Date.now() - time)}</span>;
6
6
  }
package/src/misc.ts CHANGED
@@ -53,4 +53,12 @@ export function clamp(value: number, min: number, max: number) {
53
53
 
54
54
  export function unique<T>(array: T[]): T[] {
55
55
  return Array.from(new Set(array));
56
+ }
57
+
58
+ export function mostCommon<T>(array: T[]): T | undefined {
59
+ let counts = new Map<T, number>();
60
+ for (let item of array) {
61
+ counts.set(item, (counts.get(item) || 0) + 1);
62
+ }
63
+ return Array.from(counts.entries()).sort((a, b) => b[1] - a[1])[0]?.[0];
56
64
  }
package/src/server.ts CHANGED
@@ -5,7 +5,6 @@ import { isNode, isNodeTrue } from "socket-function/src/misc";
5
5
  import { SocketFunction } from "socket-function/SocketFunction";
6
6
  import { getThreadKeyCert } from "./-a-auth/certs";
7
7
  import { logErrors } from "./errors";
8
- import { publishMachineARecords } from "./-e-certs/EdgeCertController";
9
8
  import { pathValueAuthority2 } from "./0-path-value-core/NodePathAuthorities";
10
9
  import { green } from "socket-function/src/formatting/logColors";
11
10
  import { formatTime } from "socket-function/src/formatting/format";
@@ -14,9 +13,11 @@ import { formatTime } from "socket-function/src/formatting/format";
14
13
  // we DO want to register some static diagnostics from Querysub, so... import it explicitly.
15
14
  import "./4-querysub/Querysub";
16
15
  import { getOurAuthorities } from "./config2";
17
- import { isPublic } from "./config";
16
+ import { getDomain, isPublic } from "./config";
18
17
 
19
18
  import yargs from "yargs";
19
+ import { Querysub } from "./4-querysub/Querysub";
20
+ import { createSourceCheck } from "./user-implementation/canSeeSource";
20
21
  let yargObj = isNodeTrue() && yargs(process.argv)
21
22
  .option("authority", { type: "string", desc: `Defines the base paths we are an authority on (the domain is prepended to them). Either a file path to a JSON(AuthorityPath[]), or a base64 representation of the JSON(AuthorityPath[]).` })
22
23
  .option("verbose", { type: "boolean", desc: "Log all writes and reads" })
@@ -50,13 +51,12 @@ async function main() {
50
51
  pathValueAuthority2.becomePathAuthority(authority);
51
52
  }
52
53
 
53
- let keyPair = await getThreadKeyCert();
54
- await SocketFunction.mount({
55
- port: 0, ...keyPair, public: isPublic(),
56
- // SHOULD be safe, because trust is still required. Otherwise we run into issues when we have some
57
- // public services, and some private, but still want to run things locally.
58
- autoForwardPort: true,
54
+ await Querysub.hostServer({
55
+ trustedDomains: [getDomain(), "noproxy." + getDomain()],
56
+ port: 443,
57
+ rootPath: __dirname + "/../",
58
+ clientsideEntryPoint: "./src/browser.ts",
59
+ hotReload: true,
60
+ sourceCheck: createSourceCheck(),
59
61
  });
60
-
61
- await publishMachineARecords();
62
62
  }