@joystick.js/node-canary 0.0.0-canary.30 → 0.0.0-canary.301
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/dist/action/class.js +21 -0
- package/dist/api/get.js +15 -13
- package/dist/api/set.js +15 -13
- package/dist/app/accounts/createMetadataTableColumns.js +12 -0
- package/dist/app/accounts/deleteUser.js +7 -0
- package/dist/app/accounts/generateSession.js +2 -4
- package/dist/app/accounts/getBrowserSafeUser.js +5 -2
- package/dist/app/accounts/hasLoginTokenExpired.js +1 -2
- package/dist/app/accounts/index.js +2 -0
- package/dist/app/accounts/login.js +6 -0
- package/dist/app/accounts/recoverPassword.js +3 -0
- package/dist/app/accounts/resetPassword.js +6 -0
- package/dist/app/accounts/signup.js +50 -7
- package/dist/app/accounts/verifyEmail.js +3 -0
- package/dist/app/cluster.js +26 -0
- package/dist/app/databases/mongodb/buildConnectionString.js +1 -1
- package/dist/app/databases/mongodb/createAccountsIndexes.js +18 -0
- package/dist/app/databases/mongodb/createSessionsIndexes.js +10 -0
- package/dist/app/databases/mongodb/index.js +1 -1
- package/dist/app/databases/mongodb/queries/accounts.js +8 -1
- package/dist/app/databases/mongodb/queries/queues.js +34 -25
- package/dist/app/databases/mongodb/queries/sessions.js +26 -0
- package/dist/app/databases/postgresql/createSessionsIndexes.js +10 -0
- package/dist/app/databases/postgresql/createSessionsTables.js +14 -0
- package/dist/app/databases/postgresql/handleCleanupQueues.js +35 -0
- package/dist/app/databases/postgresql/index.js +9 -2
- package/dist/app/databases/postgresql/queries/accounts.js +5 -2
- package/dist/app/databases/postgresql/queries/queues.js +69 -36
- package/dist/app/databases/postgresql/queries/sessions.js +43 -0
- package/dist/app/databases/queryMap.js +6 -2
- package/dist/app/databases/stringToSnakeCase.js +6 -0
- package/dist/app/getBrowserSafeRequest.js +3 -2
- package/dist/app/index.js +214 -73
- package/dist/app/initExpress.js +1 -1
- package/dist/app/middleware/csp.js +2 -2
- package/dist/app/middleware/getTranslations.js +64 -0
- package/dist/app/middleware/get_insecure_landing_page_html.js +71 -0
- package/dist/app/middleware/hmr/client.js +13 -9
- package/dist/app/middleware/index.js +6 -5
- package/dist/app/middleware/insecure.js +3 -4
- package/dist/app/middleware/render.js +154 -0
- package/dist/app/middleware/session.js +12 -11
- package/dist/app/queues/index.js +74 -27
- package/dist/app/registerGetters.js +5 -6
- package/dist/app/registerSetters.js +5 -6
- package/dist/app/runGetter.js +17 -5
- package/dist/app/runSessionQuery.js +15 -0
- package/dist/app/runSetter.js +17 -5
- package/dist/app/sanitizeAPIResponse.js +1 -1
- package/dist/app/validateSession.js +8 -3
- package/dist/app/validateUploaderOptions.js +3 -3
- package/dist/app/validateUploads.js +12 -1
- package/dist/email/send.js +7 -1
- package/dist/email/templates/reset-password.js +0 -1
- package/dist/fixture/index.js +40 -0
- package/dist/index.js +15 -0
- package/dist/lib/escapeKeyValuePair.js +13 -0
- package/dist/lib/formatAPIError.js +0 -1
- package/dist/lib/getBuildPath.js +1 -1
- package/dist/lib/getSSLCertificates.js +3 -3
- package/dist/lib/importFile.js +7 -0
- package/dist/lib/isValidJSONString.js +1 -1
- package/dist/lib/log.js +0 -3
- package/dist/lib/objectToSQLKeysString.js +1 -1
- package/dist/lib/objectToSQLValuesString.js +1 -1
- package/dist/lib/serializeQueryParameters.js +1 -1
- package/dist/lib/timestamps.js +47 -0
- package/dist/lib/wait.js +8 -0
- package/dist/push/logs/index.js +6 -1
- package/dist/settings/load.js +3 -5
- package/dist/ssr/compileCSS.js +4 -4
- package/dist/ssr/findComponentInTree.js +1 -1
- package/dist/ssr/getAPIForDataFunctions.js +35 -0
- package/dist/ssr/getDataFromComponent.js +15 -0
- package/dist/ssr/index.js +19 -45
- package/dist/ssr/replaceWhenTags.js +2 -3
- package/dist/ssr/setHeadTagsInHTML.js +3 -3
- package/dist/test/index.js +9 -0
- package/dist/test/trackFunctionCall.js +17 -0
- package/dist/validation/inputWithSchema/index.js +3 -3
- package/dist/validation/schema/index.js +5 -5
- package/dist/websockets/index.js +4 -0
- package/getSanitizedContext.js +43 -0
- package/package.json +2 -1
- package/dist/app/accounts/roles/index.test.js +0 -123
- package/dist/app/index.test.js +0 -575
- package/dist/app/middleware/sanitizeRequestParameters.js +0 -21
- package/dist/email/send.test.js +0 -37
- 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.
|
|
55
|
-
this.
|
|
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
|
-
|
|
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,
|
|
@@ -131,19 +154,40 @@ class App {
|
|
|
131
154
|
if (hasQueuesDatabase) {
|
|
132
155
|
process.databases._queues = getTargetDatabaseConnection("queues")?.connection;
|
|
133
156
|
}
|
|
157
|
+
if (hasSessionsDatabase) {
|
|
158
|
+
process.databases._sessions = getTargetDatabaseConnection("sessions")?.connection;
|
|
159
|
+
}
|
|
160
|
+
if (hasMongoDBUsersDatabase) {
|
|
161
|
+
await createMongoDBAccountsIndexes();
|
|
162
|
+
}
|
|
163
|
+
if (hasMongoDBSessionsDatabase) {
|
|
164
|
+
await createMongoDBSessionsIndexes();
|
|
165
|
+
}
|
|
134
166
|
if (hasPostgreSQLUsersDatabase) {
|
|
135
167
|
await createPostgreSQLAccountsTables();
|
|
136
168
|
await createPostgreSQLAccountsIndexes();
|
|
137
169
|
}
|
|
170
|
+
if (hasPostgreSQLSessionsDatabase) {
|
|
171
|
+
await createPostgreSQLSessionsTables();
|
|
172
|
+
await createPostgreSQLSessionsIndexes();
|
|
173
|
+
}
|
|
138
174
|
return process.databases;
|
|
139
175
|
}
|
|
140
|
-
onStartApp(express = {}) {
|
|
176
|
+
onStartApp(express = {}, joystick_app_instance = {}) {
|
|
141
177
|
process.on("message", (message) => {
|
|
142
178
|
if (typeof message === "string") {
|
|
143
|
-
|
|
179
|
+
const parsed_message = JSON.parse(message);
|
|
180
|
+
if (parsed_message?.type === "BUILD_ERROR") {
|
|
181
|
+
process.BUILD_ERROR = JSON.parse(message);
|
|
182
|
+
}
|
|
183
|
+
if (parsed_message?.type === "RESTART_SERVER") {
|
|
184
|
+
console.log("HANDLE RESTART");
|
|
185
|
+
}
|
|
144
186
|
}
|
|
145
187
|
});
|
|
146
|
-
|
|
188
|
+
if (process.env.NODE_ENV !== "test") {
|
|
189
|
+
console.log(`App running at: http://localhost:${express.port}`);
|
|
190
|
+
}
|
|
147
191
|
}
|
|
148
192
|
setMachineId() {
|
|
149
193
|
generateMachineId();
|
|
@@ -156,10 +200,78 @@ class App {
|
|
|
156
200
|
fs.writeFileSync("./.joystick/PROCESS_ID", `${generateId(32)}`);
|
|
157
201
|
}
|
|
158
202
|
}
|
|
203
|
+
initTests() {
|
|
204
|
+
if (process.env.NODE_ENV === "test") {
|
|
205
|
+
this.express.app.get("/api/_test/bootstrap", async (req, res) => {
|
|
206
|
+
const buildPath = `${process.cwd()}/.joystick/build`;
|
|
207
|
+
const Component = req?.query?.pathToComponent ? await importFile(`${buildPath}/${req?.query?.pathToComponent}`) : null;
|
|
208
|
+
if (Component) {
|
|
209
|
+
const componentInstance = Component();
|
|
210
|
+
const apiForDataFunctions = await getAPIForDataFunctions(req, this?.options?.api);
|
|
211
|
+
const browserSafeRequest = getBrowserSafeRequest(req);
|
|
212
|
+
const browserSafeUser = getBrowserSafeUser(req?.context?.user);
|
|
213
|
+
const data = await getDataFromComponent(componentInstance, apiForDataFunctions, browserSafeUser, browserSafeRequest);
|
|
214
|
+
const translations = await getTranslations({ build: buildPath, page: req?.query?.pathToComponent }, req);
|
|
215
|
+
const settings = loadSettings();
|
|
216
|
+
return res.status(200).send({
|
|
217
|
+
data: {
|
|
218
|
+
[data?.componentId]: data?.data
|
|
219
|
+
},
|
|
220
|
+
req: browserSafeRequest,
|
|
221
|
+
settings,
|
|
222
|
+
translations
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
res.status(200).send({ data: {}, translations: {} });
|
|
226
|
+
});
|
|
227
|
+
this.express.app.get("/api/_test/process", async (req, res) => {
|
|
228
|
+
res.status(200).send({
|
|
229
|
+
test: process?.test || {}
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
this.express.app.post("/api/_test/accounts/signup", async (req, res) => {
|
|
233
|
+
const existingUser = await runUserQuery("user", { emailAddress: req?.body?.emailAddress });
|
|
234
|
+
if (existingUser) {
|
|
235
|
+
await runUserQuery("deleteUser", { userId: existingUser?._id || existingUser?.user_id });
|
|
236
|
+
}
|
|
237
|
+
const signup = await accounts.signup({
|
|
238
|
+
emailAddress: req?.body?.emailAddress,
|
|
239
|
+
password: req?.body?.password,
|
|
240
|
+
metadata: req?.body?.metadata,
|
|
241
|
+
output: req?.body?.output || defaultUserOutputFields
|
|
242
|
+
});
|
|
243
|
+
res.status(200).send(JSON.stringify({
|
|
244
|
+
...signup?.user || {},
|
|
245
|
+
joystickLoginToken: signup?.token,
|
|
246
|
+
joystickLoginTokenExpiresAt: signup?.tokenExpiresAt
|
|
247
|
+
}));
|
|
248
|
+
});
|
|
249
|
+
this.express.app.delete("/api/_test/accounts", async (req, res) => {
|
|
250
|
+
await runUserQuery("deleteUser", { userId: req?.body?.userId });
|
|
251
|
+
res.status(200).send({ data: {} });
|
|
252
|
+
});
|
|
253
|
+
this.express.app.post("/api/_test/queues", async (req, res) => {
|
|
254
|
+
const queue = process?.queues[req?.body?.queue];
|
|
255
|
+
const job = this?.options?.queues[req?.body?.queue]?.jobs[req?.body?.job];
|
|
256
|
+
if (!queue) {
|
|
257
|
+
return res.status(404).send({ status: 404, error: `Queue ${req?.body?.queue} not found.` });
|
|
258
|
+
}
|
|
259
|
+
if (!job) {
|
|
260
|
+
return res.status(400).send({ status: 400, error: `Couldn't find a job called ${req?.body?.job} for the ${req?.body?.queue} queue.` });
|
|
261
|
+
}
|
|
262
|
+
await queue.handleNextJob({
|
|
263
|
+
_id: "joystick_test",
|
|
264
|
+
job: req?.body?.job,
|
|
265
|
+
payload: req?.body?.payload
|
|
266
|
+
});
|
|
267
|
+
res.status(200).send({ data: {} });
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
}
|
|
159
271
|
initDeploy() {
|
|
160
272
|
if (process.env.NODE_ENV === "production" && process.env.IS_PUSH_DEPLOYED) {
|
|
161
273
|
this.express.app.get("/api/_push/pre-version", async (req, res) => {
|
|
162
|
-
const instanceToken = fs.readFileSync("/root/
|
|
274
|
+
const instanceToken = fs.readFileSync("/root/push/instance_token.txt", "utf-8");
|
|
163
275
|
if (req?.headers["x-instance-token"] === instanceToken?.replace("\n", "")) {
|
|
164
276
|
if (this.options?.events?.onBeforeDeployment && typeof this.options?.events?.onBeforeDeployment === "function") {
|
|
165
277
|
await this.options.events.onBeforeDeployment(req?.query?.instance || "", req?.query?.version);
|
|
@@ -170,28 +282,12 @@ class App {
|
|
|
170
282
|
return res.status(403).send("Sorry, you must pass a valid instance token to access this endpoint.");
|
|
171
283
|
});
|
|
172
284
|
this.express.app.get("/api/_push/health", async (req, res) => {
|
|
173
|
-
const instanceToken = fs.readFileSync("/root/
|
|
285
|
+
const instanceToken = fs.readFileSync("/root/push/instance_token.txt", "utf-8");
|
|
174
286
|
if (req?.headers["x-instance-token"] === instanceToken?.replace("\n", "")) {
|
|
175
287
|
return res.status(200).send("ok");
|
|
176
288
|
}
|
|
177
289
|
return res.status(403).send("Sorry, you must pass a valid instance token to access this endpoint.");
|
|
178
290
|
});
|
|
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
291
|
}
|
|
196
292
|
}
|
|
197
293
|
initAPI(api = {}) {
|
|
@@ -199,15 +295,15 @@ class App {
|
|
|
199
295
|
const setters = api?.setters;
|
|
200
296
|
const options = api?.options;
|
|
201
297
|
const context = api?.context;
|
|
202
|
-
if (getters && isObject(getters) && Object.keys(getters).length > 0) {
|
|
203
|
-
registerGetters(this.express, Object.entries(getters), context, options, this);
|
|
298
|
+
if (getters && isObject(getters) && Object.keys(getters || {}).length > 0) {
|
|
299
|
+
registerGetters(this.express, Object.entries(getters || {}), context, options, this);
|
|
204
300
|
}
|
|
205
|
-
if (setters && isObject(setters) && Object.keys(setters).length > 0) {
|
|
206
|
-
registerSetters(this.express, Object.entries(setters), context, options, this);
|
|
301
|
+
if (setters && isObject(setters) && Object.keys(setters || {}).length > 0) {
|
|
302
|
+
registerSetters(this.express, Object.entries(setters || {}), context, options, this);
|
|
207
303
|
}
|
|
208
304
|
}
|
|
209
305
|
initRoutes(routes = {}) {
|
|
210
|
-
Object.entries(routes).forEach(([path, callback]) => {
|
|
306
|
+
Object.entries(routes || {}).forEach(([path, callback]) => {
|
|
211
307
|
const isObjectBasedRoute = path && callback && typeof callback === "object";
|
|
212
308
|
const isFunctionBasedRoute = path && callback && typeof callback === "function";
|
|
213
309
|
const method = callback?.method?.toLowerCase();
|
|
@@ -331,20 +427,29 @@ class App {
|
|
|
331
427
|
path: "/api/_websockets/uploaders"
|
|
332
428
|
})
|
|
333
429
|
},
|
|
334
|
-
...Object.entries(userWebsockets).reduce((definitions = {}, [userWebsocketName, userWebsocketDefinition]) => {
|
|
430
|
+
...Object.entries(userWebsockets || {}).reduce((definitions = {}, [userWebsocketName, userWebsocketDefinition]) => {
|
|
335
431
|
definitions[userWebsocketName] = {
|
|
336
432
|
server: new WebSocket.WebSocketServer({
|
|
337
433
|
noServer: true,
|
|
338
434
|
path: `/api/_websockets/${userWebsocketName}`
|
|
339
435
|
}),
|
|
340
|
-
onOpen:
|
|
341
|
-
|
|
342
|
-
|
|
436
|
+
onOpen: (...args) => {
|
|
437
|
+
trackFunctionCall(`node.websockets.${userWebsocketName}.onOpen`, args);
|
|
438
|
+
return userWebsocketDefinition?.onOpen ? userWebsocketDefinition?.onOpen(...args) : null;
|
|
439
|
+
},
|
|
440
|
+
onMessage: (...args) => {
|
|
441
|
+
trackFunctionCall(`node.websockets.${userWebsocketName}.onMessage`, args);
|
|
442
|
+
return userWebsocketDefinition?.onMessage ? userWebsocketDefinition?.onMessage(...args) : null;
|
|
443
|
+
},
|
|
444
|
+
onClose: (...args) => {
|
|
445
|
+
trackFunctionCall(`node.websockets.${userWebsocketName}.onClose`, args);
|
|
446
|
+
return userWebsocketDefinition?.onClose ? userWebsocketDefinition.onClose(...args) : null;
|
|
447
|
+
}
|
|
343
448
|
};
|
|
344
449
|
return definitions;
|
|
345
450
|
}, {})
|
|
346
451
|
};
|
|
347
|
-
Object.entries(websocketServers).forEach(([websocketName, websocketDefinition]) => {
|
|
452
|
+
Object.entries(websocketServers || {}).forEach(([websocketName, websocketDefinition]) => {
|
|
348
453
|
websocketDefinition.server.on("connection", function connection(websocketConnection, connectionRequest) {
|
|
349
454
|
try {
|
|
350
455
|
const [_path, params] = connectionRequest?.url?.split("?");
|
|
@@ -400,18 +505,7 @@ class App {
|
|
|
400
505
|
}
|
|
401
506
|
});
|
|
402
507
|
}
|
|
403
|
-
|
|
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() {
|
|
508
|
+
initAccounts(options = {}) {
|
|
415
509
|
this.express.app.get("/api/_accounts/authenticated", async (req, res) => {
|
|
416
510
|
const loginTokenHasExpired = await hasLoginTokenExpired(res, req?.cookies?.joystickLoginToken, req?.cookies?.joystickLoginTokenExpiresAt);
|
|
417
511
|
const status = !loginTokenHasExpired ? 200 : 401;
|
|
@@ -431,11 +525,20 @@ class App {
|
|
|
431
525
|
metadata: req?.body?.metadata,
|
|
432
526
|
output: req?.body?.output || defaultUserOutputFields
|
|
433
527
|
});
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
528
|
+
if (!process.env.NODE_ENV !== "test") {
|
|
529
|
+
accounts._setAuthenticationCookie(res, {
|
|
530
|
+
token: signup?.token,
|
|
531
|
+
tokenExpiresAt: signup?.tokenExpiresAt
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
const response = {
|
|
535
|
+
...signup?.user || {}
|
|
536
|
+
};
|
|
537
|
+
if (process.env.NODE_ENV === "test") {
|
|
538
|
+
response.joystickToken = signup?.token;
|
|
539
|
+
response.joystickLoginTokenExpiresAt = signup?.tokenExpiresAt;
|
|
540
|
+
}
|
|
541
|
+
res.status(200).send(JSON.stringify(response));
|
|
439
542
|
} catch (exception) {
|
|
440
543
|
console.log(exception);
|
|
441
544
|
return res.status(500).send(JSON.stringify({
|
|
@@ -451,11 +554,20 @@ class App {
|
|
|
451
554
|
password: req?.body?.password,
|
|
452
555
|
output: req?.body?.output || defaultUserOutputFields
|
|
453
556
|
});
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
557
|
+
if (!process.env.NODE_ENV !== "test") {
|
|
558
|
+
accounts._setAuthenticationCookie(res, {
|
|
559
|
+
token: login?.token,
|
|
560
|
+
tokenExpiresAt: login?.tokenExpiresAt
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
const response = {
|
|
564
|
+
...login?.user || {}
|
|
565
|
+
};
|
|
566
|
+
if (process.env.NODE_ENV === "test") {
|
|
567
|
+
response.joystickToken = login?.token;
|
|
568
|
+
response.joystickLoginTokenExpiresAt = login?.tokenExpiresAt;
|
|
569
|
+
}
|
|
570
|
+
res.status(200).send(JSON.stringify(response));
|
|
459
571
|
} catch (exception) {
|
|
460
572
|
console.log(exception);
|
|
461
573
|
return res.status(500).send(JSON.stringify({
|
|
@@ -465,8 +577,10 @@ class App {
|
|
|
465
577
|
});
|
|
466
578
|
this.express.app.post("/api/_accounts/logout", async (req, res) => {
|
|
467
579
|
try {
|
|
468
|
-
this.sessions.delete(req?.context?.user?._id || req?.context?.user?.user_id);
|
|
469
580
|
accounts._unsetAuthenticationCookie(res);
|
|
581
|
+
if (typeof options?.onLogout === "function") {
|
|
582
|
+
options.onLogout(req?.context?.user);
|
|
583
|
+
}
|
|
470
584
|
res.status(200).send(JSON.stringify({}));
|
|
471
585
|
} catch (exception) {
|
|
472
586
|
console.log(exception);
|
|
@@ -523,7 +637,7 @@ class App {
|
|
|
523
637
|
}
|
|
524
638
|
initUploaders(uploaders = {}) {
|
|
525
639
|
const { app } = this.express;
|
|
526
|
-
Object.entries(uploaders).forEach(([uploaderName, uploaderOptions]) => {
|
|
640
|
+
Object.entries(uploaders || {}).forEach(([uploaderName, uploaderOptions]) => {
|
|
527
641
|
const errors = validateUploaderOptions(uploaderOptions);
|
|
528
642
|
if (errors?.length > 0) {
|
|
529
643
|
log(errors, {
|
|
@@ -561,6 +675,11 @@ class App {
|
|
|
561
675
|
req,
|
|
562
676
|
uploads: validatedUploads
|
|
563
677
|
});
|
|
678
|
+
trackFunctionCall(`node.uploaders.${uploaderName}.onBeforeUpload`, [{
|
|
679
|
+
input,
|
|
680
|
+
req,
|
|
681
|
+
uploads: validatedUploads
|
|
682
|
+
}]);
|
|
564
683
|
}
|
|
565
684
|
const fileSize = parseInt(req.headers["content-length"], 10);
|
|
566
685
|
const providers = uploaderOptions?.providers?.includes("local") ? uploaderOptions?.providers.length : uploaderOptions?.providers?.length + 1;
|
|
@@ -578,6 +697,11 @@ class App {
|
|
|
578
697
|
req,
|
|
579
698
|
uploads
|
|
580
699
|
});
|
|
700
|
+
trackFunctionCall(`node.uploaders.${uploaderName}.onAfterUpload`, [{
|
|
701
|
+
input,
|
|
702
|
+
req,
|
|
703
|
+
uploads
|
|
704
|
+
}]);
|
|
581
705
|
}
|
|
582
706
|
res.status(200).send(JSON.stringify({
|
|
583
707
|
status: 200,
|
|
@@ -604,6 +728,11 @@ class App {
|
|
|
604
728
|
}
|
|
605
729
|
});
|
|
606
730
|
}
|
|
731
|
+
initIndexes(indexes = null) {
|
|
732
|
+
if (indexes && typeof indexes === "function") {
|
|
733
|
+
indexes();
|
|
734
|
+
}
|
|
735
|
+
}
|
|
607
736
|
initFixtures(fixtures = null) {
|
|
608
737
|
if (fixtures && typeof fixtures === "function") {
|
|
609
738
|
fixtures();
|
|
@@ -611,7 +740,7 @@ class App {
|
|
|
611
740
|
}
|
|
612
741
|
initQueues(queues = null) {
|
|
613
742
|
if (queues && typeof queues === "object" && !Array.isArray(queues)) {
|
|
614
|
-
const queueDefinitions = Object.entries(queues);
|
|
743
|
+
const queueDefinitions = Object.entries(queues || {});
|
|
615
744
|
for (let i = 0; i < queueDefinitions.length; i += 1) {
|
|
616
745
|
const [queueName, queueOptions] = queueDefinitions[i];
|
|
617
746
|
process.queues = {
|
|
@@ -623,7 +752,7 @@ class App {
|
|
|
623
752
|
}
|
|
624
753
|
initCronJobs(cronJobs = null) {
|
|
625
754
|
if (cronJobs && typeof cronJobs === "object" && !Array.isArray(cronJobs)) {
|
|
626
|
-
const cronJobDefinitions = Object.entries(cronJobs);
|
|
755
|
+
const cronJobDefinitions = Object.entries(cronJobs || {});
|
|
627
756
|
for (let i = 0; i < cronJobDefinitions.length; i += 1) {
|
|
628
757
|
const [cronJobName, cronJobOptions] = cronJobDefinitions[i];
|
|
629
758
|
if (cronJobOptions?.schedule && cronJobOptions?.run && typeof cronJobOptions?.run === "function") {
|
|
@@ -641,11 +770,23 @@ class App {
|
|
|
641
770
|
}
|
|
642
771
|
}
|
|
643
772
|
}
|
|
773
|
+
;
|
|
774
|
+
const handleStartApp = async (options = {}) => {
|
|
775
|
+
const app = new App(options);
|
|
776
|
+
await app.start(options);
|
|
777
|
+
return app;
|
|
778
|
+
};
|
|
644
779
|
var app_default = (options = {}) => {
|
|
645
780
|
return new Promise(async (resolve) => {
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
781
|
+
if (options?.cluster) {
|
|
782
|
+
cluster(async () => {
|
|
783
|
+
const app = await handleStartApp(options);
|
|
784
|
+
return resolve(app.express);
|
|
785
|
+
});
|
|
786
|
+
} else {
|
|
787
|
+
const app = await handleStartApp(options);
|
|
788
|
+
return resolve(app.express);
|
|
789
|
+
}
|
|
649
790
|
});
|
|
650
791
|
};
|
|
651
792
|
export {
|
package/dist/app/initExpress.js
CHANGED
|
@@ -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
|
+
};
|