@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
|
@@ -25,9 +25,8 @@ const flattenAndReplaceWhenElements = (dom = {}, options = {}) => {
|
|
|
25
25
|
};
|
|
26
26
|
var replaceWhenTags_default = (html = "") => {
|
|
27
27
|
try {
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
return dom.toString();
|
|
28
|
+
const whenRegex = new RegExp("<when>|</when>", "g");
|
|
29
|
+
return html?.replace(whenRegex, "");
|
|
31
30
|
} catch (exception) {
|
|
32
31
|
throw new Error(`[ssr.replaceWhenTags] ${exception.message}`);
|
|
33
32
|
}
|
|
@@ -28,7 +28,7 @@ var setHeadTagsInHTML_default = (htmlString = "", head = null, req = {}) => {
|
|
|
28
28
|
const metaTag = head.tags.meta[currentMetaTag];
|
|
29
29
|
const existingTag = headTag.querySelector(`meta[name="${metaTag.name}"]`);
|
|
30
30
|
const newTag = htmlParser.parse(`<meta />`);
|
|
31
|
-
const metaTagEntries = Object.entries(metaTag);
|
|
31
|
+
const metaTagEntries = Object.entries(metaTag || {});
|
|
32
32
|
let currentMetaTagEntry = metaTagEntries.length;
|
|
33
33
|
while (currentMetaTagEntry--) {
|
|
34
34
|
const [attributeName, attributeValue] = metaTagEntries[currentMetaTagEntry];
|
|
@@ -47,7 +47,7 @@ var setHeadTagsInHTML_default = (htmlString = "", head = null, req = {}) => {
|
|
|
47
47
|
while (currentLinkTag--) {
|
|
48
48
|
const linkTag = head.tags.link[currentLinkTag];
|
|
49
49
|
const newTag = htmlParser.parse(`<link />`);
|
|
50
|
-
let linkTagEntries = Object.entries(linkTag);
|
|
50
|
+
let linkTagEntries = Object.entries(linkTag || {});
|
|
51
51
|
let currentLinkTagEntry = linkTagEntries.length;
|
|
52
52
|
while (currentLinkTagEntry--) {
|
|
53
53
|
const [attributeName, attributeValue] = linkTagEntries[currentLinkTagEntry];
|
|
@@ -61,7 +61,7 @@ var setHeadTagsInHTML_default = (htmlString = "", head = null, req = {}) => {
|
|
|
61
61
|
while (currentScriptTag--) {
|
|
62
62
|
const scriptTag = head.tags.script[currentScriptTag];
|
|
63
63
|
const newTag = htmlParser.parse(`<script><\/script>`);
|
|
64
|
-
let scriptTagEntries = Object.entries(scriptTag);
|
|
64
|
+
let scriptTagEntries = Object.entries(scriptTag || {});
|
|
65
65
|
let currentScriptTagEntry = scriptTagEntries.length;
|
|
66
66
|
while (currentScriptTagEntry--) {
|
|
67
67
|
const [attributeName, attributeValue] = scriptTagEntries[currentScriptTagEntry];
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
var trackFunctionCall_default = (path = "", args = []) => {
|
|
2
|
+
if (process.env.NODE_ENV === "test") {
|
|
3
|
+
process.test = {
|
|
4
|
+
...process.test || {},
|
|
5
|
+
functionCalls: {
|
|
6
|
+
...process?.test?.functionCalls || {},
|
|
7
|
+
[path]: [
|
|
8
|
+
...process?.test?.functionCalls && process?.test?.functionCalls[path] || [],
|
|
9
|
+
{ calledAt: new Date().toISOString(), args }
|
|
10
|
+
]
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
export {
|
|
16
|
+
trackFunctionCall_default as default
|
|
17
|
+
};
|
|
@@ -47,7 +47,7 @@ const addToValidationQueue = (queue = [], schema = {}, input = {}, parentPath =
|
|
|
47
47
|
}
|
|
48
48
|
const rulesOnlySchema = !!(schema.type && constants.types.includes(schema.type));
|
|
49
49
|
if (!rulesOnlySchema) {
|
|
50
|
-
Object.entries(schema).forEach(([field, rules]) => {
|
|
50
|
+
Object.entries(schema || {}).forEach(([field, rules]) => {
|
|
51
51
|
const path = `${parentPath ? `${parentPath}.${field}` : field}`;
|
|
52
52
|
const validationTask = addValidationTask({
|
|
53
53
|
queue,
|
|
@@ -77,12 +77,12 @@ const validateInputWithSchema = async (input = null, schema = null, parentPath =
|
|
|
77
77
|
if (!input) {
|
|
78
78
|
errors.push("Input is required.");
|
|
79
79
|
}
|
|
80
|
-
if (schema && Object.keys(schema) && !schema.type) {
|
|
80
|
+
if (schema && Object.keys(schema || {}) && !schema.type) {
|
|
81
81
|
validateSchema(schema);
|
|
82
82
|
}
|
|
83
83
|
const queue = addToValidationQueue([], schema, input, parentPath);
|
|
84
84
|
await Promise.all(queue.flatMap((validationTask) => {
|
|
85
|
-
return Object.entries(validationTask.rules).flatMap(async ([ruleName, ruleValue]) => {
|
|
85
|
+
return Object.entries(validationTask.rules || {}).flatMap(async ([ruleName, ruleValue]) => {
|
|
86
86
|
const validator = constants.rules[ruleName];
|
|
87
87
|
if (validator && !validationTask.path.includes(".$.")) {
|
|
88
88
|
const result = await validator(ruleValue, validationTask.inputValue, validationTask.path);
|
|
@@ -2,16 +2,16 @@ import throwError from "../lib/throwError";
|
|
|
2
2
|
import { isObject } from "../lib/typeValidators";
|
|
3
3
|
import constants from "../lib/constants";
|
|
4
4
|
const validateRuleType = (schema) => {
|
|
5
|
-
Object.entries(schema).forEach(([field, rules]) => {
|
|
5
|
+
Object.entries(schema || {}).forEach(([field, rules]) => {
|
|
6
6
|
if (rules && rules.type && !constants.types.includes(rules.type)) {
|
|
7
7
|
throw new Error(`Invalid value for schema field "${field}" type rule. ${rules.type} is not supported. Use one of the following: ${constants.types.slice(0, constants.types.length - 1).join(", ")}, or ${constants.types[constants.types.length - 1]}.`);
|
|
8
8
|
}
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
const validateRuleNames = (schema) => {
|
|
12
|
-
Object.entries(schema).forEach(([field, rules]) => {
|
|
13
|
-
Object.keys(rules).forEach((ruleName) => {
|
|
14
|
-
const ruleNames = Object.keys(constants.rules);
|
|
12
|
+
Object.entries(schema || {}).forEach(([field, rules]) => {
|
|
13
|
+
Object.keys(rules || {}).forEach((ruleName) => {
|
|
14
|
+
const ruleNames = Object.keys(constants.rules || {});
|
|
15
15
|
if (!ruleNames.includes(ruleName)) {
|
|
16
16
|
throw new Error(`Invalid rule name ${ruleName} in rule for ${field} field.`);
|
|
17
17
|
}
|
|
@@ -19,7 +19,7 @@ const validateRuleNames = (schema) => {
|
|
|
19
19
|
});
|
|
20
20
|
};
|
|
21
21
|
const validateRulesAreObjects = (schema) => {
|
|
22
|
-
Object.entries(schema).forEach(([field, rules]) => {
|
|
22
|
+
Object.entries(schema || {}).forEach(([field, rules]) => {
|
|
23
23
|
if (!isObject(rules)) {
|
|
24
24
|
throw new Error(`Must pass an object containing rules to validate by for ${field} field.`);
|
|
25
25
|
}
|
package/dist/websockets/index.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import emitWebsocketEvent from "./emitWebsocketEvent";
|
|
2
|
+
import trackFunctionCall from "../test/trackFunctionCall.js";
|
|
2
3
|
var websockets_default = (serverName = "") => {
|
|
3
4
|
return {
|
|
4
5
|
send: (payload = {}, uniqueConnectionId = "") => {
|
|
5
6
|
const emitterName = uniqueConnectionId ? `${serverName}_${uniqueConnectionId}` : serverName;
|
|
7
|
+
trackFunctionCall(`node.websockets.${serverName}.send`, [
|
|
8
|
+
payload
|
|
9
|
+
]);
|
|
6
10
|
emitWebsocketEvent(emitterName, "message", payload);
|
|
7
11
|
}
|
|
8
12
|
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export default (context = {}) => {
|
|
2
|
+
const sanitizedContext = { ...context };
|
|
3
|
+
|
|
4
|
+
if (sanitizedContext?.req) {
|
|
5
|
+
delete sanitizedContext.req;
|
|
6
|
+
sanitizedContext.req = {
|
|
7
|
+
method: context?.req?.method,
|
|
8
|
+
headers: context?.req?.headers,
|
|
9
|
+
url: context?.req?.url,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (sanitizedContext?.res) {
|
|
14
|
+
delete sanitizedContext.res;
|
|
15
|
+
sanitizedContext.res = {
|
|
16
|
+
method: context?.res?.method,
|
|
17
|
+
headers: context?.res?.headers,
|
|
18
|
+
url: context?.res?.url,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (sanitizedContext.mongodb) {
|
|
23
|
+
delete sanitizedContext.mongodb;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (sanitizedContext.postgresql) {
|
|
27
|
+
delete sanitizedContext.postgresql;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (sanitizedContext.redis) {
|
|
31
|
+
delete sanitizedContext.redis;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (sanitizedContext._users) {
|
|
35
|
+
delete sanitizedContext._users;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (sanitizedContext._queues) {
|
|
39
|
+
delete sanitizedContext._queues;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return sanitizedContext;
|
|
43
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@joystick.js/node-canary",
|
|
3
|
-
"version": "0.0.0-canary.
|
|
3
|
+
"version": "0.0.0-canary.310",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A Node.js framework for building web apps.",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"node-html-parser": "^5.1.0",
|
|
39
39
|
"nodemailer": "^6.7.0",
|
|
40
40
|
"pg": "^8.7.3",
|
|
41
|
+
"pg-escape": "^0.2.0",
|
|
41
42
|
"process": "^0.11.10",
|
|
42
43
|
"query-string": "^7.0.1",
|
|
43
44
|
"sanitize-html": "^2.7.3",
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
import { jest } from "@jest/globals";
|
|
2
|
-
import { killPortProcess } from "kill-port-process";
|
|
3
|
-
import setAppSettingsForTest from "../../../tests/lib/setAppSettingsForTest";
|
|
4
|
-
import startTestDatabase from "../../../tests/lib/databases/start";
|
|
5
|
-
import stopTestDatabase from "../../../tests/lib/databases/stop";
|
|
6
|
-
import { beforeEach, expect, test } from "@jest/globals";
|
|
7
|
-
import roles from "./index";
|
|
8
|
-
import accounts from "../index";
|
|
9
|
-
jest.mock("../../../../node_modules/dayjs", () => {
|
|
10
|
-
const _dayjs = jest.requireActual("../../../../node_modules/dayjs");
|
|
11
|
-
const _utc = jest.requireActual("../../../../node_modules/dayjs/plugin/utc");
|
|
12
|
-
_dayjs.extend(_utc);
|
|
13
|
-
return () => _dayjs("2022-01-01T00:00:00.000Z");
|
|
14
|
-
});
|
|
15
|
-
setAppSettingsForTest({
|
|
16
|
-
"config": {
|
|
17
|
-
"databases": [
|
|
18
|
-
{
|
|
19
|
-
"provider": "mongodb",
|
|
20
|
-
"users": true,
|
|
21
|
-
"options": {}
|
|
22
|
-
}
|
|
23
|
-
],
|
|
24
|
-
"i18n": {
|
|
25
|
-
"defaultLanguage": "en-US"
|
|
26
|
-
},
|
|
27
|
-
"middleware": {},
|
|
28
|
-
"email": {
|
|
29
|
-
"from": "app@test.com",
|
|
30
|
-
"smtp": {
|
|
31
|
-
"host": "fake.email.com",
|
|
32
|
-
"port": 587,
|
|
33
|
-
"username": "test",
|
|
34
|
-
"password": "password"
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
},
|
|
38
|
-
"global": {},
|
|
39
|
-
"public": {},
|
|
40
|
-
"private": {}
|
|
41
|
-
});
|
|
42
|
-
global.joystick = {
|
|
43
|
-
settings: {
|
|
44
|
-
config: {
|
|
45
|
-
databases: [{
|
|
46
|
-
"provider": "mongodb",
|
|
47
|
-
"users": true,
|
|
48
|
-
"options": {}
|
|
49
|
-
}]
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
|
-
const app = (await import("../../index")).default;
|
|
54
|
-
let instance;
|
|
55
|
-
describe("app/accounts/roles/index.js", () => {
|
|
56
|
-
beforeAll(async () => {
|
|
57
|
-
process.env.PORT = 3600;
|
|
58
|
-
});
|
|
59
|
-
beforeEach(async () => {
|
|
60
|
-
instance = await app({});
|
|
61
|
-
});
|
|
62
|
-
afterEach(async () => {
|
|
63
|
-
if (instance?.server?.close && typeof instance.server.close === "function") {
|
|
64
|
-
instance.server.close();
|
|
65
|
-
}
|
|
66
|
-
await killPortProcess(process.env.PORT);
|
|
67
|
-
});
|
|
68
|
-
afterAll(async () => {
|
|
69
|
-
});
|
|
70
|
-
test("roles.add adds role to roles collection in database", async () => {
|
|
71
|
-
await roles.add("admin");
|
|
72
|
-
const roleExists = await process.databases.mongodb.collection("roles").findOne({ role: "admin" });
|
|
73
|
-
expect(!!roleExists).toBe(true);
|
|
74
|
-
});
|
|
75
|
-
test("roles.remove removes role from roles collection in database", async () => {
|
|
76
|
-
await roles.add("admin");
|
|
77
|
-
await roles.remove("admin");
|
|
78
|
-
const roleExists = await process.databases.mongodb.collection("roles").findOne({ role: "admin" });
|
|
79
|
-
expect(!!roleExists).toBe(false);
|
|
80
|
-
});
|
|
81
|
-
test("roles.list returns a list of roles in the roles collection in database", async () => {
|
|
82
|
-
await roles.add("admin");
|
|
83
|
-
await roles.add("manager");
|
|
84
|
-
await roles.add("employee");
|
|
85
|
-
const rolesInDatabase = await roles.list();
|
|
86
|
-
await roles.remove("admin");
|
|
87
|
-
await roles.remove("manager");
|
|
88
|
-
await roles.remove("employee");
|
|
89
|
-
expect(rolesInDatabase).toEqual(["admin", "manager", "employee"]);
|
|
90
|
-
});
|
|
91
|
-
test("roles.grant adds role to user in users collection in database", async () => {
|
|
92
|
-
await process.databases.mongodb.collection("users").deleteOne({ emailAddress: "test@test.com" });
|
|
93
|
-
const user = await accounts.signup({ emailAddress: "test@test.com", password: "password" });
|
|
94
|
-
await roles.grant(user?.userId, "admin");
|
|
95
|
-
await roles.grant(user?.userId, "rolethatdoesntexist");
|
|
96
|
-
const userAfterGrant = await process.databases.mongodb.collection("users").findOne({ _id: user?.userId });
|
|
97
|
-
const rolesCreated = await process.databases.mongodb.collection("roles").find().toArray();
|
|
98
|
-
expect(userAfterGrant?.roles?.includes("admin")).toBe(true);
|
|
99
|
-
expect(rolesCreated?.length).toBe(2);
|
|
100
|
-
});
|
|
101
|
-
test("roles.revoke removes role from user in users collection in database", async () => {
|
|
102
|
-
await process.databases.mongodb.collection("users").deleteOne({ emailAddress: "test@test.com" });
|
|
103
|
-
const user = await accounts.signup({ emailAddress: "test@test.com", password: "password" });
|
|
104
|
-
await roles.grant(user?.userId, "admin");
|
|
105
|
-
await roles.revoke(user?.userId, "admin");
|
|
106
|
-
const userAfterGrant = await process.databases.mongodb.collection("users").findOne({ _id: user?.userId });
|
|
107
|
-
expect(userAfterGrant?.roles?.includes("admin")).toBe(false);
|
|
108
|
-
});
|
|
109
|
-
test("roles.userHasRole returns true if user has role", async () => {
|
|
110
|
-
await process.databases.mongodb.collection("users").deleteOne({ emailAddress: "test@test.com" });
|
|
111
|
-
const user = await accounts.signup({ emailAddress: "test@test.com", password: "password" });
|
|
112
|
-
await roles.grant(user?.userId, "admin");
|
|
113
|
-
const userHasRole = await roles.userHasRole(user?.userId, "admin");
|
|
114
|
-
expect(userHasRole).toBe(true);
|
|
115
|
-
});
|
|
116
|
-
test("roles.userHasRole returns false if user does not have role", async () => {
|
|
117
|
-
await process.databases.mongodb.collection("users").deleteOne({ emailAddress: "test@test.com" });
|
|
118
|
-
const user = await accounts.signup({ emailAddress: "test@test.com", password: "password" });
|
|
119
|
-
await roles.grant(user?.userId, "admin");
|
|
120
|
-
const userHasRole = await roles.userHasRole(user?.userId, "manager");
|
|
121
|
-
expect(userHasRole).toBe(false);
|
|
122
|
-
});
|
|
123
|
-
});
|