miqro 6.1.4 → 6.2.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.
Files changed (42) hide show
  1. package/build/editor.bundle.js +10 -2
  2. package/build/esm/editor/components/editor.js +8 -0
  3. package/build/esm/editor/components/file-editor.js +1 -1
  4. package/build/esm/editor/components/highlight-text-area.js +2 -1
  5. package/build/esm/src/common/jwt.d.ts +49 -0
  6. package/build/esm/src/common/jwt.js +76 -0
  7. package/build/esm/src/inflate/inflate-sea.js +9 -8
  8. package/build/esm/src/inflate/setup-http.d.ts +1 -0
  9. package/build/esm/src/inflate/setup-http.js +8 -0
  10. package/build/esm/src/lib.d.ts +1 -1
  11. package/build/esm/src/lib.js +2 -2
  12. package/build/esm/src/services/app.js +5 -4
  13. package/build/esm/src/services/globals.js +45 -1
  14. package/build/esm/src/services/utils/cluster-ws.d.ts +4 -2
  15. package/build/esm/src/services/utils/cluster-ws.js +10 -1
  16. package/build/esm/src/services/utils/server-interface.d.ts +4 -30
  17. package/build/esm/src/services/utils/server-interface.js +44 -64
  18. package/build/esm/src/services/utils/websocketmanager.d.ts +3 -0
  19. package/build/esm/src/services/utils/websocketmanager.js +8 -2
  20. package/build/esm/src/types.d.ts +76 -5
  21. package/build/lib.cjs +2983 -362
  22. package/editor/components/editor.tsx +8 -0
  23. package/editor/components/file-editor.tsx +1 -1
  24. package/editor/components/highlight-text-area.tsx +2 -1
  25. package/package.json +4 -3
  26. package/sea/install-nodejs.sh +1 -1
  27. package/sea/node.version.tag +1 -1
  28. package/sea/types.json +1 -1
  29. package/src/common/jwt.ts +85 -0
  30. package/src/inflate/inflate-sea.ts +9 -8
  31. package/src/inflate/setup-http.ts +9 -0
  32. package/src/lib.ts +2 -2
  33. package/src/services/app.ts +6 -4
  34. package/src/services/globals.ts +45 -2
  35. package/src/services/utils/cluster-ws.ts +6 -1
  36. package/src/services/utils/server-interface.ts +44 -140
  37. package/src/services/utils/websocketmanager.ts +10 -2
  38. package/src/types/cookie.d.ts +2 -0
  39. package/src/types/jose.d.ts +2 -0
  40. package/src/types/miqro.d.ts +92 -2
  41. package/src/types/server.globals.d.ts +4 -29
  42. package/src/types.ts +78 -5
@@ -0,0 +1,85 @@
1
+ import { EncryptJWT, EncryptOptions, JWTDecryptOptions, JWTDecryptResult, JWTPayload, JWTVerifyOptions, JWTVerifyResult, ProtectedHeaderParameters, SignJWT, SignOptions, decodeJwt, decodeProtectedHeader, jwtDecrypt as joseJWTDecrypt, jwtVerify } from "jose";
2
+ import { KeyObject } from "node:crypto";
3
+ import { EncryptJWTOptions, JWTSignOptions } from "../types.js";
4
+
5
+ /**
6
+ * creates a JWT encrypted token with jose
7
+ *
8
+ * @param payload the payload to encrypt
9
+ * @param secret the secret example. const secret = createSecretKey(process.env.JWT_SECRET, 'utf-8');
10
+ * @param options options like expiratation date, issuer and audience
11
+ * @returns
12
+ */
13
+ export async function encryptJWT(payload: JWTPayload, secret: KeyObject, options?: Partial<EncryptJWTOptions>): Promise<string> {
14
+ const en = new EncryptJWT(payload)
15
+ .setProtectedHeader({
16
+ alg: options?.alg ? options?.alg : 'dir',
17
+ enc: options?.enc ? options?.enc : 'A128CBC-HS256'
18
+ })
19
+ .setIssuedAt(options?.iat)
20
+ .setIssuer(options?.iss ? options?.iss : 'urn:example:issuer')
21
+ .setAudience(options?.aud ? options?.aud : 'urn:example:audience')
22
+ .setExpirationTime(options?.exp ? options?.exp : '2h');
23
+
24
+ return await en.encrypt(secret, options?.options);
25
+ }
26
+
27
+ /**
28
+ * decrypts a JWT token with jose
29
+ * @param jwt the JWT token
30
+ * @param secret the secret example. const secret = createSecretKey(process.env.JWT_SECRET, 'utf-8');
31
+ * @param options options like issuer and audience
32
+ * @returns
33
+ */
34
+ export async function decryptJWT<PayloadType = JWTPayload>(jwt: string, secret: KeyObject, options?: Partial<JWTDecryptOptions>): Promise<JWTDecryptResult<PayloadType>> {
35
+ return await joseJWTDecrypt(jwt, secret, options)
36
+ }
37
+
38
+ /**
39
+ * verify a JWT token with jose
40
+ * @param jwt the JWT token
41
+ * @param secret the secret example. const secret = createSecretKey(process.env.JWT_SECRET, 'utf-8');
42
+ * @param options options like issuer and audience
43
+ * @returns
44
+ */
45
+ export async function verifyJWT<PayloadType = JWTPayload>(jwt: string, secret: KeyObject, options?: Partial<JWTVerifyOptions>): Promise<JWTVerifyResult<PayloadType>> {
46
+ return await jwtVerify(jwt, secret, options)
47
+ }
48
+
49
+ /**
50
+ * creates a signed JWT with jose
51
+ *
52
+ * @param payload the payload to encrypt
53
+ * @param secret the secret example. const secret = createSecretKey(process.env.JWT_SECRET, 'utf-8');
54
+ * @param options options like expiratation date, issuer and audience
55
+ * @returns
56
+ */
57
+ export async function signJWT(payload: JWTPayload, secret: KeyObject, options?: Partial<JWTSignOptions>): Promise<string> {
58
+ const sign = new SignJWT(payload)
59
+ .setProtectedHeader({
60
+ alg: options?.alg ? options?.alg : 'HS256',
61
+ })
62
+ .setIssuedAt(options?.iat)
63
+ .setIssuer(options?.iss ? options?.iss : 'urn:example:issuer')
64
+ .setAudience(options?.aud ? options?.aud : 'urn:example:audience')
65
+ .setExpirationTime(options?.exp ? options?.exp : '2h');
66
+ return sign.sign(secret, options?.options);
67
+ }
68
+
69
+ /**
70
+ * decodes a protected header with jose
71
+ * @param token
72
+ * @returns
73
+ */
74
+ export function decodeProtectedHeaderJWT(token: string | object): ProtectedHeaderParameters {
75
+ return decodeProtectedHeader(token);
76
+ }
77
+
78
+ /**
79
+ * decodes a jwt token
80
+ * @param jwt
81
+ * @returns
82
+ */
83
+ export function decodeJWT<PayloadType = JWTPayload>(jwt: string): PayloadType & JWTPayload {
84
+ return decodeJwt<PayloadType>(jwt)
85
+ }
@@ -1,6 +1,6 @@
1
1
  import { Logger } from "@miqro/core";
2
2
  import { chmodSync, constants, mkdirSync, writeFileSync } from "node:fs";
3
- import { dirname, extname, join, relative, resolve } from "node:path";
3
+ import { basename, dirname, extname, join, relative, resolve } from "node:path";
4
4
  import { cwd, platform } from "node:process";
5
5
 
6
6
  import { RouteFileMap, StaticFileMap } from "./setup-http.js";
@@ -66,22 +66,22 @@ export async function inflateAppForSea(logger: Logger, inflateDir: string, servi
66
66
 
67
67
  writeFile(logger, join(inflateDir, "sea", "package.json"), `{ "type": "module", "private": true }`);
68
68
 
69
- writeFile(logger, join(inflateDir, "sea", "app.cjs"), `const { ServerInterfaceImpl, ServerRequestHandler, WebSocketManager, initGlobals, DBManager, App, LoggerHandler, LogProvider, LocalCache, ClusterCache } = require("./lib.cjs");
69
+ writeFile(logger, join(inflateDir, "sea", "app.cjs"), `const { createServerInterface, ServerRequestHandler, WebSocketManager, initGlobals, DBManager, App, LoggerHandler, LogProvider, LocalCache, ClusterCache } = require("./lib.cjs");
70
70
 
71
71
  async function main() {
72
72
  const PORT = process.env["PORT"] ? process.env["PORT"] : 8080;
73
- const logProvider = new LogProvider();
73
+ const loggerProvider = new LogProvider();
74
74
  const localCache = new LocalCache();
75
75
  const cache = new ClusterCache();
76
76
  const webSocketManager = new WebSocketManager();
77
77
  const dbManager = new DBManager();
78
78
  await initGlobals();
79
- const serverInterface = new ServerInterfaceImpl({
79
+ const serverInterface = createServerInterface({
80
80
  cache,
81
81
  localCache,
82
- logProvider,
82
+ loggerProvider,
83
83
  wsManager: webSocketManager,
84
- logger: logProvider.getLogger("server"),
84
+ logger: loggerProvider.getLogger("server"),
85
85
  dbManager,
86
86
  port: PORT
87
87
  });
@@ -133,9 +133,10 @@ ${Object.keys(serviceRouteFileMap)
133
133
  .map(filePath => serviceRouteFileMap[filePath])
134
134
  .filter(data => data.previewMethod === "api")
135
135
  .map(data => data.routes.map(r => {
136
- const rPath = r.inflatePath;
136
+ const rPath = join(relative(cwd(), dirname(data.filePath)), basename(data.filePath));
137
137
  if (rPath) {
138
- const apiInflatedPath = join("..", "..", service, "http", rPath + ".api.js");
138
+ const rPathExt = extname(rPath);
139
+ const apiInflatedPath = join("..", "..", rPath.substring(0, rPath.length - rPathExt.length) + ".js");
139
140
  return ` await appendAPIModule(router, "../../${service}/http", "./${apiInflatedPath}", (await import("./${apiInflatedPath}")).default);`;
140
141
  } else {
141
142
  return "";
@@ -27,6 +27,7 @@ export interface RouteFileMap {
27
27
  options?: RouterHandlerOptions;
28
28
  inflatePath?: string;
29
29
  }[];
30
+ filePath: string;
30
31
  service: string;
31
32
  previewMethod: "api" | "html" | null;
32
33
  }
@@ -93,6 +94,7 @@ function createStaticRoute(service: string, logger: Logger, router: Router, dir:
93
94
  path: normalizePath(path)
94
95
  }],
95
96
  service,
97
+ filePath: file.filePath,
96
98
  previewMethod: "html"
97
99
  };
98
100
 
@@ -185,6 +187,7 @@ async function createRouterFromDirectory(server: ServerInterface, hotreload: boo
185
187
  routeFileMap[file.filePath] = {
186
188
  routes,
187
189
  service,
190
+ filePath: file.filePath,
188
191
  previewMethod: "api"
189
192
  };
190
193
 
@@ -230,6 +233,7 @@ async function createRouterFromDirectory(server: ServerInterface, hotreload: boo
230
233
  routeFileMap[file.filePath] = {
231
234
  routes,
232
235
  service,
236
+ filePath: file.filePath,
233
237
  previewMethod: "html"
234
238
  };
235
239
 
@@ -297,6 +301,7 @@ async function createRouterFromDirectory(server: ServerInterface, hotreload: boo
297
301
 
298
302
  routeFileMap[file.filePath] = {
299
303
  routes,
304
+ filePath: file.filePath,
300
305
  service,
301
306
  previewMethod: "html"
302
307
  };
@@ -375,6 +380,7 @@ async function createRouterFromDirectory(server: ServerInterface, hotreload: boo
375
380
  method: "GET",
376
381
  path
377
382
  }],
383
+ filePath: file.filePath,
378
384
  service,
379
385
  previewMethod: "html"
380
386
  };
@@ -428,6 +434,7 @@ async function createRouterFromDirectory(server: ServerInterface, hotreload: boo
428
434
  path
429
435
  }],
430
436
  service,
437
+ filePath: file.filePath,
431
438
  previewMethod: "html"
432
439
  };
433
440
 
@@ -487,6 +494,7 @@ async function createRouterFromDirectory(server: ServerInterface, hotreload: boo
487
494
  method: "GET",
488
495
  path
489
496
  }],
497
+ filePath: file.filePath,
490
498
  service,
491
499
  previewMethod: "html"
492
500
  };
@@ -545,6 +553,7 @@ async function createRouterFromDirectory(server: ServerInterface, hotreload: boo
545
553
  path
546
554
  }],
547
555
  service,
556
+ filePath: file.filePath,
548
557
  previewMethod: "html"
549
558
  };
550
559
 
package/src/lib.ts CHANGED
@@ -10,10 +10,10 @@ export { DBManager } from "./services/utils/db-manager.js";
10
10
  export { LogProvider, LogProviderOptions } from "./services/utils/log.js";
11
11
  export { Miqro, MiqroOptions, ServerRequestHandler } from "./services/app.js";
12
12
  export { initGlobals, assertGlobalTampered } from "./services/globals.js";
13
- export { appendAPIModule } from "./inflate/utils/sea-utils.js";
14
13
 
15
14
  //exported for --inflate-sea
16
- export { ServerInterfaceImpl, ServerInterfaceImplOptions } from "./services/utils/server-interface.js";
15
+ export { appendAPIModule } from "./inflate/utils/sea-utils.js";
16
+ export { createServerInterface, ServerInterfaceImplOptions } from "./services/utils/server-interface.js";
17
17
  export { App, LoggerHandler, Router } from "@miqro/core";
18
18
  export { migration } from "@miqro/query";
19
19
 
@@ -25,7 +25,7 @@ import { setupExitHandlers } from "../common/exit.js";
25
25
  import { inflateDBConfig, inflateDBMigrations, MigrationModule } from "../inflate/setup-db.js";
26
26
  import { getServicePath } from "../common/paths.js";
27
27
  import { LogConfigMap } from "../inflate/setup-log.js";
28
- import { ServerInterfaceImpl } from "./utils/server-interface.js";
28
+ import { createServerInterface } from "./utils/server-interface.js";
29
29
  import { getPORT, importMiqroJSON } from "../common/arguments.js";
30
30
  import { createLogProviderOptions } from "./utils/log-transport.js";
31
31
  import { createAdminInterface } from "./utils/admin-interface.js";
@@ -139,6 +139,7 @@ export class Miqro {
139
139
  this.localCache = new LocalCache(`MiqroApplicationLocalCache[${this.options.name}]`, this.logger);
140
140
  this.webSocketManager = new WebSocketManager({
141
141
  logger: this.logger,
142
+ loggerProvider: this.loggerProvider,
142
143
  name: `MiqroApplicationWebsocketManager[${this.options.name}]`,
143
144
  avoidLogSocket: this.options.editor
144
145
  });
@@ -150,16 +151,17 @@ export class Miqro {
150
151
  logger: this.logger
151
152
  });
152
153
 
153
- this.serverInterface = new ServerInterfaceImpl({
154
+ this.serverInterface = createServerInterface({
154
155
  cache: this.cache,
155
- localCache: this.localCache,
156
156
  dbManager: this.dbManager,
157
- wsManager: this.webSocketManager,
157
+ localCache: this.localCache,
158
+ webSocketManager: this.webSocketManager,
158
159
  app: this,
159
160
  logger: this.logger,
160
161
  loggerProvider: this.loggerProvider,
161
162
  port: this.options.port
162
163
  });
164
+
163
165
  this.serverRequestHandler = ServerRequestHandler(this.serverInterface);
164
166
 
165
167
  this.adminInterface = createAdminInterface(this);
@@ -1,5 +1,6 @@
1
1
  //@ts-ignore
2
2
  import { ReadBuffer, URLEncodedParser, JSONParser, TextParser, CORS, SessionHandler } from "@miqro/core";
3
+ import cluster from "node:cluster";
3
4
  import { strictEqual } from "node:assert";
4
5
  import { HTMLEncode } from "@miqro/jsx-node";
5
6
  import { createElement as realCreateElement, enableDebugLog, useRuntime, Link, Router, usePathname, Fragment, useEffect, useRef, useState, useQuery, useRefresh, useElement, createContext, useContext, Component, Props } from "@miqro/jsx";
@@ -7,7 +8,10 @@ import { createElement as realCreateElement, enableDebugLog, useRuntime, Link, R
7
8
  import { jsx2HTML } from "../common/jsx.js";
8
9
  import { inflateMD2HTML } from "../inflate/md.js";
9
10
  import { EXIT_CODES } from "../common/constants.js";
10
- import { ServerGlobal } from "../lib.js";
11
+ import { ClusterCache, LocalCache, ServerGlobal } from "../lib.js";
12
+ import { decodeJWT, decodeProtectedHeaderJWT, decryptJWT, encryptJWT, signJWT, verifyJWT } from "../common/jwt.js";
13
+ import { createSecretKey } from "node:crypto";
14
+ import { Parser } from "@miqro/parser";
11
15
 
12
16
  /*const globaljsx: any = Object.freeze({
13
17
  useContext,
@@ -83,7 +87,46 @@ const globalServer: ServerGlobal = Object.freeze<ServerGlobal>({
83
87
  session: SessionHandler
84
88
  }),
85
89
  encodeHTML: HTMLEncode,
86
- inflateMDtoHTML: inflateMD2HTML
90
+ inflateMDtoHTML: inflateMD2HTML,
91
+ createSecretKey,
92
+ newParser() {
93
+ return new Parser();
94
+ },
95
+ newClusterCache(name, logger) {
96
+ return new ClusterCache(name, logger);
97
+ },
98
+ newLocalCache(name, logger) {
99
+ return new LocalCache(name, logger);
100
+ },
101
+ getWorkerNumber(): number {
102
+ return cluster.isPrimary || process.env["CLUSTER_NODE_NUMBER"] === undefined ? 0 : parseInt(process.env["CLUSTER_NODE_NUMBER"], 10);
103
+ },
104
+ getWorkerCount(): number {
105
+ return cluster.isPrimary || process.env["CLUSTER_NODE_NUMBER"] === undefined || process.env["CLUSTER_COUNT"] === undefined ? 1 : parseInt(process.env["CLUSTER_COUNT"], 10);
106
+ },
107
+ isPrimaryWorker(): boolean {
108
+ return cluster.isPrimary || process.env["CLUSTER_NODE_NUMBER"] === "0";
109
+ },
110
+ jwt: {
111
+ decode(jwt) {
112
+ return decodeJWT(jwt);
113
+ },
114
+ decodeProtectedHeader(token) {
115
+ return decodeProtectedHeaderJWT(token);
116
+ },
117
+ decrypt(jwt, secret, options) {
118
+ return decryptJWT(jwt, secret, options);
119
+ },
120
+ encrypt(payload, secret, options) {
121
+ return encryptJWT(payload, secret, options);
122
+ },
123
+ sign(payload, secret, options) {
124
+ return signJWT(payload, secret, options);
125
+ },
126
+ verify(jwt, secret, options) {
127
+ return verifyJWT(jwt, secret, options);
128
+ }
129
+ }
87
130
  }) as ServerGlobal;
88
131
 
89
132
  export function browserJSXGlobals(inFile: string, jsxPath: string | false = false, useExport = true): string {
@@ -17,7 +17,7 @@ export interface ClusterWebSocketServer2Message {
17
17
  export class ClusterWebSocketServer2 extends WebSocketServer {
18
18
  public remoteClients: Set<string> = new Set();
19
19
  listener: (data: any) => Promise<void>;
20
- public constructor(protected name: string, options: WebSocketServerOptions) {
20
+ public constructor(protected name: string, public path: string, public logger: Logger | Console, options: WebSocketServerOptions) {
21
21
  super({
22
22
  ...options,
23
23
  validate: (req) => {
@@ -25,6 +25,7 @@ export class ClusterWebSocketServer2 extends WebSocketServer {
25
25
  this.options.maxConnections !== undefined &&
26
26
  this.clients.size + this.remoteClients.size >= this.options.maxConnections
27
27
  ) {
28
+ this.logger?.warn("[%s] max web socket connection reached! connection refused from [%s]", req.uuid, req.socket.remoteAddress);
28
29
  return false;
29
30
  } else {
30
31
  return options.validate ? options.validate(req) : true;
@@ -41,6 +42,8 @@ export class ClusterWebSocketServer2 extends WebSocketServer {
41
42
  errorMessage: error.message
42
43
  } as ClusterWebSocketServer2Message);
43
44
  }
45
+ this.logger?.error("[%s] error from (%s) error [%s]", req.uuid, req.req.socket.remoteAddress, error);
46
+ this.logger?.error(error);
44
47
  if (options.onError) {
45
48
  options.onError(req, error);
46
49
  }
@@ -55,6 +58,7 @@ export class ClusterWebSocketServer2 extends WebSocketServer {
55
58
  clientUUID: req.uuid
56
59
  } as ClusterWebSocketServer2Message);
57
60
  }
61
+ this.logger?.log("[%s] new web socket connection from (%s)", req.uuid, req.req.socket.remoteAddress);
58
62
  if (options.onConnection) {
59
63
  options.onConnection(req);
60
64
  }
@@ -69,6 +73,7 @@ export class ClusterWebSocketServer2 extends WebSocketServer {
69
73
  clientUUID: req.uuid
70
74
  } as ClusterWebSocketServer2Message);
71
75
  }
76
+ this.logger?.log("[%s] [%s] web socket disconnection from (%s)", req.uuid, this.path, req.req.socket.remoteAddress);
72
77
  if (options.onDisconnect) {
73
78
  options.onDisconnect(req);
74
79
  }
@@ -7,137 +7,33 @@ import { Miqro } from "../app.js";
7
7
  import { WebSocketManager } from "./websocketmanager.js";
8
8
  import { execSync } from "node:child_process";
9
9
  import { LogProvider } from "./log.js";
10
-
11
- /*
12
- {
13
- cache: this.cache,
14
- localCache: this.localCache,
15
- db: {
16
- get: (name: string) => {
17
- return this.dbManager.getDB(name);
18
- },
19
- getMigrations: () => {
20
- if (this.inflated) {
21
- const ret: NamedMigration[] = [];
22
- for (const d of this.inflated.dbList) {
23
- ret.push(...(d.migrations.map(m => {
24
- return {
25
- name: m.name,
26
- service: m.service,
27
- dbName: m.dbName
28
- }
29
- })));
30
- }
31
- return ret;
32
- }
33
- return [];
34
- },
35
- migrate: (options) => {
36
- return this.migrate(options);
37
- },
38
- },
39
- ws: {
40
- get: (path: string) => {
41
- if (path === LOG_SOCKET_PATH) {
42
- throw new Error("cannot use this path");
43
- }
44
- return this.webSocketManager.getWS(path);
45
- },
46
- disconnectAll: (path: string) => {
47
- if (path === LOG_SOCKET_PATH) {
48
- throw new Error("cannot use this path");
49
- }
50
- this.webSocketManager.disconnectAllFrom(path);
51
- }
52
- },
53
- isPrimaryWorker: () => {
54
- return cluster.isPrimary || process.env["CLUSTER_NODE_NUMBER"] === "0";
55
- },
56
- openBrowser: (path: string) => {
57
- const PORT = this.options.port;
58
- const URL = `http://localhost:${PORT}${path}`;
59
- const DEFAULT_OPEN = process.platform === "win32" ? "explorer" : process.platform === "darwin" ? "open" : "xdg-open";
60
- const OPEN = process.env["BROWSER"] ? process.env["BROWSER"] === "none" ? false : process.env["BROWSER"] : DEFAULT_OPEN;
61
- if (OPEN) {
62
- const openCMD = `${OPEN} "${URL}"`;
63
- this.logger?.info("opening browser with [%s]", openCMD);
64
- execSync(openCMD);
65
- } else {
66
- this.logger?.warn("ignoring browser [%s]", process.env["BROWSER"]);
67
- }
68
- },
69
- logger: this.logger,
70
- getLogger: (identifier: string, options?: { level?: any; transports?: any[]; formatter?: any; }) => {
71
- return this.loggerProvider.getLogger(identifier, options);
72
- }
73
- }*/
10
+ import { initGlobals } from "../globals.js";
74
11
 
75
12
  export interface ServerInterfaceImplOptions {
76
13
  cache: CacheInterface;
77
14
  localCache: CacheInterface;
78
15
  dbManager: DBManager;
79
- wsManager: WebSocketManager;
16
+ webSocketManager: WebSocketManager;
80
17
  logger?: Logger;
81
18
  app?: Miqro;
82
19
  port?: string;
83
20
  loggerProvider?: LogProvider;
84
21
  }
85
22
 
86
- export class ServerInterfaceImpl implements ServerInterface {
87
- public cache: CacheInterface;
88
- public localCache: CacheInterface;
89
- public logger?: Logger;
90
- public port?: string;
91
-
92
- public db: {
93
- get(name: string): Database | null;
94
- getMigrations(): NamedMigration[];
95
- migrate(options: MigrateOptions): Promise<void>;
96
- };
97
- public ws: {
98
- get(path: string): WebSocketServer | undefined;
99
- disconnectAll(path: string): void;
100
- };
101
- public loggerProvider?: LogProvider;
102
- openBrowser: (path: string) => void;
103
-
104
- constructor(options: ServerInterfaceImplOptions) {
105
- this.cache = options.cache;
106
- this.localCache = options.localCache;
107
- this.logger = options.logger ? options.logger : options.loggerProvider ? options.loggerProvider.getLogger("server") : undefined;
108
- this.port = options.port;
109
-
110
- const dbManager = options.dbManager;
111
- const wsManager = options.wsManager;
112
- this.loggerProvider = options.loggerProvider;
113
- const app = options.app;
114
-
115
- this.openBrowser = (path: string) => {
116
- const PORT = this.port;
117
- const URL = `http://localhost${PORT ? `:${PORT}` : ""}${path}`;
118
- const DEFAULT_OPEN = process.platform === "win32" ? "explorer" : process.platform === "darwin" ? "open" : "xdg-open";
119
- const OPEN = app.options.browser !== undefined && String(app.options.browser).toUpperCase() !== "TRUE" && String(app.options.browser).toUpperCase() !== "1" ?
120
- String(app.options.browser).toUpperCase() !== "0" && String(app.options.browser).toUpperCase() !== "FALSE" && String(app.options.browser).toUpperCase() !== "NONE" && app.options.browser ?
121
- app.options.browser : false :
122
- process.env["BROWSER"] ?
123
- process.env["BROWSER"] === "none" ? false : process.env["BROWSER"] : DEFAULT_OPEN;
124
- if (OPEN) {
125
- const openCMD = `${OPEN} "${URL}"`;
126
- this.logger?.info("opening browser with [%s]", openCMD);
127
- execSync(openCMD);
128
- } else {
129
- this.logger?.warn("ignoring browser [%s]", OPEN);
130
- }
131
- }
132
-
133
- this.db = Object.freeze({
134
- get: (name: string) => {
135
- return dbManager.getDB(name);
23
+ export function createServerInterface(options: ServerInterfaceImplOptions): ServerInterface {
24
+ initGlobals();
25
+ return Object.freeze<ServerInterface>({
26
+ cache: options.cache,
27
+ localCache: options.localCache,
28
+ logger: options.logger,
29
+ db: {
30
+ get(name) {
31
+ return options.dbManager.getDB(name);
136
32
  },
137
- getMigrations: () => {
138
- if (app?.inflated) {
33
+ getMigrations() {
34
+ if (options.app?.inflated) {
139
35
  const ret: NamedMigration[] = [];
140
- for (const d of app?.inflated.dbList) {
36
+ for (const d of options.app?.inflated.dbList) {
141
37
  ret.push(...(d.migrations.map(m => {
142
38
  return {
143
39
  name: m.name,
@@ -150,30 +46,38 @@ export class ServerInterfaceImpl implements ServerInterface {
150
46
  }
151
47
  return [];
152
48
  },
153
- migrate: (options: MigrateOptions) => {
154
- return app?.migrate(options);
155
- }
156
- });
157
- this.ws = Object.freeze({
49
+ migrate(migrateOptions) {
50
+ return options?.app?.migrate(migrateOptions);
51
+ },
52
+ },
53
+ ws: {
158
54
  get: (name: string) => {
159
- return wsManager?.getWS(name);
55
+ return options?.webSocketManager?.getWS(name);
160
56
  },
161
57
  disconnectAll: (path: string) => {
162
- return wsManager?.disconnectAllButLOGSocket();
58
+ return options?.webSocketManager?.disconnectAllButLOGSocket();
163
59
  }
164
- });
165
- }
166
- public getWorkerNumber(): number {
167
- return cluster.isPrimary || process.env["CLUSTER_NODE_NUMBER"] === undefined ? 0 : parseInt(process.env["CLUSTER_NODE_NUMBER"], 10);
168
- }
169
- public getWorkerCount(): number {
170
- return cluster.isPrimary || process.env["CLUSTER_NODE_NUMBER"] === undefined || process.env["CLUSTER_COUNT"] === undefined ? 1 : parseInt(process.env["CLUSTER_COUNT"], 10);
171
- }
172
- public isPrimaryWorker(): boolean {
173
- return cluster.isPrimary || process.env["CLUSTER_NODE_NUMBER"] === "0";
174
- }
175
-
176
- public getLogger(identifier: string, options?: { level?: any; transports?: any[]; formatter?: any; }): Logger {
177
- return this.loggerProvider?.getLogger(identifier, options);
178
- }
60
+ },
61
+ openBrowser(path) {
62
+ const PORT = options.port;
63
+ const URL = `http://localhost${PORT ? `:${PORT}` : ""}${path}`;
64
+ const DEFAULT_OPEN = process.platform === "win32" ? "explorer" : process.platform === "darwin" ? "open" : "xdg-open";
65
+ const OPEN = options?.app?.options.browser !== undefined && String(options?.app?.options.browser).toUpperCase() !== "TRUE" && String(options?.app?.options.browser).toUpperCase() !== "1" ?
66
+ String(options?.app.options.browser).toUpperCase() !== "0" && String(options?.app?.options.browser).toUpperCase() !== "FALSE" && String(options?.app?.options.browser).toUpperCase() !== "NONE" && options?.app?.options.browser ?
67
+ options?.app.options.browser : false :
68
+ process.env["BROWSER"] ?
69
+ process.env["BROWSER"] === "none" ? false : process.env["BROWSER"] : DEFAULT_OPEN;
70
+ if (OPEN) {
71
+ const openCMD = `${OPEN} "${URL}"`;
72
+ options?.logger?.info("opening browser with [%s]", openCMD);
73
+ execSync(openCMD);
74
+ } else {
75
+ options?.logger?.warn("ignoring browser [%s]", OPEN);
76
+ }
77
+ },
78
+ getLogger(identifier, loggerOptions) {
79
+ return options?.loggerProvider?.getLogger(identifier, loggerOptions);
80
+ },
81
+ ...server
82
+ });
179
83
  }
@@ -2,9 +2,11 @@ import { Logger } from "@miqro/core";
2
2
  import { LOG_SOCKET_PATH } from "../../../editor/common/constants.js";
3
3
  import { ClusterWebSocketServer2 } from "./cluster-ws.js";
4
4
  import { WSConfig } from "../../types.js";
5
+ import { LogProvider } from "./log.js";
5
6
 
6
7
  export interface WebSocketManagerOptions {
7
8
  logger?: Logger | Console;
9
+ loggerProvider?: LogProvider;
8
10
  name?: string;
9
11
  avoidLogSocket?: boolean;
10
12
  }
@@ -14,10 +16,12 @@ export class WebSocketManager {
14
16
  public logger?: Logger | Console | null = null;
15
17
  public name: string;
16
18
  public avoidLogSocket: boolean;
19
+ public loggerProvider: LogProvider;
17
20
  constructor(options?: WebSocketManagerOptions) {
18
21
  this.onUpgrade = this.onUpgrade.bind(this);
19
22
  this.logger = options && options.logger ? options.logger : null;
20
23
  this.name = options && options.name ? options.name : "WebSocketManager";
24
+ this.loggerProvider = options.loggerProvider;
21
25
  this.avoidLogSocket = options && options.avoidLogSocket ? options.avoidLogSocket : false;
22
26
  }
23
27
 
@@ -46,7 +50,9 @@ export class WebSocketManager {
46
50
  throw new Error(`ws on path ${wsConfig.path} already setup!`);
47
51
  }
48
52
  this.logger?.debug("setting up websocket on [%s]", wsConfig.path);
49
- const server = new ClusterWebSocketServer2(wsConfig.path, wsConfig);
53
+ const identifier = wsConfig.path.replaceAll("/", "_").toUpperCase();
54
+ const logger = this.loggerProvider && identifier.length >= 0 ? this.loggerProvider.getLogger(identifier.substring(identifier.charAt(0) === "_" ? 1 : 0)) : this.logger;
55
+ const server = new ClusterWebSocketServer2(this.name + wsConfig.path, wsConfig.path, logger, wsConfig);
50
56
  this.runningGlobalWSMap.set(wsConfig.path, server);
51
57
  }
52
58
  }
@@ -65,7 +71,9 @@ export class WebSocketManager {
65
71
  throw new Error(`ws on path ${wsConfig.path} already setup!`);
66
72
  }
67
73
  this.logger?.debug("setting up websocket on [%s]", wsConfig.path);
68
- const server = new ClusterWebSocketServer2(this.name + wsConfig.path, wsConfig);
74
+ const identifier = wsConfig.path.replaceAll("/", "_").toUpperCase();
75
+ const logger = this.loggerProvider && identifier.length >= 0 ? this.loggerProvider.getLogger(identifier.substring(identifier.charAt(0) === "_" ? 1 : 0)) : this.logger;
76
+ const server = new ClusterWebSocketServer2(this.name + wsConfig.path, wsConfig.path, logger, wsConfig);
69
77
  this.runningGlobalWSMap.set(wsConfig.path, server);
70
78
  }
71
79
  }
@@ -0,0 +1,2 @@
1
+ import "../globals.js";
2
+ export * from "cookie/index.js";
@@ -0,0 +1,2 @@
1
+ import "../globals.js";
2
+ export * from "jose/types/index.js";