@joystick.js/node-canary 0.0.0-canary.7 → 0.0.0-canary.71
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 -1
- 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 +2 -1
- package/dist/app/accounts/hasLoginTokenExpired.js +1 -2
- package/dist/app/accounts/index.js +2 -0
- package/dist/app/accounts/signup.js +30 -4
- package/dist/app/databases/mongodb/index.js +1 -1
- package/dist/app/databases/mongodb/queries/accounts.js +7 -0
- package/dist/app/databases/postgresql/queries/accounts.js +3 -0
- package/dist/app/databases/stringToSnakeCase.js +6 -0
- package/dist/app/getBrowserSafeRequest.js +3 -2
- package/dist/app/index.js +66 -12
- package/dist/app/middleware/getTranslations.js +64 -0
- package/dist/app/middleware/index.js +4 -5
- package/dist/app/middleware/render.js +11 -68
- package/dist/app/sanitizeAPIResponse.js +1 -6
- package/dist/app/validateSession.js +3 -0
- package/dist/email/templates/reset-password.js +0 -1
- package/dist/index.js +7 -2
- package/dist/lib/escapeHTML.js +9 -0
- package/dist/lib/escapeKeyValuePair.js +13 -0
- package/dist/lib/getBuildPath.js +1 -1
- package/dist/lib/getSSLCertificates.js +1 -1
- package/dist/lib/importFile.js +7 -0
- package/dist/lib/log.js +0 -3
- package/dist/lib/nodeUrlPolyfills.js +7 -14
- package/dist/ssr/getAPIForDataFunctions.js +33 -0
- package/dist/ssr/getDataFromComponent.js +14 -0
- package/dist/ssr/index.js +5 -41
- 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/sanitizeQueryParameters.js +0 -16
- package/dist/email/send.test.js +0 -37
package/_package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@joystick.js/node",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.166",
|
|
4
4
|
"developmentVersion": "1.0.0-beta.1419",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "A Node.js framework for building web apps.",
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"node-html-parser": "^5.1.0",
|
|
40
40
|
"nodemailer": "^6.7.0",
|
|
41
41
|
"pg": "^8.7.3",
|
|
42
|
+
"pg-escape": "^0.2.0",
|
|
42
43
|
"process": "^0.11.10",
|
|
43
44
|
"query-string": "^7.0.1",
|
|
44
45
|
"sanitize-html": "^2.7.3",
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import escape from "pg-escape";
|
|
2
|
+
var createMetadataTableColumns_default = async (usersDatabase = "", sqlizedMetadata = {}) => {
|
|
3
|
+
if (usersDatabase === "postgresql") {
|
|
4
|
+
const columns = Object.keys(sqlizedMetadata);
|
|
5
|
+
for (let i = 0; i < columns?.length; i += 1) {
|
|
6
|
+
await process.databases.postgresql.query(escape(`ALTER TABLE users ADD COLUMN IF NOT EXISTS %I TEXT;`, columns[i]));
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
export {
|
|
11
|
+
createMetadataTableColumns_default as default
|
|
12
|
+
};
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import dayjs from "dayjs";
|
|
2
|
+
import utc from "dayjs/plugin/utc.js";
|
|
2
3
|
import generateId from "../../lib/generateId";
|
|
3
|
-
|
|
4
|
-
const utc = await import("dayjs/plugin/utc");
|
|
5
|
-
dayjs.extend(utc.default);
|
|
6
|
-
}
|
|
4
|
+
dayjs.extend(utc);
|
|
7
5
|
var generateSession_default = () => {
|
|
8
6
|
return {
|
|
9
7
|
token: generateId(64),
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isObject } from "../../validation/lib/typeValidators";
|
|
2
|
+
import escapeKeyValuePair from "../../lib/escapeKeyValuePair.js";
|
|
2
3
|
var getBrowserSafeUser_default = (user = null) => {
|
|
3
4
|
if (!user || !isObject(user)) {
|
|
4
5
|
return null;
|
|
@@ -16,7 +17,7 @@ var getBrowserSafeUser_default = (user = null) => {
|
|
|
16
17
|
return fields;
|
|
17
18
|
}
|
|
18
19
|
}, {});
|
|
19
|
-
return browserSafeUser;
|
|
20
|
+
return escapeKeyValuePair(browserSafeUser);
|
|
20
21
|
};
|
|
21
22
|
export {
|
|
22
23
|
getBrowserSafeUser_default as default
|
|
@@ -5,8 +5,7 @@ var hasLoginTokenExpired_default = async (res, token = null, tokenExpiresAt = nu
|
|
|
5
5
|
unsetAuthenticationCookie(res);
|
|
6
6
|
return true;
|
|
7
7
|
}
|
|
8
|
-
const
|
|
9
|
-
const hasExpired = process.env.NODE_ENV === "test" ? _dayjs().isAfter(_dayjs(tokenExpiresAt)) : dayjs().isAfter(dayjs(tokenExpiresAt));
|
|
8
|
+
const hasExpired = dayjs().isAfter(dayjs(tokenExpiresAt));
|
|
10
9
|
if (hasExpired) {
|
|
11
10
|
unsetAuthenticationCookie(res);
|
|
12
11
|
return true;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import _setAuthenticationCookie from "./setAuthenticationCookie.js";
|
|
2
2
|
import _unsetAuthenticationCookie from "./unsetAuthenticationCookie.js";
|
|
3
3
|
import defaultUserOutputFields from "./defaultUserOutputFields";
|
|
4
|
+
import deleteUser from "./deleteUser.js";
|
|
4
5
|
import getBrowserSafeUser from "./getBrowserSafeUser";
|
|
5
6
|
import login from "./login";
|
|
6
7
|
import recoverPassword from "./recoverPassword";
|
|
@@ -14,6 +15,7 @@ var accounts_default = {
|
|
|
14
15
|
_setAuthenticationCookie,
|
|
15
16
|
_unsetAuthenticationCookie,
|
|
16
17
|
defaultUserOutputFields,
|
|
18
|
+
deleteUser,
|
|
17
19
|
getBrowserSafeUser,
|
|
18
20
|
login,
|
|
19
21
|
recoverPassword,
|
|
@@ -6,6 +6,9 @@ import { isObject } from "../../validation/lib/typeValidators";
|
|
|
6
6
|
import getOutput from "../getOutput";
|
|
7
7
|
import typesMap from "../databases/typesMap";
|
|
8
8
|
import getTargetDatabaseProvider from "../databases/getTargetDatabaseProvider.js";
|
|
9
|
+
import stringToSnakeCase from "../databases/stringToSnakeCase.js";
|
|
10
|
+
import createMetadataTableColumns from "./createMetadataTableColumns.js";
|
|
11
|
+
import roles from "./roles/index.js";
|
|
9
12
|
const addSessionToUser = (userId = null, session = null) => {
|
|
10
13
|
try {
|
|
11
14
|
return runUserQuery("addSession", { userId, session });
|
|
@@ -27,6 +30,16 @@ const insertUserInDatabase = async (user = {}) => {
|
|
|
27
30
|
throw new Error(formatErrorString("signup.insertUserInDatabase", exception));
|
|
28
31
|
}
|
|
29
32
|
};
|
|
33
|
+
const sqlizeMetadata = (metadata = {}) => {
|
|
34
|
+
try {
|
|
35
|
+
return Object.entries(metadata).reduce((sqlized = {}, [key, value]) => {
|
|
36
|
+
sqlized[stringToSnakeCase(key)] = value;
|
|
37
|
+
return sqlized;
|
|
38
|
+
}, {});
|
|
39
|
+
} catch (exception) {
|
|
40
|
+
throw new Error(`[actionName.sqlizeMetadata] ${exception.message}`);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
30
43
|
const getUserToCreate = async (options = {}) => {
|
|
31
44
|
try {
|
|
32
45
|
const usersDatabase = getTargetDatabaseProvider("users");
|
|
@@ -40,12 +53,19 @@ const getUserToCreate = async (options = {}) => {
|
|
|
40
53
|
if (options?.username) {
|
|
41
54
|
user.username = options?.username;
|
|
42
55
|
}
|
|
43
|
-
if (options?.metadata && isObject(options.metadata) && usersDatabaseType === "sql"
|
|
44
|
-
|
|
56
|
+
if (options?.metadata && isObject(options.metadata) && usersDatabaseType === "sql") {
|
|
57
|
+
const sqlizedMetadata = sqlizeMetadata(options.metadata);
|
|
58
|
+
await createMetadataTableColumns(usersDatabase, sqlizedMetadata);
|
|
59
|
+
const { roles: [], ...restOfMetadata } = options?.metadata;
|
|
60
|
+
user = {
|
|
61
|
+
...sqlizeMetadata(restOfMetadata),
|
|
62
|
+
...user
|
|
63
|
+
};
|
|
45
64
|
}
|
|
46
65
|
if (options?.metadata && isObject(options.metadata) && usersDatabaseType === "nosql") {
|
|
66
|
+
const { roles: [], ...restOfMetadata } = options?.metadata;
|
|
47
67
|
user = {
|
|
48
|
-
...
|
|
68
|
+
...restOfMetadata || {},
|
|
49
69
|
...user
|
|
50
70
|
};
|
|
51
71
|
}
|
|
@@ -78,7 +98,13 @@ const signup = async (options, { resolve, reject }) => {
|
|
|
78
98
|
const user = await getUserByUserId(userId);
|
|
79
99
|
const session = generateSession();
|
|
80
100
|
if (user?._id || user?.user_id) {
|
|
81
|
-
await addSessionToUser(user
|
|
101
|
+
await addSessionToUser(user?._id || user?.user_id, session);
|
|
102
|
+
}
|
|
103
|
+
if (options?.metadata?.roles?.length > 0 && process.env.NODE_ENV === "test") {
|
|
104
|
+
for (let i = 0; i < options?.metadata?.roles?.length; i += 1) {
|
|
105
|
+
const role = options?.metadata?.roles[i];
|
|
106
|
+
roles.grant(user?._id || user?.user_id, role);
|
|
107
|
+
}
|
|
82
108
|
}
|
|
83
109
|
return resolve({
|
|
84
110
|
...session,
|
|
@@ -17,7 +17,7 @@ var mongodb_default = async (settings = {}, databasePort = 2610) => {
|
|
|
17
17
|
const connectionOptions = {
|
|
18
18
|
useNewUrlParser: true,
|
|
19
19
|
useUnifiedTopology: true,
|
|
20
|
-
ssl: !process.env.NODE_ENV
|
|
20
|
+
ssl: !["development", "test"].includes(process.env.NODE_ENV),
|
|
21
21
|
...settings?.options || {}
|
|
22
22
|
};
|
|
23
23
|
if (settings?.options?.ca) {
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import generateId from "../../../../lib/generateId";
|
|
2
2
|
import dayjs from "dayjs";
|
|
3
|
+
import utc from "dayjs/plugin/utc.js";
|
|
4
|
+
dayjs.extend(utc);
|
|
3
5
|
var accounts_default = {
|
|
4
6
|
existingUser: async (input = {}) => {
|
|
5
7
|
let existingUserWithEmailAddress;
|
|
@@ -35,6 +37,11 @@ var accounts_default = {
|
|
|
35
37
|
}
|
|
36
38
|
return null;
|
|
37
39
|
},
|
|
40
|
+
deleteUser: async (input = {}) => {
|
|
41
|
+
return process.databases._users?.collection("users").deleteOne({
|
|
42
|
+
_id: input?.userId
|
|
43
|
+
});
|
|
44
|
+
},
|
|
38
45
|
deleteOldSessions: async (input = {}) => {
|
|
39
46
|
const user = await process.databases._users?.collection("users").findOne({
|
|
40
47
|
_id: input?.userId
|
|
@@ -43,6 +43,9 @@ var accounts_default = {
|
|
|
43
43
|
}
|
|
44
44
|
return null;
|
|
45
45
|
},
|
|
46
|
+
deleteUser: async (input = {}) => {
|
|
47
|
+
await process.databases._users?.query(`DELETE FROM users WHERE user_id = $1`, [input?.userId]);
|
|
48
|
+
},
|
|
46
49
|
deleteOldSessions: async (input = {}) => {
|
|
47
50
|
await process.databases._users?.query(`DELETE FROM users_sessions WHERE user_id = $1 AND token_expires_at::date < NOW()`, [input?.userId]);
|
|
48
51
|
},
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import getBrowserSafeUser from "./accounts/getBrowserSafeUser";
|
|
2
|
+
import escapeKeyValuePair from "../lib/escapeKeyValuePair.js";
|
|
2
3
|
var getBrowserSafeRequest_default = (req = {}) => {
|
|
3
4
|
const browserSafeRequest = {};
|
|
4
|
-
browserSafeRequest.params = req.params;
|
|
5
|
-
browserSafeRequest.query = req.query;
|
|
5
|
+
browserSafeRequest.params = escapeKeyValuePair(req.params);
|
|
6
|
+
browserSafeRequest.query = escapeKeyValuePair(req.query);
|
|
6
7
|
browserSafeRequest.context = {
|
|
7
8
|
user: getBrowserSafeUser(req.context.user)
|
|
8
9
|
};
|
package/dist/app/index.js
CHANGED
|
@@ -32,8 +32,14 @@ import Queue from "./queues/index.js";
|
|
|
32
32
|
import readDirectory from "../lib/readDirectory.js";
|
|
33
33
|
import getBuildPath from "../lib/getBuildPath.js";
|
|
34
34
|
import generateMachineId from "../lib/generateMachineId.js";
|
|
35
|
+
import importFile from "../lib/importFile.js";
|
|
35
36
|
import emitWebsocketEvent from "../websockets/emitWebsocketEvent.js";
|
|
36
37
|
import getTargetDatabaseConnection from "./databases/getTargetDatabaseConnection.js";
|
|
38
|
+
import getAPIForDataFunctions from "../ssr/getAPIForDataFunctions.js";
|
|
39
|
+
import getBrowserSafeRequest from "./getBrowserSafeRequest.js";
|
|
40
|
+
import getDataFromComponent from "../ssr/getDataFromComponent.js";
|
|
41
|
+
import getTranslations from "./middleware/getTranslations.js";
|
|
42
|
+
import runUserQuery from "./accounts/runUserQuery.js";
|
|
37
43
|
process.setMaxListeners(0);
|
|
38
44
|
class App {
|
|
39
45
|
constructor(options = {}) {
|
|
@@ -53,6 +59,7 @@ class App {
|
|
|
53
59
|
this.initWebsockets(options?.websockets || {});
|
|
54
60
|
this.initDevelopmentRoutes();
|
|
55
61
|
this.initAccounts();
|
|
62
|
+
this.initTests();
|
|
56
63
|
this.initDeploy();
|
|
57
64
|
this.initAPI(options?.api);
|
|
58
65
|
this.initRoutes(options?.routes);
|
|
@@ -143,7 +150,9 @@ class App {
|
|
|
143
150
|
process.BUILD_ERROR = JSON.parse(message);
|
|
144
151
|
}
|
|
145
152
|
});
|
|
146
|
-
|
|
153
|
+
if (process.env.NODE_ENV !== "test") {
|
|
154
|
+
console.log(`App running at: http://localhost:${express.port}`);
|
|
155
|
+
}
|
|
147
156
|
}
|
|
148
157
|
setMachineId() {
|
|
149
158
|
generateMachineId();
|
|
@@ -156,6 +165,33 @@ class App {
|
|
|
156
165
|
fs.writeFileSync("./.joystick/PROCESS_ID", `${generateId(32)}`);
|
|
157
166
|
}
|
|
158
167
|
}
|
|
168
|
+
initTests() {
|
|
169
|
+
if (process.env.NODE_ENV === "test") {
|
|
170
|
+
this.express.app.get("/api/_test/bootstrap", async (req, res) => {
|
|
171
|
+
const buildPath = `${process.cwd()}/.joystick/build`;
|
|
172
|
+
const Component = req?.query?.pathToComponent ? await importFile(`${buildPath}/${req?.query?.pathToComponent}`) : null;
|
|
173
|
+
if (Component) {
|
|
174
|
+
const componentInstance = Component();
|
|
175
|
+
const apiForDataFunctions = await getAPIForDataFunctions(req, this?.options?.api);
|
|
176
|
+
const browserSafeRequest = getBrowserSafeRequest(req);
|
|
177
|
+
const data = await getDataFromComponent(componentInstance, apiForDataFunctions, browserSafeRequest);
|
|
178
|
+
const translations = await getTranslations({ build: buildPath, page: req?.query?.pathToComponent }, req);
|
|
179
|
+
return res.status(200).send({
|
|
180
|
+
data: {
|
|
181
|
+
[data?.componentId]: data?.data
|
|
182
|
+
},
|
|
183
|
+
translations,
|
|
184
|
+
req: browserSafeRequest
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
res.status(200).send({ data: {}, translations: {} });
|
|
188
|
+
});
|
|
189
|
+
this.express.app.delete("/api/_test/accounts", async (req, res) => {
|
|
190
|
+
await runUserQuery("deleteUser", { userId: req?.body?.userId });
|
|
191
|
+
res.status(200).send({ data: {} });
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
}
|
|
159
195
|
initDeploy() {
|
|
160
196
|
if (process.env.NODE_ENV === "production" && process.env.IS_PUSH_DEPLOYED) {
|
|
161
197
|
this.express.app.get("/api/_push/pre-version", async (req, res) => {
|
|
@@ -401,7 +437,7 @@ class App {
|
|
|
401
437
|
});
|
|
402
438
|
}
|
|
403
439
|
initDevelopmentRoutes() {
|
|
404
|
-
if (process.env.NODE_ENV
|
|
440
|
+
if (["development", "test"].includes(process.env.NODE_ENV)) {
|
|
405
441
|
this.express.app.get("/api/_joystick/sessions", async (req, res) => {
|
|
406
442
|
const sessions = Array.from(this.sessions.entries())?.reduce((acc = {}, [key, value]) => {
|
|
407
443
|
acc[key] = value;
|
|
@@ -431,11 +467,20 @@ class App {
|
|
|
431
467
|
metadata: req?.body?.metadata,
|
|
432
468
|
output: req?.body?.output || defaultUserOutputFields
|
|
433
469
|
});
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
470
|
+
if (!process.env.NODE_ENV !== "test") {
|
|
471
|
+
accounts._setAuthenticationCookie(res, {
|
|
472
|
+
token: signup?.token,
|
|
473
|
+
tokenExpiresAt: signup?.tokenExpiresAt
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
const response = {
|
|
477
|
+
...signup?.user || {}
|
|
478
|
+
};
|
|
479
|
+
if (process.env.NODE_ENV === "test") {
|
|
480
|
+
response.joystickToken = signup?.token;
|
|
481
|
+
response.joystickLoginTokenExpiresAt = signup?.tokenExpiresAt;
|
|
482
|
+
}
|
|
483
|
+
res.status(200).send(JSON.stringify(response));
|
|
439
484
|
} catch (exception) {
|
|
440
485
|
console.log(exception);
|
|
441
486
|
return res.status(500).send(JSON.stringify({
|
|
@@ -451,11 +496,20 @@ class App {
|
|
|
451
496
|
password: req?.body?.password,
|
|
452
497
|
output: req?.body?.output || defaultUserOutputFields
|
|
453
498
|
});
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
499
|
+
if (!process.env.NODE_ENV !== "test") {
|
|
500
|
+
accounts._setAuthenticationCookie(res, {
|
|
501
|
+
token: login?.token,
|
|
502
|
+
tokenExpiresAt: login?.tokenExpiresAt
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
const response = {
|
|
506
|
+
...login?.user || {}
|
|
507
|
+
};
|
|
508
|
+
if (process.env.NODE_ENV === "test") {
|
|
509
|
+
response.joystickToken = login?.token;
|
|
510
|
+
response.joystickLoginTokenExpiresAt = login?.tokenExpiresAt;
|
|
511
|
+
}
|
|
512
|
+
res.status(200).send(JSON.stringify(response));
|
|
459
513
|
} catch (exception) {
|
|
460
514
|
console.log(exception);
|
|
461
515
|
return res.status(500).send(JSON.stringify({
|
|
@@ -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
|
+
};
|
|
@@ -3,6 +3,7 @@ import compression from "compression";
|
|
|
3
3
|
import cookieParser from "cookie-parser";
|
|
4
4
|
import favicon from "serve-favicon";
|
|
5
5
|
import fs from "fs";
|
|
6
|
+
import { __package } from "../../index.js";
|
|
6
7
|
import insecure from "./insecure.js";
|
|
7
8
|
import requestMethods from "./requestMethods.js";
|
|
8
9
|
import bodyParser from "./bodyParser.js";
|
|
@@ -14,11 +15,10 @@ import runUserQuery from "../accounts/runUserQuery.js";
|
|
|
14
15
|
import replaceBackslashesWithForwardSlashes from "../../lib/replaceBackslashesWithForwardSlashes.js";
|
|
15
16
|
import replaceFileProtocol from "../../lib/replaceFileProtocol.js";
|
|
16
17
|
import getBuildPath from "../../lib/getBuildPath.js";
|
|
17
|
-
import sanitizeQueryParameters from "./sanitizeQueryParameters.js";
|
|
18
18
|
import session from "./session.js";
|
|
19
19
|
import csp from "./csp.js";
|
|
20
20
|
const cwd = replaceFileProtocol(replaceBackslashesWithForwardSlashes(process.cwd()));
|
|
21
|
-
const faviconPath =
|
|
21
|
+
const faviconPath = "public/favicon.ico";
|
|
22
22
|
var middleware_default = ({
|
|
23
23
|
app,
|
|
24
24
|
port,
|
|
@@ -41,7 +41,6 @@ var middleware_default = ({
|
|
|
41
41
|
}
|
|
42
42
|
next();
|
|
43
43
|
});
|
|
44
|
-
app.use(sanitizeQueryParameters);
|
|
45
44
|
app.use(requestMethods);
|
|
46
45
|
if (cspConfig) {
|
|
47
46
|
app.use((req, res, next) => csp(req, res, next, cspConfig));
|
|
@@ -54,7 +53,7 @@ var middleware_default = ({
|
|
|
54
53
|
});
|
|
55
54
|
app.use("/_joystick/utils/process.js", (_req, res) => {
|
|
56
55
|
res.set("Content-Type", "text/javascript");
|
|
57
|
-
const processPolyfill = fs.readFileSync(`${
|
|
56
|
+
const processPolyfill = fs.readFileSync(`${__package}/app/utils/process.js`, "utf-8");
|
|
58
57
|
res.send(processPolyfill.replace("${NODE_ENV}", process.env.NODE_ENV));
|
|
59
58
|
});
|
|
60
59
|
app.use("/_joystick/index.client.js", express.static(`${buildPath}index.client.js`, {
|
|
@@ -65,7 +64,7 @@ var middleware_default = ({
|
|
|
65
64
|
app.use("/_joystick/ui", express.static(`${buildPath}ui`, { eTag: false, maxAge: "0" }));
|
|
66
65
|
app.use("/_joystick/hmr/client.js", (_req, res) => {
|
|
67
66
|
res.set("Content-Type", "text/javascript");
|
|
68
|
-
const hmrClient = fs.readFileSync(`${
|
|
67
|
+
const hmrClient = fs.readFileSync(`${__package}/app/middleware/hmr/client.js`, "utf-8");
|
|
69
68
|
res.send(hmrClient.replace("${process.env.PORT}", parseInt(process.env.PORT, 10) + 1));
|
|
70
69
|
});
|
|
71
70
|
app.use(favicon(faviconPath));
|
|
@@ -8,6 +8,10 @@ import generateErrorPage from "../../lib/generateErrorPage.js";
|
|
|
8
8
|
import replaceFileProtocol from "../../lib/replaceFileProtocol.js";
|
|
9
9
|
import replaceBackslashesWithForwardSlashes from "../../lib/replaceBackslashesWithForwardSlashes.js";
|
|
10
10
|
import getBuildPath from "../../lib/getBuildPath.js";
|
|
11
|
+
import escapeHTML from "../../lib/escapeHTML.js";
|
|
12
|
+
import escapeKeyValuePair from "../../lib/escapeKeyValuePair.js";
|
|
13
|
+
import importFile from "../../lib/importFile.js";
|
|
14
|
+
import getTranslations from "./getTranslations.js";
|
|
11
15
|
const generateHash = (input = "") => {
|
|
12
16
|
return crypto.createHash("sha256").update(input).digest("hex");
|
|
13
17
|
};
|
|
@@ -65,71 +69,12 @@ const getCachedHTML = ({ cachePath, cacheFileName, currentDiff }) => {
|
|
|
65
69
|
const getUrl = (request = {}) => {
|
|
66
70
|
const [path = null] = request.url?.split("?");
|
|
67
71
|
return {
|
|
68
|
-
params: request.params,
|
|
69
|
-
query: request.query,
|
|
70
|
-
route: request.route.path,
|
|
71
|
-
path
|
|
72
|
+
params: escapeKeyValuePair(request.params),
|
|
73
|
+
query: escapeKeyValuePair(request.query),
|
|
74
|
+
route: escapeHTML(request.route.path),
|
|
75
|
+
path: escapeHTML(path)
|
|
72
76
|
};
|
|
73
77
|
};
|
|
74
|
-
const getFile = async (buildPath = "") => {
|
|
75
|
-
const file = await import(buildPath);
|
|
76
|
-
return file.default;
|
|
77
|
-
};
|
|
78
|
-
const getTranslationsFile = async (languageFilePath = "", paths = "") => {
|
|
79
|
-
const languageFile = await getFile(`${paths.build}/i18n/${languageFilePath}`);
|
|
80
|
-
const isValidLanguageFile = languageFile && isObject(languageFile);
|
|
81
|
-
if (isValidLanguageFile) {
|
|
82
|
-
const translationsForPage = languageFile[paths.page];
|
|
83
|
-
return translationsForPage ? translationsForPage : languageFile;
|
|
84
|
-
}
|
|
85
|
-
return {};
|
|
86
|
-
};
|
|
87
|
-
const getTranslations = async (paths = {}, languagePreferences = []) => {
|
|
88
|
-
const languageFiles = fs.readdirSync(`${paths.build}/i18n`);
|
|
89
|
-
let matchingFile = null;
|
|
90
|
-
for (let i = 0; i < languagePreferences.length; i += 1) {
|
|
91
|
-
const languageRegex = languagePreferences[i];
|
|
92
|
-
const match = languageFiles.find((languageFile) => !!languageFile.match(languageRegex));
|
|
93
|
-
if (match) {
|
|
94
|
-
matchingFile = match;
|
|
95
|
-
break;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
const translationsFile = await getTranslationsFile(matchingFile, paths);
|
|
99
|
-
return translationsFile;
|
|
100
|
-
};
|
|
101
|
-
const getLanguagePreferenceRegexes = (userLanguage = "", browserLanguages = []) => {
|
|
102
|
-
let languagePreferences = [];
|
|
103
|
-
if (userLanguage) {
|
|
104
|
-
languagePreferences.push(userLanguage);
|
|
105
|
-
}
|
|
106
|
-
const filteredBrowserLanguages = browserLanguages?.filter((language) => {
|
|
107
|
-
return !language?.includes("*");
|
|
108
|
-
});
|
|
109
|
-
languagePreferences.push(...filteredBrowserLanguages);
|
|
110
|
-
languagePreferences.push(settings?.config?.i18n?.defaultLanguage);
|
|
111
|
-
return languagePreferences?.flatMap((language) => {
|
|
112
|
-
const variants = [language];
|
|
113
|
-
if (language?.length === 2) {
|
|
114
|
-
variants.push(`${language.substring(0, 2)}-`);
|
|
115
|
-
}
|
|
116
|
-
if (language?.length > 2) {
|
|
117
|
-
variants.push(`${language?.split("-")[0]}`);
|
|
118
|
-
variants.push(`${language?.split("-")[0]}-`);
|
|
119
|
-
}
|
|
120
|
-
return variants;
|
|
121
|
-
})?.map((languageString) => {
|
|
122
|
-
const lastCharacter = languageString[languageString.length - 1];
|
|
123
|
-
if (lastCharacter === "-") {
|
|
124
|
-
return new RegExp(`^${languageString}[A-Z]+.js`, "g");
|
|
125
|
-
}
|
|
126
|
-
return new RegExp(`^${languageString}.js`, "g");
|
|
127
|
-
});
|
|
128
|
-
};
|
|
129
|
-
const parseBrowserLanguages = (languages = "") => {
|
|
130
|
-
const rawLanguages = languages.split(",");
|
|
131
|
-
return rawLanguages?.map((rawLanguage) => rawLanguage.split(";")[0]);
|
|
132
|
-
};
|
|
133
78
|
var render_default = (req, res, next, appInstance = {}) => {
|
|
134
79
|
res.render = async function(path = "", options = {}) {
|
|
135
80
|
const urlFormattedForCache = req?.url?.split("/")?.filter((part) => !!part)?.join("_");
|
|
@@ -167,13 +112,11 @@ var render_default = (req, res, next, appInstance = {}) => {
|
|
|
167
112
|
return res.send(cachedHTML);
|
|
168
113
|
}
|
|
169
114
|
}
|
|
170
|
-
const pageFile = await
|
|
115
|
+
const pageFile = await importFile(pagePath);
|
|
171
116
|
const Page = pageFile;
|
|
172
|
-
const layoutFile = layoutPath ? await
|
|
117
|
+
const layoutFile = layoutPath ? await importFile(layoutPath) : null;
|
|
173
118
|
const Layout = layoutFile;
|
|
174
|
-
const
|
|
175
|
-
const languagePreferenceRegexes = getLanguagePreferenceRegexes(req?.context?.user?.language, browserLanguages);
|
|
176
|
-
const translations = await getTranslations({ build: buildPath, page: path }, languagePreferenceRegexes);
|
|
119
|
+
const translations = await getTranslations({ build: buildPath, page: path }, req);
|
|
177
120
|
const url = getUrl(req);
|
|
178
121
|
const props = { ...options?.props || {} };
|
|
179
122
|
if (layoutPath && fs.existsSync(layoutPath)) {
|
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
import util from "util";
|
|
2
|
-
import
|
|
3
|
-
const escapeHTML = (string = "") => {
|
|
4
|
-
return String(string).replace(/[&<>"'`=\/]/g, function(match) {
|
|
5
|
-
return HTML_ENTITY_MAP[match];
|
|
6
|
-
});
|
|
7
|
-
};
|
|
2
|
+
import escapeHTML from "../lib/escapeHTML.js";
|
|
8
3
|
const sanitizeAPIResponse = (data = null) => {
|
|
9
4
|
let sanitizedData = data;
|
|
10
5
|
if (!util.isString(sanitizedData) && !util.isObject(sanitizedData) && !Array.isArray(sanitizedData)) {
|
|
@@ -3,6 +3,9 @@ var validateSession_default = (req = null, res = null, sessions = null) => {
|
|
|
3
3
|
const sessionToken = req?.cookies?.joystickSession;
|
|
4
4
|
const csrfToken = req?.headers["x-joystick-csrf"];
|
|
5
5
|
const session = sessions?.get(sessionToken);
|
|
6
|
+
if (csrfToken === "joystick_test") {
|
|
7
|
+
return true;
|
|
8
|
+
}
|
|
6
9
|
if (!session || session && session.csrf !== csrfToken) {
|
|
7
10
|
res.status(403).send(JSON.stringify({
|
|
8
11
|
errors: [formatAPIError(new Error("Unauthorized request."))]
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import ui from "@joystick.js/ui";
|
|
2
2
|
const ResetPassword = ui.component({
|
|
3
|
-
id: process.env.NODE_ENV === "test" ? "testComponent1234" : null,
|
|
4
3
|
render: ({ props }) => {
|
|
5
4
|
return `
|
|
6
5
|
<p>A password reset was requested for this email address (${props.emailAddress}). If you requested this reset, click the link below to reset your password:</p>
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import sanitizeHTML from "sanitize-html";
|
|
2
1
|
import fs from "fs";
|
|
2
|
+
import { fileURLToPath } from "url";
|
|
3
|
+
import { dirname } from "path";
|
|
4
|
+
import sanitizeHTML from "sanitize-html";
|
|
3
5
|
import _accounts from "./app/accounts";
|
|
4
6
|
import _action from "./action/index.js";
|
|
5
7
|
import _websockets from "./websockets";
|
|
@@ -27,16 +29,18 @@ const sanitize = {
|
|
|
27
29
|
allowedTags: sanitizeHTML.defaults.allowedTags
|
|
28
30
|
}
|
|
29
31
|
};
|
|
32
|
+
const currentFilePath = fileURLToPath(import.meta.url);
|
|
33
|
+
const __package = dirname(currentFilePath);
|
|
30
34
|
const __filename = nodeUrlPolyfills.__filename;
|
|
31
35
|
const __dirname = nodeUrlPolyfills.__dirname;
|
|
32
36
|
const id = generateId;
|
|
33
37
|
const origin = getOrigin();
|
|
34
38
|
const settings = loadSettings();
|
|
35
|
-
console.log("HERE", fs.readFileSync(__dirname("dist/app/utils/process.js")?.replace("file://", ""), "utf-8"));
|
|
36
39
|
global.joystick = {
|
|
37
40
|
id: generateId,
|
|
38
41
|
emitters: {},
|
|
39
42
|
settings,
|
|
43
|
+
__package,
|
|
40
44
|
__dirname,
|
|
41
45
|
__filename
|
|
42
46
|
};
|
|
@@ -58,6 +62,7 @@ var src_default = {
|
|
|
58
62
|
export {
|
|
59
63
|
__dirname,
|
|
60
64
|
__filename,
|
|
65
|
+
__package,
|
|
61
66
|
accounts,
|
|
62
67
|
action,
|
|
63
68
|
src_default as default,
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import escapeHTML from "./escapeHTML.js";
|
|
2
|
+
var escapeKeyValuePair_default = (target = {}) => {
|
|
3
|
+
const parameters = Object.entries(target || {});
|
|
4
|
+
for (let i = 0; i < parameters?.length; i += 1) {
|
|
5
|
+
const [key, value] = parameters[i];
|
|
6
|
+
delete target[key];
|
|
7
|
+
target[escapeHTML(key)] = escapeHTML(value);
|
|
8
|
+
}
|
|
9
|
+
return target;
|
|
10
|
+
};
|
|
11
|
+
export {
|
|
12
|
+
escapeKeyValuePair_default as default
|
|
13
|
+
};
|