querysub 0.516.0 → 0.518.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "querysub",
3
- "version": "0.516.0",
3
+ "version": "0.518.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",
@@ -475,6 +475,15 @@ async function edgeNodeFunction(config: {
475
475
  // The url override wins over everything (including the IP domain default). If the host isn't in the list, it is ignored.
476
476
  {
477
477
  let overrideHost = new URL(document.location.href).searchParams.get("edgenode");
478
+ if (overrideHost) {
479
+ // The URLParam system rewrites search params JSON encoded (strings get quotation marks), so decode the same way it does
480
+ try {
481
+ let parsed = JSON.parse(overrideHost);
482
+ if (typeof parsed === "string") {
483
+ overrideHost = parsed;
484
+ }
485
+ } catch { }
486
+ }
478
487
  if (overrideHost) {
479
488
  let overrideNode = edgeNodes.find(x => x.host === overrideHost);
480
489
  if (overrideNode) {
@@ -1083,6 +1083,7 @@ export class Querysub {
1083
1083
  let mountPromise = SocketFunction.mount({
1084
1084
  public: isPublic(),
1085
1085
  port,
1086
+ autoForwardPort: true,
1086
1087
  ...await getThreadKeyCert(getDomain()),
1087
1088
  });
1088
1089
 
@@ -556,7 +556,8 @@ export class QuerysubControllerBase {
556
556
  throw new Error(`Call has no network, so it would never run (no FunctionRunner would pick it up). Call ${debugCallSpec(call)}`);
557
557
  }
558
558
  // Networks where every function runner is non-public are only usable by super users (they were never meant to receive public traffic, ex, a dev network).
559
- if (!isNetworkPublic(call.network) && !await isManagementUser()) {
559
+ // ALSO, We have to allow calls on the same machine as us, otherwise developers can't log in!
560
+ if (!isNetworkPublic(call.network) && !(await isManagementUser() || call.callerIP === "127.0.0.1")) {
560
561
  throw new Error(`Caller is not a super user, and so cannot add calls on the non-public network "${call.network}". Call ${debugCallSpec(call)}`);
561
562
  }
562
563
 
@@ -57,7 +57,7 @@ function TicketButton(props: {
57
57
  }
58
58
  qreact.inline(TicketButton);
59
59
 
60
- let getMatcher = cacheLimited(100, (pattern: string) =>
60
+ export let getMatcher = cacheLimited(100, (pattern: string) =>
61
61
  createMatchesPattern(Buffer.from(pattern), false)
62
62
  );
63
63
 
@@ -4,8 +4,9 @@ import { css } from "typesafecss";
4
4
  import { SocketFunction } from "socket-function/SocketFunction";
5
5
  import { ATag } from "../../../library-components/ATag";
6
6
  import { managementPageURL, showingManagementURL } from "../../managementPages";
7
- import { LogDatumRenderer } from "./ErrorNotificationPage";
7
+ import { getMatcher, LogDatumRenderer } from "./ErrorNotificationPage";
8
8
  import { getErrorNotificationsManager } from "./errorWatcher";
9
+ import { LogDatum } from "../diskLogger";
9
10
  import { TicketsController } from "../errorTickets/tickets";
10
11
  import { STATE_HUES, ticketFilterURL } from "../errorTickets/TicketPage";
11
12
  import { isTicketFinished, TicketState, TICKET_STATES } from "../errorTickets/ticketTypes";
@@ -49,9 +50,21 @@ export class ErrorWarning extends qreact.Component {
49
50
  render() {
50
51
  let ticketCounts = this.renderTicketCounts();
51
52
 
53
+ // Errors that already have a ticket in an open (not finished) state are hidden here — there's no point telling the user about a bug that is already under investigation
54
+ let openTicketPatterns: string[] = [];
55
+ for (let ticket of TicketsController(SocketFunction.browserNodeId()).getTickets() || []) {
56
+ if (isTicketFinished(ticket.state)) continue;
57
+ if (!ticket.suppressionPattern) continue;
58
+ openTicketPatterns.push(ticket.suppressionPattern);
59
+ }
60
+ const isCoveredByOpenTicket = (datum: LogDatum) => {
61
+ let errorBuffer = Buffer.from(JSON.stringify(datum));
62
+ return openTicketPatterns.some(pattern => getMatcher(pattern)(errorBuffer));
63
+ };
64
+
52
65
  let errorWarning: preact.ComponentChild = undefined;
53
- if (!this.manager.state.isLoading && this.manager.state.unmatchedErrors.length > 0) {
54
- let firstError = this.manager.state.unmatchedErrors[0];
66
+ let firstError = !this.manager.state.isLoading && this.manager.state.unmatchedErrors.find(x => !isCoveredByOpenTicket(x)) || undefined;
67
+ if (firstError) {
55
68
  errorWarning = <div className={css.hbox(4).alignItems("start").hsl(0, 0, 90).bord2(0, 0, 85).pad2(4, 2).hslcolor(0, 0, 0)}>
56
69
  <ATag className={css.paddingTop(5).flexShrink0} values={[
57
70
  showingManagementURL.getOverride(true),
@@ -80,6 +80,23 @@ export async function registerManagementPages2(config: {
80
80
  let inputPages: typeof config.pages = [];
81
81
 
82
82
 
83
+ inputPages.push({
84
+ title: "Routing Table",
85
+ componentName: "AuthoritySpecPage",
86
+ controllerName: "AuthoritySpecPageController",
87
+ getModule: () => import("./misc-pages/AuthoritySpecPage"),
88
+ });
89
+ inputPages.push({
90
+ title: "LOG VIEWER",
91
+ componentName: "LogViewer3",
92
+ getModule: () => import("./logs/IndexedLogs/LogViewer3"),
93
+ });
94
+ inputPages.push({
95
+ title: "Tickets",
96
+ componentName: "TicketPage",
97
+ controllerName: "",
98
+ getModule: () => import("./logs/errorTickets/TicketPage"),
99
+ });
83
100
  // IMPORTANT! The controller name has to be the name of the controller in the file. No human would just randomly put some junk there, but the AI keeps putting random junk there and being surprised when they get an error because that controller doesn't actually exist in that file.
84
101
  inputPages.push({
85
102
  title: "Machines",
@@ -111,12 +128,6 @@ export async function registerManagementPages2(config: {
111
128
  controllerName: "SnapshotViewerController",
112
129
  getModule: () => import("./misc-pages/SnapshotViewer"),
113
130
  });
114
- inputPages.push({
115
- title: "Routing Table",
116
- componentName: "AuthoritySpecPage",
117
- controllerName: "AuthoritySpecPageController",
118
- getModule: () => import("./misc-pages/AuthoritySpecPage"),
119
- });
120
131
  inputPages.push({
121
132
  title: "DNS",
122
133
  componentName: "DNSPage",
@@ -134,11 +145,6 @@ export async function registerManagementPages2(config: {
134
145
  componentName: "FunctionCaptureAnalyzePage",
135
146
  getModule: () => import("./misc-pages/FunctionCaptureAnalyzePage"),
136
147
  });
137
- inputPages.push({
138
- title: "LOG VIEWER",
139
- componentName: "LogViewer3",
140
- getModule: () => import("./logs/IndexedLogs/LogViewer3"),
141
- });
142
148
  // It's really sad to comment out the life cycles page, but I'm just never going to use it, it's just too slow and too annoying. It's easier to get the AI to search stuff.
143
149
  // inputPages.push({
144
150
  // title: "Life Cycles",
@@ -152,12 +158,7 @@ export async function registerManagementPages2(config: {
152
158
  controllerName: "",
153
159
  getModule: () => import("./logs/errorNotifications2/ErrorNotificationPage"),
154
160
  });
155
- inputPages.push({
156
- title: "Tickets",
157
- componentName: "TicketPage",
158
- controllerName: "",
159
- getModule: () => import("./logs/errorTickets/TicketPage"),
160
- });
161
+
161
162
  // NOTE: Even if we never use this, it is needed if a server or site is bootstrapping. However, they should probably include this in their own header dynamically as well.
162
163
  inputPages.push({
163
164
  title: "Security",
@@ -11,18 +11,27 @@ const EDGE_NODE_URL_PARAM = "edgenode";
11
11
  /** A dropdown showing all the edge nodes the bootstrapper had to pick from (with their probed latencies), which one we booted from, and allowing forcing a specific edge node. Forcing sets a url parameter and refreshes the page, as the edge node is used by the bootstrapper, so it can only apply on a fresh page load. Super users only. */
12
12
  export class EdgeNodeSelector extends qreact.Component<{}> {
13
13
  render() {
14
- if (!isCurrentUserSuperUser()) return undefined;
14
+ if (!(isCurrentUserSuperUser() || location.hostname.startsWith("127-0-0-1"))) return undefined;
15
15
  // The stats are a plain global the bootstrapper updates as its probes finish, so we re-read them every second
16
16
  Querysub.nowDelayed(timeInSecond);
17
17
  let stats = globalThis.EDGE_NODE_STATS;
18
18
  const booted = (globalThis as any).BOOTED_EDGE_NODE as EdgeNodeConfig | undefined;
19
19
  if (!booted) return undefined;
20
+ // The URLParam system rewrites search params JSON encoded (strings get quotation marks), so read/write the same way it does
20
21
  let forced = new URL(document.location.href).searchParams.get(EDGE_NODE_URL_PARAM) || "";
22
+ if (forced) {
23
+ try {
24
+ let parsed = JSON.parse(forced);
25
+ if (typeof parsed === "string") {
26
+ forced = parsed;
27
+ }
28
+ } catch { }
29
+ }
21
30
 
22
31
  const setEdgeNode = (host: string) => {
23
32
  let url = new URL(document.location.href);
24
33
  if (host) {
25
- url.searchParams.set(EDGE_NODE_URL_PARAM, host);
34
+ url.searchParams.set(EDGE_NODE_URL_PARAM, JSON.stringify(host));
26
35
  } else {
27
36
  url.searchParams.delete(EDGE_NODE_URL_PARAM);
28
37
  }
@@ -7,7 +7,7 @@ import { forcedNetworkURL, getAutoSelectedNetwork, getNetworkSelectionInfos, get
7
7
  /** A dropdown showing all the function runner networks, which one our function calls are being put on, and allowing forcing a specific network (and unforcing it, going back to the automatic selection). Super users only. */
8
8
  export class NetworkSelector extends qreact.Component<{}> {
9
9
  render() {
10
- if (!isCurrentUserSuperUser()) return undefined;
10
+ if (!(isCurrentUserSuperUser() || location.hostname.startsWith("127-0-0-1"))) return undefined;
11
11
  let infos = getNetworkSelectionInfos();
12
12
  let forced = forcedNetworkURL.value;
13
13
  let selected = getSelectedNetwork({ noThrow: true });