@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.
- 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/generate_sql_from_object.js +45 -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 +40 -26
- 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 +66 -4
- 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 +222 -75
- 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 +11 -68
- package/dist/app/middleware/session.js +12 -11
- package/dist/app/queues/index.js +64 -28
- 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 +19 -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 +10 -3
- 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,
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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/
|
|
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/
|
|
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:
|
|
341
|
-
|
|
342
|
-
|
|
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
|
-
|
|
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
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
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
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
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
|
-
|
|
647
|
-
|
|
648
|
-
|
|
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 {
|
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
|
+
};
|