@joystick.js/node-canary 0.0.0-canary.31 → 0.0.0-canary.310

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 (91) hide show
  1. package/_package.json +2 -2
  2. package/dist/action/class.js +21 -0
  3. package/dist/api/get.js +15 -13
  4. package/dist/api/set.js +15 -13
  5. package/dist/app/accounts/createMetadataTableColumns.js +12 -0
  6. package/dist/app/accounts/deleteUser.js +7 -0
  7. package/dist/app/accounts/generateSession.js +2 -4
  8. package/dist/app/accounts/getBrowserSafeUser.js +5 -2
  9. package/dist/app/accounts/hasLoginTokenExpired.js +1 -2
  10. package/dist/app/accounts/index.js +2 -0
  11. package/dist/app/accounts/login.js +6 -0
  12. package/dist/app/accounts/recoverPassword.js +3 -0
  13. package/dist/app/accounts/resetPassword.js +6 -0
  14. package/dist/app/accounts/signup.js +50 -7
  15. package/dist/app/accounts/verifyEmail.js +3 -0
  16. package/dist/app/cluster.js +26 -0
  17. package/dist/app/databases/generate_sql_from_object.js +45 -0
  18. package/dist/app/databases/mongodb/buildConnectionString.js +1 -1
  19. package/dist/app/databases/mongodb/createAccountsIndexes.js +18 -0
  20. package/dist/app/databases/mongodb/createSessionsIndexes.js +10 -0
  21. package/dist/app/databases/mongodb/index.js +1 -1
  22. package/dist/app/databases/mongodb/queries/accounts.js +8 -1
  23. package/dist/app/databases/mongodb/queries/queues.js +40 -26
  24. package/dist/app/databases/mongodb/queries/sessions.js +26 -0
  25. package/dist/app/databases/postgresql/createSessionsIndexes.js +10 -0
  26. package/dist/app/databases/postgresql/createSessionsTables.js +14 -0
  27. package/dist/app/databases/postgresql/handleCleanupQueues.js +35 -0
  28. package/dist/app/databases/postgresql/index.js +66 -4
  29. package/dist/app/databases/postgresql/queries/accounts.js +5 -2
  30. package/dist/app/databases/postgresql/queries/queues.js +69 -36
  31. package/dist/app/databases/postgresql/queries/sessions.js +43 -0
  32. package/dist/app/databases/queryMap.js +6 -2
  33. package/dist/app/databases/stringToSnakeCase.js +6 -0
  34. package/dist/app/getBrowserSafeRequest.js +3 -2
  35. package/dist/app/index.js +222 -75
  36. package/dist/app/initExpress.js +1 -1
  37. package/dist/app/middleware/csp.js +2 -2
  38. package/dist/app/middleware/getTranslations.js +64 -0
  39. package/dist/app/middleware/get_insecure_landing_page_html.js +71 -0
  40. package/dist/app/middleware/hmr/client.js +13 -9
  41. package/dist/app/middleware/index.js +6 -5
  42. package/dist/app/middleware/insecure.js +3 -4
  43. package/dist/app/middleware/render.js +11 -68
  44. package/dist/app/middleware/session.js +12 -11
  45. package/dist/app/queues/index.js +64 -28
  46. package/dist/app/registerGetters.js +5 -6
  47. package/dist/app/registerSetters.js +5 -6
  48. package/dist/app/runGetter.js +17 -5
  49. package/dist/app/runSessionQuery.js +15 -0
  50. package/dist/app/runSetter.js +17 -5
  51. package/dist/app/sanitizeAPIResponse.js +1 -1
  52. package/dist/app/validateSession.js +8 -3
  53. package/dist/app/validateUploaderOptions.js +3 -3
  54. package/dist/app/validateUploads.js +12 -1
  55. package/dist/email/send.js +7 -1
  56. package/dist/email/templates/reset-password.js +0 -1
  57. package/dist/fixture/index.js +40 -0
  58. package/dist/index.js +19 -0
  59. package/dist/lib/escapeKeyValuePair.js +13 -0
  60. package/dist/lib/formatAPIError.js +0 -1
  61. package/dist/lib/getBuildPath.js +1 -1
  62. package/dist/lib/getSSLCertificates.js +3 -3
  63. package/dist/lib/importFile.js +7 -0
  64. package/dist/lib/isValidJSONString.js +1 -1
  65. package/dist/lib/log.js +0 -3
  66. package/dist/lib/objectToSQLKeysString.js +1 -1
  67. package/dist/lib/objectToSQLValuesString.js +1 -1
  68. package/dist/lib/serializeQueryParameters.js +1 -1
  69. package/dist/lib/timestamps.js +47 -0
  70. package/dist/lib/wait.js +8 -0
  71. package/dist/push/logs/index.js +10 -3
  72. package/dist/settings/load.js +3 -5
  73. package/dist/ssr/compileCSS.js +4 -4
  74. package/dist/ssr/findComponentInTree.js +1 -1
  75. package/dist/ssr/getAPIForDataFunctions.js +35 -0
  76. package/dist/ssr/getDataFromComponent.js +15 -0
  77. package/dist/ssr/index.js +19 -45
  78. package/dist/ssr/replaceWhenTags.js +2 -3
  79. package/dist/ssr/setHeadTagsInHTML.js +3 -3
  80. package/dist/test/index.js +9 -0
  81. package/dist/test/trackFunctionCall.js +17 -0
  82. package/dist/validation/inputWithSchema/index.js +3 -3
  83. package/dist/validation/schema/index.js +5 -5
  84. package/dist/websockets/index.js +4 -0
  85. package/getSanitizedContext.js +43 -0
  86. package/package.json +2 -1
  87. package/dist/app/accounts/roles/index.test.js +0 -123
  88. package/dist/app/index.test.js +0 -575
  89. package/dist/app/middleware/sanitizeRequestParameters.js +0 -21
  90. package/dist/email/send.test.js +0 -37
  91. package/dist/validation/index.test.js +0 -463
package/dist/app/index.js CHANGED
@@ -6,6 +6,8 @@ import queryString from "query-string";
6
6
  import multer from "multer";
7
7
  import cron from "node-cron";
8
8
  import { execSync } from "child_process";
9
+ import dayjs from "dayjs";
10
+ import cluster from "./cluster.js";
9
11
  import initExpress from "./initExpress.js";
10
12
  import handleProcessErrors from "./handleProcessErrors";
11
13
  import registerGetters from "./registerGetters.js";
@@ -25,49 +27,58 @@ import runUploader from "./runUploader";
25
27
  import generateId from "../lib/generateId.js";
26
28
  import getOutput from "./getOutput.js";
27
29
  import defaultUserOutputFields from "./accounts/defaultUserOutputFields.js";
30
+ import createMongoDBAccountsIndexes from "./databases/mongodb/createAccountsIndexes";
31
+ import createMongoDBSessionsIndexes from "./databases/mongodb/createSessionsIndexes";
28
32
  import createPostgreSQLAccountsTables from "./databases/postgresql/createAccountsTables";
29
33
  import createPostgreSQLAccountsIndexes from "./databases/postgresql/createAccountsIndexes";
34
+ import createPostgreSQLSessionsTables from "./databases/postgresql/createSessionsTables";
35
+ import createPostgreSQLSessionsIndexes from "./databases/postgresql/createSessionsIndexes";
30
36
  import loadSettings from "../settings/load.js";
31
37
  import Queue from "./queues/index.js";
32
38
  import readDirectory from "../lib/readDirectory.js";
33
39
  import getBuildPath from "../lib/getBuildPath.js";
34
40
  import generateMachineId from "../lib/generateMachineId.js";
41
+ import importFile from "../lib/importFile.js";
35
42
  import emitWebsocketEvent from "../websockets/emitWebsocketEvent.js";
36
43
  import getTargetDatabaseConnection from "./databases/getTargetDatabaseConnection.js";
44
+ import getAPIForDataFunctions from "../ssr/getAPIForDataFunctions.js";
45
+ import getBrowserSafeRequest from "./getBrowserSafeRequest.js";
46
+ import getDataFromComponent from "../ssr/getDataFromComponent.js";
47
+ import getTranslations from "./middleware/getTranslations.js";
48
+ import runUserQuery from "./accounts/runUserQuery.js";
49
+ import wait from "../lib/wait.js";
50
+ import trackFunctionCall from "../test/trackFunctionCall.js";
51
+ import getBrowserSafeUser from "./accounts/getBrowserSafeUser.js";
37
52
  process.setMaxListeners(0);
38
53
  class App {
39
54
  constructor(options = {}) {
40
55
  this.setMachineId();
41
56
  this.setJoystickProcessId();
42
57
  handleProcessErrors(options?.events);
43
- const HMRSessions = JSON.parse(process.env.HMR_SESSIONS || "{}");
44
- this.sessions = new Map(HMRSessions ? Object.entries(HMRSessions) : []);
45
58
  this.databases = [];
46
59
  this.express = {};
47
60
  this.options = options || {};
61
+ process.joystick = {
62
+ _app: {
63
+ options
64
+ }
65
+ };
48
66
  }
49
67
  async start(options = {}) {
50
68
  await this.invalidateCache();
51
69
  this.databases = await this.loadDatabases();
52
70
  this.express = initExpress(this.onStartApp, options, this);
53
71
  this.initWebsockets(options?.websockets || {});
54
- this.initDevelopmentRoutes();
55
- this.initAccounts();
72
+ this.initAccounts(options?.accounts);
73
+ this.initTests();
56
74
  this.initDeploy();
57
75
  this.initAPI(options?.api);
58
- this.initRoutes(options?.routes);
59
76
  this.initUploaders(options?.uploaders);
77
+ this.initIndexes(options?.indexes);
60
78
  this.initFixtures(options?.fixtures);
61
79
  this.initQueues(options?.queues);
62
80
  this.initCronJobs(options?.cronJobs);
63
- if (process.env.NODE_ENV === "development") {
64
- process.on("message", (message) => {
65
- const parsedMessage = typeof message === "string" ? JSON.parse(message) : message;
66
- if (parsedMessage?.type === " RESTART_SERVER") {
67
- this.express?.server?.close();
68
- }
69
- });
70
- }
81
+ this.initRoutes(options?.routes);
71
82
  }
72
83
  async invalidateCache() {
73
84
  const uiFiles = fs.existsSync(`${getBuildPath()}ui`) ? await readDirectory(`${getBuildPath()}ui`) : [];
@@ -85,9 +96,21 @@ class App {
85
96
  const hasQueuesDatabase = settings?.config?.databases?.some((database = {}) => {
86
97
  return !!database?.queues;
87
98
  });
99
+ const hasSessionsDatabase = settings?.config?.databases?.some((database = {}) => {
100
+ return !!database?.sessions;
101
+ });
102
+ const hasMongoDBUsersDatabase = settings?.config?.databases?.some((database = {}) => {
103
+ return database?.provider === "mongodb" && database?.users;
104
+ });
105
+ const hasMongoDBSessionsDatabase = settings?.config?.databases?.some((database = {}) => {
106
+ return database?.provider === "mongodb" && database?.sessions;
107
+ });
88
108
  const hasPostgreSQLUsersDatabase = settings?.config?.databases?.some((database = {}) => {
89
109
  return database?.provider === "postgresql" && database?.users;
90
110
  });
111
+ const hasPostgreSQLSessionsDatabase = settings?.config?.databases?.some((database = {}) => {
112
+ return database?.provider === "postgresql" && database?.sessions;
113
+ });
91
114
  const databases = settings?.config?.databases?.map((database) => {
92
115
  return {
93
116
  provider: database?.provider,
@@ -114,12 +137,18 @@ class App {
114
137
  ...process.databases || {},
115
138
  postgresql: !hasMultipleOfProvider ? {
116
139
  ...postgresql?.pool,
117
- query: postgresql?.query
140
+ query: postgresql?.query,
141
+ select: postgresql?.select,
142
+ insert: postgresql?.insert,
143
+ update: postgresql?.update
118
144
  } : {
119
145
  ...process?.databases?.postgresql || {},
120
146
  [database?.settings?.name || `postgresql_${databasePort}`]: {
121
147
  ...postgresql?.pool,
122
- query: postgresql?.query
148
+ query: postgresql?.query,
149
+ select: postgresql?.select,
150
+ insert: postgresql?.insert,
151
+ update: postgresql?.update
123
152
  }
124
153
  }
125
154
  };
@@ -131,19 +160,40 @@ class App {
131
160
  if (hasQueuesDatabase) {
132
161
  process.databases._queues = getTargetDatabaseConnection("queues")?.connection;
133
162
  }
163
+ if (hasSessionsDatabase) {
164
+ process.databases._sessions = getTargetDatabaseConnection("sessions")?.connection;
165
+ }
166
+ if (hasMongoDBUsersDatabase) {
167
+ await createMongoDBAccountsIndexes();
168
+ }
169
+ if (hasMongoDBSessionsDatabase) {
170
+ await createMongoDBSessionsIndexes();
171
+ }
134
172
  if (hasPostgreSQLUsersDatabase) {
135
173
  await createPostgreSQLAccountsTables();
136
174
  await createPostgreSQLAccountsIndexes();
137
175
  }
176
+ if (hasPostgreSQLSessionsDatabase) {
177
+ await createPostgreSQLSessionsTables();
178
+ await createPostgreSQLSessionsIndexes();
179
+ }
138
180
  return process.databases;
139
181
  }
140
- onStartApp(express = {}) {
182
+ onStartApp(express = {}, joystick_app_instance = {}) {
141
183
  process.on("message", (message) => {
142
184
  if (typeof message === "string") {
143
- process.BUILD_ERROR = JSON.parse(message);
185
+ const parsed_message = JSON.parse(message);
186
+ if (parsed_message?.type === "BUILD_ERROR") {
187
+ process.BUILD_ERROR = JSON.parse(message);
188
+ }
189
+ if (parsed_message?.type === "RESTART_SERVER") {
190
+ console.log("HANDLE RESTART");
191
+ }
144
192
  }
145
193
  });
146
- console.log(`App running at: http://localhost:${express.port}`);
194
+ if (process.env.NODE_ENV !== "test") {
195
+ console.log(`App running at: http://localhost:${express.port}`);
196
+ }
147
197
  }
148
198
  setMachineId() {
149
199
  generateMachineId();
@@ -156,10 +206,78 @@ class App {
156
206
  fs.writeFileSync("./.joystick/PROCESS_ID", `${generateId(32)}`);
157
207
  }
158
208
  }
209
+ initTests() {
210
+ if (process.env.NODE_ENV === "test") {
211
+ this.express.app.get("/api/_test/bootstrap", async (req, res) => {
212
+ const buildPath = `${process.cwd()}/.joystick/build`;
213
+ const Component = req?.query?.pathToComponent ? await importFile(`${buildPath}/${req?.query?.pathToComponent}`) : null;
214
+ if (Component) {
215
+ const componentInstance = Component();
216
+ const apiForDataFunctions = await getAPIForDataFunctions(req, this?.options?.api);
217
+ const browserSafeRequest = getBrowserSafeRequest(req);
218
+ const browserSafeUser = getBrowserSafeUser(req?.context?.user);
219
+ const data = await getDataFromComponent(componentInstance, apiForDataFunctions, browserSafeUser, browserSafeRequest);
220
+ const translations = await getTranslations({ build: buildPath, page: req?.query?.pathToComponent }, req);
221
+ const settings = loadSettings();
222
+ return res.status(200).send({
223
+ data: {
224
+ [data?.componentId]: data?.data
225
+ },
226
+ req: browserSafeRequest,
227
+ settings,
228
+ translations
229
+ });
230
+ }
231
+ res.status(200).send({ data: {}, translations: {} });
232
+ });
233
+ this.express.app.get("/api/_test/process", async (req, res) => {
234
+ res.status(200).send({
235
+ test: process?.test || {}
236
+ });
237
+ });
238
+ this.express.app.post("/api/_test/accounts/signup", async (req, res) => {
239
+ const existingUser = await runUserQuery("user", { emailAddress: req?.body?.emailAddress });
240
+ if (existingUser) {
241
+ await runUserQuery("deleteUser", { userId: existingUser?._id || existingUser?.user_id });
242
+ }
243
+ const signup = await accounts.signup({
244
+ emailAddress: req?.body?.emailAddress,
245
+ password: req?.body?.password,
246
+ metadata: req?.body?.metadata,
247
+ output: req?.body?.output || defaultUserOutputFields
248
+ });
249
+ res.status(200).send(JSON.stringify({
250
+ ...signup?.user || {},
251
+ joystickLoginToken: signup?.token,
252
+ joystickLoginTokenExpiresAt: signup?.tokenExpiresAt
253
+ }));
254
+ });
255
+ this.express.app.delete("/api/_test/accounts", async (req, res) => {
256
+ await runUserQuery("deleteUser", { userId: req?.body?.userId });
257
+ res.status(200).send({ data: {} });
258
+ });
259
+ this.express.app.post("/api/_test/queues", async (req, res) => {
260
+ const queue = process?.queues[req?.body?.queue];
261
+ const job = this?.options?.queues[req?.body?.queue]?.jobs[req?.body?.job];
262
+ if (!queue) {
263
+ return res.status(404).send({ status: 404, error: `Queue ${req?.body?.queue} not found.` });
264
+ }
265
+ if (!job) {
266
+ return res.status(400).send({ status: 400, error: `Couldn't find a job called ${req?.body?.job} for the ${req?.body?.queue} queue.` });
267
+ }
268
+ await queue.handleNextJob({
269
+ _id: "joystick_test",
270
+ job: req?.body?.job,
271
+ payload: req?.body?.payload
272
+ });
273
+ res.status(200).send({ data: {} });
274
+ });
275
+ }
276
+ }
159
277
  initDeploy() {
160
278
  if (process.env.NODE_ENV === "production" && process.env.IS_PUSH_DEPLOYED) {
161
279
  this.express.app.get("/api/_push/pre-version", async (req, res) => {
162
- const instanceToken = fs.readFileSync("/root/token.txt", "utf-8");
280
+ const instanceToken = fs.readFileSync("/root/push/instance_token.txt", "utf-8");
163
281
  if (req?.headers["x-instance-token"] === instanceToken?.replace("\n", "")) {
164
282
  if (this.options?.events?.onBeforeDeployment && typeof this.options?.events?.onBeforeDeployment === "function") {
165
283
  await this.options.events.onBeforeDeployment(req?.query?.instance || "", req?.query?.version);
@@ -170,28 +288,12 @@ class App {
170
288
  return res.status(403).send("Sorry, you must pass a valid instance token to access this endpoint.");
171
289
  });
172
290
  this.express.app.get("/api/_push/health", async (req, res) => {
173
- const instanceToken = fs.readFileSync("/root/token.txt", "utf-8");
291
+ const instanceToken = fs.readFileSync("/root/push/instance_token.txt", "utf-8");
174
292
  if (req?.headers["x-instance-token"] === instanceToken?.replace("\n", "")) {
175
293
  return res.status(200).send("ok");
176
294
  }
177
295
  return res.status(403).send("Sorry, you must pass a valid instance token to access this endpoint.");
178
296
  });
179
- this.express.app.get("/api/_push/logs", async (req, res) => {
180
- const instanceToken = fs.readFileSync("/root/token.txt", "utf-8");
181
- if (req?.headers["x-instance-token"] === instanceToken?.replace("\n", "")) {
182
- const logs = execSync(`export NODE_ENV=production && instance logs${req?.query?.before ? ` --before ${req?.query?.before}` : ""}${req?.query?.after ? ` --after ${req?.query?.after}` : ""}`);
183
- return res.status(200).send(logs);
184
- }
185
- return res.status(403).send("Sorry, you must pass a valid instance token to access this endpoint.");
186
- });
187
- this.express.app.get("/api/_push/metrics", async (req, res) => {
188
- const instanceToken = fs.readFileSync("/root/token.txt", "utf-8");
189
- if (req?.headers["x-instance-token"] === instanceToken?.replace("\n", "")) {
190
- const metrics = execSync(`export NODE_ENV=production && instance metrics`);
191
- return res.status(200).send(metrics);
192
- }
193
- return res.status(403).send("Sorry, you must pass a valid instance token to access this endpoint.");
194
- });
195
297
  }
196
298
  }
197
299
  initAPI(api = {}) {
@@ -199,15 +301,15 @@ class App {
199
301
  const setters = api?.setters;
200
302
  const options = api?.options;
201
303
  const context = api?.context;
202
- if (getters && isObject(getters) && Object.keys(getters).length > 0) {
203
- registerGetters(this.express, Object.entries(getters), context, options, this);
304
+ if (getters && isObject(getters) && Object.keys(getters || {}).length > 0) {
305
+ registerGetters(this.express, Object.entries(getters || {}), context, options, this);
204
306
  }
205
- if (setters && isObject(setters) && Object.keys(setters).length > 0) {
206
- registerSetters(this.express, Object.entries(setters), context, options, this);
307
+ if (setters && isObject(setters) && Object.keys(setters || {}).length > 0) {
308
+ registerSetters(this.express, Object.entries(setters || {}), context, options, this);
207
309
  }
208
310
  }
209
311
  initRoutes(routes = {}) {
210
- Object.entries(routes).forEach(([path, callback]) => {
312
+ Object.entries(routes || {}).forEach(([path, callback]) => {
211
313
  const isObjectBasedRoute = path && callback && typeof callback === "object";
212
314
  const isFunctionBasedRoute = path && callback && typeof callback === "function";
213
315
  const method = callback?.method?.toLowerCase();
@@ -331,20 +433,29 @@ class App {
331
433
  path: "/api/_websockets/uploaders"
332
434
  })
333
435
  },
334
- ...Object.entries(userWebsockets).reduce((definitions = {}, [userWebsocketName, userWebsocketDefinition]) => {
436
+ ...Object.entries(userWebsockets || {}).reduce((definitions = {}, [userWebsocketName, userWebsocketDefinition]) => {
335
437
  definitions[userWebsocketName] = {
336
438
  server: new WebSocket.WebSocketServer({
337
439
  noServer: true,
338
440
  path: `/api/_websockets/${userWebsocketName}`
339
441
  }),
340
- onOpen: userWebsocketDefinition?.onOpen || null,
341
- onMessage: userWebsocketDefinition?.onMessage || null,
342
- onClose: userWebsocketDefinition?.onClose || null
442
+ onOpen: (...args) => {
443
+ trackFunctionCall(`node.websockets.${userWebsocketName}.onOpen`, args);
444
+ return userWebsocketDefinition?.onOpen ? userWebsocketDefinition?.onOpen(...args) : null;
445
+ },
446
+ onMessage: (...args) => {
447
+ trackFunctionCall(`node.websockets.${userWebsocketName}.onMessage`, args);
448
+ return userWebsocketDefinition?.onMessage ? userWebsocketDefinition?.onMessage(...args) : null;
449
+ },
450
+ onClose: (...args) => {
451
+ trackFunctionCall(`node.websockets.${userWebsocketName}.onClose`, args);
452
+ return userWebsocketDefinition?.onClose ? userWebsocketDefinition.onClose(...args) : null;
453
+ }
343
454
  };
344
455
  return definitions;
345
456
  }, {})
346
457
  };
347
- Object.entries(websocketServers).forEach(([websocketName, websocketDefinition]) => {
458
+ Object.entries(websocketServers || {}).forEach(([websocketName, websocketDefinition]) => {
348
459
  websocketDefinition.server.on("connection", function connection(websocketConnection, connectionRequest) {
349
460
  try {
350
461
  const [_path, params] = connectionRequest?.url?.split("?");
@@ -400,18 +511,7 @@ class App {
400
511
  }
401
512
  });
402
513
  }
403
- initDevelopmentRoutes() {
404
- if (process.env.NODE_ENV === "development") {
405
- this.express.app.get("/api/_joystick/sessions", async (req, res) => {
406
- const sessions = Array.from(this.sessions.entries())?.reduce((acc = {}, [key, value]) => {
407
- acc[key] = value;
408
- return acc;
409
- }, {});
410
- res.status(200).send(JSON.stringify(sessions));
411
- });
412
- }
413
- }
414
- initAccounts() {
514
+ initAccounts(options = {}) {
415
515
  this.express.app.get("/api/_accounts/authenticated", async (req, res) => {
416
516
  const loginTokenHasExpired = await hasLoginTokenExpired(res, req?.cookies?.joystickLoginToken, req?.cookies?.joystickLoginTokenExpiresAt);
417
517
  const status = !loginTokenHasExpired ? 200 : 401;
@@ -431,11 +531,20 @@ class App {
431
531
  metadata: req?.body?.metadata,
432
532
  output: req?.body?.output || defaultUserOutputFields
433
533
  });
434
- accounts._setAuthenticationCookie(res, {
435
- token: signup?.token,
436
- tokenExpiresAt: signup?.tokenExpiresAt
437
- });
438
- res.status(200).send(JSON.stringify(signup?.user || {}));
534
+ if (!process.env.NODE_ENV !== "test") {
535
+ accounts._setAuthenticationCookie(res, {
536
+ token: signup?.token,
537
+ tokenExpiresAt: signup?.tokenExpiresAt
538
+ });
539
+ }
540
+ const response = {
541
+ ...signup?.user || {}
542
+ };
543
+ if (process.env.NODE_ENV === "test") {
544
+ response.joystickToken = signup?.token;
545
+ response.joystickLoginTokenExpiresAt = signup?.tokenExpiresAt;
546
+ }
547
+ res.status(200).send(JSON.stringify(response));
439
548
  } catch (exception) {
440
549
  console.log(exception);
441
550
  return res.status(500).send(JSON.stringify({
@@ -451,11 +560,20 @@ class App {
451
560
  password: req?.body?.password,
452
561
  output: req?.body?.output || defaultUserOutputFields
453
562
  });
454
- accounts._setAuthenticationCookie(res, {
455
- token: login?.token,
456
- tokenExpiresAt: login?.tokenExpiresAt
457
- });
458
- res.status(200).send(JSON.stringify(login?.user || {}));
563
+ if (!process.env.NODE_ENV !== "test") {
564
+ accounts._setAuthenticationCookie(res, {
565
+ token: login?.token,
566
+ tokenExpiresAt: login?.tokenExpiresAt
567
+ });
568
+ }
569
+ const response = {
570
+ ...login?.user || {}
571
+ };
572
+ if (process.env.NODE_ENV === "test") {
573
+ response.joystickToken = login?.token;
574
+ response.joystickLoginTokenExpiresAt = login?.tokenExpiresAt;
575
+ }
576
+ res.status(200).send(JSON.stringify(response));
459
577
  } catch (exception) {
460
578
  console.log(exception);
461
579
  return res.status(500).send(JSON.stringify({
@@ -465,8 +583,10 @@ class App {
465
583
  });
466
584
  this.express.app.post("/api/_accounts/logout", async (req, res) => {
467
585
  try {
468
- this.sessions.delete(req?.context?.user?._id || req?.context?.user?.user_id);
469
586
  accounts._unsetAuthenticationCookie(res);
587
+ if (typeof options?.onLogout === "function") {
588
+ options.onLogout(req?.context?.user);
589
+ }
470
590
  res.status(200).send(JSON.stringify({}));
471
591
  } catch (exception) {
472
592
  console.log(exception);
@@ -523,7 +643,7 @@ class App {
523
643
  }
524
644
  initUploaders(uploaders = {}) {
525
645
  const { app } = this.express;
526
- Object.entries(uploaders).forEach(([uploaderName, uploaderOptions]) => {
646
+ Object.entries(uploaders || {}).forEach(([uploaderName, uploaderOptions]) => {
527
647
  const errors = validateUploaderOptions(uploaderOptions);
528
648
  if (errors?.length > 0) {
529
649
  log(errors, {
@@ -561,6 +681,11 @@ class App {
561
681
  req,
562
682
  uploads: validatedUploads
563
683
  });
684
+ trackFunctionCall(`node.uploaders.${uploaderName}.onBeforeUpload`, [{
685
+ input,
686
+ req,
687
+ uploads: validatedUploads
688
+ }]);
564
689
  }
565
690
  const fileSize = parseInt(req.headers["content-length"], 10);
566
691
  const providers = uploaderOptions?.providers?.includes("local") ? uploaderOptions?.providers.length : uploaderOptions?.providers?.length + 1;
@@ -578,6 +703,11 @@ class App {
578
703
  req,
579
704
  uploads
580
705
  });
706
+ trackFunctionCall(`node.uploaders.${uploaderName}.onAfterUpload`, [{
707
+ input,
708
+ req,
709
+ uploads
710
+ }]);
581
711
  }
582
712
  res.status(200).send(JSON.stringify({
583
713
  status: 200,
@@ -604,6 +734,11 @@ class App {
604
734
  }
605
735
  });
606
736
  }
737
+ initIndexes(indexes = null) {
738
+ if (indexes && typeof indexes === "function") {
739
+ indexes();
740
+ }
741
+ }
607
742
  initFixtures(fixtures = null) {
608
743
  if (fixtures && typeof fixtures === "function") {
609
744
  fixtures();
@@ -611,7 +746,7 @@ class App {
611
746
  }
612
747
  initQueues(queues = null) {
613
748
  if (queues && typeof queues === "object" && !Array.isArray(queues)) {
614
- const queueDefinitions = Object.entries(queues);
749
+ const queueDefinitions = Object.entries(queues || {});
615
750
  for (let i = 0; i < queueDefinitions.length; i += 1) {
616
751
  const [queueName, queueOptions] = queueDefinitions[i];
617
752
  process.queues = {
@@ -623,7 +758,7 @@ class App {
623
758
  }
624
759
  initCronJobs(cronJobs = null) {
625
760
  if (cronJobs && typeof cronJobs === "object" && !Array.isArray(cronJobs)) {
626
- const cronJobDefinitions = Object.entries(cronJobs);
761
+ const cronJobDefinitions = Object.entries(cronJobs || {});
627
762
  for (let i = 0; i < cronJobDefinitions.length; i += 1) {
628
763
  const [cronJobName, cronJobOptions] = cronJobDefinitions[i];
629
764
  if (cronJobOptions?.schedule && cronJobOptions?.run && typeof cronJobOptions?.run === "function") {
@@ -641,11 +776,23 @@ class App {
641
776
  }
642
777
  }
643
778
  }
779
+ ;
780
+ const handleStartApp = async (options = {}) => {
781
+ const app = new App(options);
782
+ await app.start(options);
783
+ return app;
784
+ };
644
785
  var app_default = (options = {}) => {
645
786
  return new Promise(async (resolve) => {
646
- const app = new App(options);
647
- await app.start(options);
648
- return resolve(app.express);
787
+ if (options?.cluster) {
788
+ cluster(async () => {
789
+ const app = await handleStartApp(options);
790
+ return resolve(app.express);
791
+ });
792
+ } else {
793
+ const app = await handleStartApp(options);
794
+ return resolve(app.express);
795
+ }
649
796
  });
650
797
  };
651
798
  export {
@@ -29,7 +29,7 @@ var initExpress_default = (onInit = () => {
29
29
  server
30
30
  };
31
31
  if (onInit) {
32
- onInit(instance);
32
+ onInit(instance, appInstance);
33
33
  }
34
34
  return instance;
35
35
  } catch (exception) {
@@ -27,7 +27,7 @@ var csp_default = (req, res, next, config = null) => {
27
27
  directiveDefaults["script-src"].push("'unsafe-eval'");
28
28
  directiveDefaults["connect-src"].push("ws:");
29
29
  }
30
- const directiveNames = Object.keys(directiveDefaults);
30
+ const directiveNames = Object.keys(directiveDefaults || {});
31
31
  for (let i = 0; i < directiveNames?.length; i += 1) {
32
32
  const directive = directiveNames[i];
33
33
  directiveDefaults[directive] = [
@@ -36,7 +36,7 @@ var csp_default = (req, res, next, config = null) => {
36
36
  ...directives[directive] || []
37
37
  ];
38
38
  }
39
- const csp = Object.keys(directiveDefaults).map((source) => {
39
+ const csp = Object.keys(directiveDefaults || {}).map((source) => {
40
40
  return `${source} ${directiveDefaults[source].join(" ")}`;
41
41
  })?.join("; ");
42
42
  res.setHeader("Content-Security-Policy", csp);
@@ -0,0 +1,64 @@
1
+ import fs from "fs";
2
+ import importFile from "../../lib/importFile.js";
3
+ import { isObject } from "../../validation/lib/typeValidators.js";
4
+ import settings from "../../settings/index.js";
5
+ const getTranslationsFile = async (languageFilePath = "", paths = "") => {
6
+ const languageFile = await importFile(`${paths.build}/i18n/${languageFilePath}`);
7
+ const isValidLanguageFile = languageFile && isObject(languageFile);
8
+ if (isValidLanguageFile) {
9
+ const translationsForPage = languageFile[paths.page];
10
+ return translationsForPage ? translationsForPage : languageFile;
11
+ }
12
+ return {};
13
+ };
14
+ const getLanguagePreferenceRegexes = (userLanguage = "", browserLanguages = []) => {
15
+ let languagePreferences = [];
16
+ if (userLanguage) {
17
+ languagePreferences.push(userLanguage);
18
+ }
19
+ const filteredBrowserLanguages = browserLanguages?.filter((language) => {
20
+ return !language?.includes("*");
21
+ });
22
+ languagePreferences.push(...filteredBrowserLanguages);
23
+ languagePreferences.push(settings?.config?.i18n?.defaultLanguage);
24
+ return languagePreferences?.flatMap((language) => {
25
+ const variants = [language];
26
+ if (language?.length === 2) {
27
+ variants.push(`${language.substring(0, 2)}-`);
28
+ }
29
+ if (language?.length > 2) {
30
+ variants.push(`${language?.split("-")[0]}`);
31
+ variants.push(`${language?.split("-")[0]}-`);
32
+ }
33
+ return variants;
34
+ })?.map((languageString) => {
35
+ const lastCharacter = languageString[languageString.length - 1];
36
+ if (lastCharacter === "-") {
37
+ return new RegExp(`^${languageString}[A-Z]+.js`, "g");
38
+ }
39
+ return new RegExp(`^${languageString}.js`, "g");
40
+ });
41
+ };
42
+ const parseBrowserLanguages = (languages = "") => {
43
+ const rawLanguages = languages.split(",");
44
+ return rawLanguages?.map((rawLanguage) => rawLanguage.split(";")[0]);
45
+ };
46
+ var getTranslations_default = async (paths = {}, req = {}) => {
47
+ const languageFiles = fs.readdirSync(`${paths.build}/i18n`);
48
+ const browserLanguages = parseBrowserLanguages(req?.headers["accept-language"]);
49
+ const languagePreferences = getLanguagePreferenceRegexes(req?.context?.user?.language, browserLanguages);
50
+ let matchingFile = null;
51
+ for (let i = 0; i < languagePreferences.length; i += 1) {
52
+ const languageRegex = languagePreferences[i];
53
+ const match = languageFiles.find((languageFile) => !!languageFile.match(languageRegex));
54
+ if (match) {
55
+ matchingFile = match;
56
+ break;
57
+ }
58
+ }
59
+ const translationsFile = await getTranslationsFile(matchingFile, paths);
60
+ return translationsFile;
61
+ };
62
+ export {
63
+ getTranslations_default as default
64
+ };
@@ -0,0 +1,71 @@
1
+ var get_insecure_landing_page_html_default = (hostname = "", path = "") => {
2
+ const url = `https://${hostname}${path}`;
3
+ return `
4
+ <html>
5
+ <head>
6
+ <title>Insecure Connection</title>
7
+ <style type="text/css">
8
+ body {
9
+ font-family: "Helvetica Neue", "Helvetica", "Arial", sans-serif;
10
+ font-size: 16px;
11
+ line-height: 24px;
12
+ background: #fafafa;
13
+ display: flex;
14
+ margin: 0;
15
+ padding: 20px;
16
+ justify-content: center;
17
+ }
18
+
19
+ .warning {
20
+ width: 100%;
21
+ max-width: 500px;
22
+ background: #fff;
23
+ border: 1px solid #eee;
24
+ border-radius: 3px;
25
+ padding: 35px;
26
+ align-self: flex-start;
27
+ }
28
+
29
+ .warning h1 {
30
+ color: #000;
31
+ font-size: 22px;
32
+ line-height: 30px;
33
+ margin: 0;
34
+ }
35
+
36
+ .warning p {
37
+ font-size: 16px;
38
+ line-height: 25px;
39
+ font-weight: 400;
40
+ color: #555;
41
+ margin: 10px 0 0;
42
+ }
43
+
44
+ .warning p a {
45
+ color: #555;
46
+ }
47
+
48
+ @media screen and (min-width: 768px) {
49
+ body {
50
+ padding: 40px;
51
+ }
52
+ }
53
+ </style>
54
+ </head>
55
+ <body>
56
+ <div class="warning">
57
+ <h1>Insecure Connection<h1>
58
+ <p>The site at this URL requires an SSL-secured connection. Please visit <a href="${url}">${url}</a> instead. You will be automatically redirected in 15 seconds.</p>
59
+ </div>
60
+ <script>
61
+ setTimeout(() => {
62
+ location.href = '${url}';
63
+ }, 15 * 1000);
64
+ <\/script>
65
+ </body>
66
+ </html>
67
+ `;
68
+ };
69
+ export {
70
+ get_insecure_landing_page_html_default as default
71
+ };