@joystick.js/node-canary 0.0.0-canary.6 → 0.0.0-canary.61
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 +1 -1
- package/dist/app/accounts/getBrowserSafeUser.js +2 -1
- package/dist/app/accounts/hasLoginTokenExpired.js +1 -2
- package/dist/app/databases/mongodb/index.js +1 -1
- package/dist/app/getBrowserSafeRequest.js +3 -2
- package/dist/app/index.js +27 -2
- package/dist/app/middleware/index.js +4 -5
- package/dist/app/middleware/render.js +10 -11
- 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 -11
- package/dist/ssr/getAPIForDataFunctions.js +33 -0
- package/dist/ssr/getDataFromComponent.js +14 -0
- package/dist/ssr/index.js +5 -41
- package/package.json +1 -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,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;
|
|
@@ -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,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,12 @@ 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";
|
|
37
41
|
process.setMaxListeners(0);
|
|
38
42
|
class App {
|
|
39
43
|
constructor(options = {}) {
|
|
@@ -53,6 +57,7 @@ class App {
|
|
|
53
57
|
this.initWebsockets(options?.websockets || {});
|
|
54
58
|
this.initDevelopmentRoutes();
|
|
55
59
|
this.initAccounts();
|
|
60
|
+
this.initTests();
|
|
56
61
|
this.initDeploy();
|
|
57
62
|
this.initAPI(options?.api);
|
|
58
63
|
this.initRoutes(options?.routes);
|
|
@@ -143,7 +148,9 @@ class App {
|
|
|
143
148
|
process.BUILD_ERROR = JSON.parse(message);
|
|
144
149
|
}
|
|
145
150
|
});
|
|
146
|
-
|
|
151
|
+
if (process.env.NODE_ENV !== "test") {
|
|
152
|
+
console.log(`App running at: http://localhost:${express.port}`);
|
|
153
|
+
}
|
|
147
154
|
}
|
|
148
155
|
setMachineId() {
|
|
149
156
|
generateMachineId();
|
|
@@ -156,6 +163,24 @@ class App {
|
|
|
156
163
|
fs.writeFileSync("./.joystick/PROCESS_ID", `${generateId(32)}`);
|
|
157
164
|
}
|
|
158
165
|
}
|
|
166
|
+
initTests() {
|
|
167
|
+
if (process.env.NODE_ENV === "test") {
|
|
168
|
+
this.express.app.get("/api/_test/bootstrap", async (req, res) => {
|
|
169
|
+
const Component = req?.query?.pathToComponent ? await importFile(`${process.cwd()}/.joystick/build/${req?.query?.pathToComponent}`) : null;
|
|
170
|
+
if (Component) {
|
|
171
|
+
const componentInstance = Component();
|
|
172
|
+
const apiForDataFunctions = await getAPIForDataFunctions(req, this?.options?.api);
|
|
173
|
+
const browserSafeRequest = getBrowserSafeRequest(req);
|
|
174
|
+
const data = await getDataFromComponent(componentInstance, apiForDataFunctions, browserSafeRequest);
|
|
175
|
+
return res.status(200).send({
|
|
176
|
+
data: data?.data,
|
|
177
|
+
translations: {}
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
res.status(200).send({ data: {}, translations: {} });
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
159
184
|
initDeploy() {
|
|
160
185
|
if (process.env.NODE_ENV === "production" && process.env.IS_PUSH_DEPLOYED) {
|
|
161
186
|
this.express.app.get("/api/_push/pre-version", async (req, res) => {
|
|
@@ -401,7 +426,7 @@ class App {
|
|
|
401
426
|
});
|
|
402
427
|
}
|
|
403
428
|
initDevelopmentRoutes() {
|
|
404
|
-
if (process.env.NODE_ENV
|
|
429
|
+
if (["development", "test"].includes(process.env.NODE_ENV)) {
|
|
405
430
|
this.express.app.get("/api/_joystick/sessions", async (req, res) => {
|
|
406
431
|
const sessions = Array.from(this.sessions.entries())?.reduce((acc = {}, [key, value]) => {
|
|
407
432
|
acc[key] = value;
|
|
@@ -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,9 @@ 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";
|
|
11
14
|
const generateHash = (input = "") => {
|
|
12
15
|
return crypto.createHash("sha256").update(input).digest("hex");
|
|
13
16
|
};
|
|
@@ -65,18 +68,14 @@ const getCachedHTML = ({ cachePath, cacheFileName, currentDiff }) => {
|
|
|
65
68
|
const getUrl = (request = {}) => {
|
|
66
69
|
const [path = null] = request.url?.split("?");
|
|
67
70
|
return {
|
|
68
|
-
params: request.params,
|
|
69
|
-
query: request.query,
|
|
70
|
-
route: request.route.path,
|
|
71
|
-
path
|
|
71
|
+
params: escapeKeyValuePair(request.params),
|
|
72
|
+
query: escapeKeyValuePair(request.query),
|
|
73
|
+
route: escapeHTML(request.route.path),
|
|
74
|
+
path: escapeHTML(path)
|
|
72
75
|
};
|
|
73
76
|
};
|
|
74
|
-
const getFile = async (buildPath = "") => {
|
|
75
|
-
const file = await import(buildPath);
|
|
76
|
-
return file.default;
|
|
77
|
-
};
|
|
78
77
|
const getTranslationsFile = async (languageFilePath = "", paths = "") => {
|
|
79
|
-
const languageFile = await
|
|
78
|
+
const languageFile = await importFile(`${paths.build}/i18n/${languageFilePath}`);
|
|
80
79
|
const isValidLanguageFile = languageFile && isObject(languageFile);
|
|
81
80
|
if (isValidLanguageFile) {
|
|
82
81
|
const translationsForPage = languageFile[paths.page];
|
|
@@ -167,9 +166,9 @@ var render_default = (req, res, next, appInstance = {}) => {
|
|
|
167
166
|
return res.send(cachedHTML);
|
|
168
167
|
}
|
|
169
168
|
}
|
|
170
|
-
const pageFile = await
|
|
169
|
+
const pageFile = await importFile(pagePath);
|
|
171
170
|
const Page = pageFile;
|
|
172
|
-
const layoutFile = layoutPath ? await
|
|
171
|
+
const layoutFile = layoutPath ? await importFile(layoutPath) : null;
|
|
173
172
|
const Layout = layoutFile;
|
|
174
173
|
const browserLanguages = parseBrowserLanguages(req?.headers["accept-language"]);
|
|
175
174
|
const languagePreferenceRegexes = getLanguagePreferenceRegexes(req?.context?.user?.language, browserLanguages);
|
|
@@ -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
|
+
};
|
package/dist/lib/getBuildPath.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from "fs";
|
|
2
2
|
var getBuildPath_default = () => {
|
|
3
|
-
if (process.env.NODE_ENV
|
|
3
|
+
if (["development", "test"].includes(process.env.NODE_ENV) || fs.existsSync(".joystick/build")) {
|
|
4
4
|
return ".joystick/build/";
|
|
5
5
|
}
|
|
6
6
|
return "";
|
|
@@ -6,7 +6,7 @@ var getSSLCertificates_default = (ssl = null) => {
|
|
|
6
6
|
const keyPath = process.env.IS_PUSH_DEPLOYED ? pushKeyPath : ssl?.key || null;
|
|
7
7
|
const certExists = fs.existsSync(certPath);
|
|
8
8
|
const keyExists = fs.existsSync(keyPath);
|
|
9
|
-
if (process.env.NODE_ENV
|
|
9
|
+
if (["development", "test"].includes(process.env.NODE_ENV) || !certExists || !keyExists) {
|
|
10
10
|
return null;
|
|
11
11
|
}
|
|
12
12
|
return {
|
package/dist/lib/log.js
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { fileURLToPath } from "url";
|
|
2
|
+
import { dirname } from "path";
|
|
2
3
|
var nodeUrlPolyfills_default = {
|
|
3
|
-
__filename: (url =
|
|
4
|
-
|
|
5
|
-
return "";
|
|
6
|
-
}
|
|
7
|
-
return new URL("", url).pathname;
|
|
4
|
+
__filename: (url = "") => {
|
|
5
|
+
return fileURLToPath(url);
|
|
8
6
|
},
|
|
9
|
-
__dirname: (url =
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
return new URL(".", url).pathname;
|
|
7
|
+
__dirname: (url = "") => {
|
|
8
|
+
const currentFilePath = fileURLToPath(url);
|
|
9
|
+
return dirname(currentFilePath);
|
|
14
10
|
}
|
|
15
11
|
};
|
|
16
12
|
export {
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import get from "../api/get";
|
|
2
|
+
import set from "../api/set";
|
|
3
|
+
var getAPIForDataFunctions_default = (req = {}, api = {}) => {
|
|
4
|
+
try {
|
|
5
|
+
return {
|
|
6
|
+
get: (getterName = "", getterOptions = {}) => {
|
|
7
|
+
return get({
|
|
8
|
+
getterName,
|
|
9
|
+
getterOptions: api?.getters[getterName] || {},
|
|
10
|
+
input: getterOptions?.input,
|
|
11
|
+
output: getterOptions?.output,
|
|
12
|
+
context: req?.context,
|
|
13
|
+
APIOptions: api?.options
|
|
14
|
+
});
|
|
15
|
+
},
|
|
16
|
+
set: (setterName = "", setterOptions = {}) => {
|
|
17
|
+
return set({
|
|
18
|
+
setterName,
|
|
19
|
+
setterOptions: api?.setters[setterName] || {},
|
|
20
|
+
input: setterOptions?.input,
|
|
21
|
+
output: setterOptions?.output,
|
|
22
|
+
context: req?.context,
|
|
23
|
+
APIOptions: api?.options
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
} catch (exception) {
|
|
28
|
+
throw new Error(`[ssr.getAPIForDataFunctions] ${exception.message}`);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
export {
|
|
32
|
+
getAPIForDataFunctions_default as default
|
|
33
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
var getDataFromComponent_default = async (componentInstance = {}, api = {}, browserSafeRequest = {}) => {
|
|
2
|
+
try {
|
|
3
|
+
const data = await componentInstance.handleFetchData(api, browserSafeRequest, {}, componentInstance);
|
|
4
|
+
return {
|
|
5
|
+
componentId: componentInstance?.id,
|
|
6
|
+
data
|
|
7
|
+
};
|
|
8
|
+
} catch (exception) {
|
|
9
|
+
throw new Error(`[ssr.getDataFromComponent] ${exception.message}`);
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
export {
|
|
13
|
+
getDataFromComponent_default as default
|
|
14
|
+
};
|
package/dist/ssr/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import fs from "fs";
|
|
2
|
+
import { __package } from "../index.js";
|
|
2
3
|
import get from "../api/get";
|
|
3
4
|
import set from "../api/set";
|
|
4
5
|
import getBrowserSafeRequest from "../app/getBrowserSafeRequest";
|
|
@@ -7,6 +8,8 @@ import getCSSFromTree from "./getCSSFromTree";
|
|
|
7
8
|
import replaceWhenTags from "./replaceWhenTags";
|
|
8
9
|
import setHeadTagsInHTML from "./setHeadTagsInHTML";
|
|
9
10
|
import { parseHTML } from "linkedom";
|
|
11
|
+
import getDataFromComponent from "./getDataFromComponent.js";
|
|
12
|
+
import getAPIForDataFunctions from "./getAPIForDataFunctions.js";
|
|
10
13
|
const injectCSSIntoHTML = (html, baseCSS = "", css = "") => {
|
|
11
14
|
try {
|
|
12
15
|
return html.replace("${css}", css).replace("${globalCSS}", `<style>${baseCSS || ""}</style>`).replace("${componentCSS}", css);
|
|
@@ -184,7 +187,7 @@ const getBaseCSS = (baseHTMLName = "") => {
|
|
|
184
187
|
try {
|
|
185
188
|
const customBaseCSSPathForEmail = baseHTMLName ? `${process.cwd()}/email/base_${baseHTMLName}.css` : null;
|
|
186
189
|
const customDefaultBaseCSSPathForEmail = `${process.cwd()}/email/base.css`;
|
|
187
|
-
const defaultBaseCSSPathForEmail = process.env.NODE_ENV === "test" ? `${process.cwd()}/src/email/templates/base.css` : `${
|
|
190
|
+
const defaultBaseCSSPathForEmail = process.env.NODE_ENV === "test" ? `${process.cwd()}/src/email/templates/base.css` : `${__package}/email/templates/base.css`;
|
|
188
191
|
let baseCSSPathToFetch = defaultBaseCSSPathForEmail;
|
|
189
192
|
if (fs.existsSync(customDefaultBaseCSSPathForEmail)) {
|
|
190
193
|
baseCSSPathToFetch = customDefaultBaseCSSPathForEmail;
|
|
@@ -242,7 +245,7 @@ const getBaseHTML = (isEmailRender = false, baseEmailHTMLName = "") => {
|
|
|
242
245
|
if (isEmailRender) {
|
|
243
246
|
const customBaseHTMLPathForEmail = baseEmailHTMLName ? `${process.cwd()}/email/base_${baseEmailHTMLName}.html` : null;
|
|
244
247
|
const customDefaultBaseHTMLPathForEmail = `${process.cwd()}/email/base.html`;
|
|
245
|
-
const defaultBaseHTMLPathForEmail = process.env.NODE_ENV === "test" ? `${process.cwd()}/src/email/templates/base.html` : `${
|
|
248
|
+
const defaultBaseHTMLPathForEmail = process.env.NODE_ENV === "test" ? `${process.cwd()}/src/email/templates/base.html` : `${__package}/email/templates/base.html`;
|
|
246
249
|
baseHTMLPathToFetch = defaultBaseHTMLPathForEmail;
|
|
247
250
|
if (fs.existsSync(customDefaultBaseHTMLPathForEmail)) {
|
|
248
251
|
baseHTMLPathToFetch = customDefaultBaseHTMLPathForEmail;
|
|
@@ -289,17 +292,6 @@ const buildTreeForComponent = (componentInstance = {}, ssrTree = {}, translation
|
|
|
289
292
|
throw new Error(`[ssr.buildTreeForComponent] ${exception.message}`);
|
|
290
293
|
}
|
|
291
294
|
};
|
|
292
|
-
const getDataFromComponent = async (componentInstance = {}, api = {}, browserSafeRequest = {}) => {
|
|
293
|
-
try {
|
|
294
|
-
const data = await componentInstance.handleFetchData(api, browserSafeRequest, {}, componentInstance);
|
|
295
|
-
return {
|
|
296
|
-
componentId: componentInstance?.id,
|
|
297
|
-
data
|
|
298
|
-
};
|
|
299
|
-
} catch (exception) {
|
|
300
|
-
throw new Error(`[ssr.getDataFromComponent] ${exception.message}`);
|
|
301
|
-
}
|
|
302
|
-
};
|
|
303
295
|
const getTreeForSSR = (componentInstance = {}) => {
|
|
304
296
|
try {
|
|
305
297
|
return {
|
|
@@ -320,34 +312,6 @@ const getComponentInstance = (Component, options = {}) => {
|
|
|
320
312
|
throw new Error(`[ssr.getComponentInstance] ${exception.message}`);
|
|
321
313
|
}
|
|
322
314
|
};
|
|
323
|
-
const getAPIForDataFunctions = (req = {}, api = {}) => {
|
|
324
|
-
try {
|
|
325
|
-
return {
|
|
326
|
-
get: (getterName = "", getterOptions = {}) => {
|
|
327
|
-
return get({
|
|
328
|
-
getterName,
|
|
329
|
-
getterOptions: api?.getters[getterName] || {},
|
|
330
|
-
input: getterOptions?.input,
|
|
331
|
-
output: getterOptions?.output,
|
|
332
|
-
context: req?.context,
|
|
333
|
-
APIOptions: api?.options
|
|
334
|
-
});
|
|
335
|
-
},
|
|
336
|
-
set: (setterName = "", setterOptions = {}) => {
|
|
337
|
-
return set({
|
|
338
|
-
setterName,
|
|
339
|
-
setterOptions: api?.setters[setterName] || {},
|
|
340
|
-
input: setterOptions?.input,
|
|
341
|
-
output: setterOptions?.output,
|
|
342
|
-
context: req?.context,
|
|
343
|
-
APIOptions: api?.options
|
|
344
|
-
});
|
|
345
|
-
}
|
|
346
|
-
};
|
|
347
|
-
} catch (exception) {
|
|
348
|
-
throw new Error(`[ssr.getAPIForDataFunctions] ${exception.message}`);
|
|
349
|
-
}
|
|
350
|
-
};
|
|
351
315
|
const validateOptions = (options) => {
|
|
352
316
|
try {
|
|
353
317
|
if (!options)
|
package/package.json
CHANGED
|
@@ -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
|
-
});
|
package/dist/app/index.test.js
DELETED
|
@@ -1,575 +0,0 @@
|
|
|
1
|
-
import { afterAll, afterEach, beforeAll, expect, jest, test } from "@jest/globals";
|
|
2
|
-
import { mockRequest, mockResponse } from "jest-mock-req-res";
|
|
3
|
-
import { killPortProcess } from "kill-port-process";
|
|
4
|
-
import assertRoutesDoNotExistInRegexes from "../tests/lib/assertRoutesDoNotExistInRegexes";
|
|
5
|
-
import assertRoutesExistInRegexes from "../tests/lib/assertRoutesExistInRegexes";
|
|
6
|
-
import setAppSettingsForTest from "../tests/lib/setAppSettingsForTest";
|
|
7
|
-
import startTestDatabase from "../tests/lib/databases/start";
|
|
8
|
-
import stopTestDatabase from "../tests/lib/databases/stop";
|
|
9
|
-
import getRouteRegexes from "../tests/lib/getRouteRegexes";
|
|
10
|
-
jest.mock("../../node_modules/crypto-extra", () => {
|
|
11
|
-
return {
|
|
12
|
-
randomString: () => "abc1234"
|
|
13
|
-
};
|
|
14
|
-
});
|
|
15
|
-
jest.mock("../../node_modules/bcrypt", () => {
|
|
16
|
-
return {
|
|
17
|
-
hashSync: () => "hashed$password",
|
|
18
|
-
compareSync: () => {
|
|
19
|
-
return true;
|
|
20
|
-
}
|
|
21
|
-
};
|
|
22
|
-
});
|
|
23
|
-
jest.mock("../../node_modules/dayjs", () => {
|
|
24
|
-
const _dayjs = jest.requireActual("../../node_modules/dayjs");
|
|
25
|
-
const _utc = jest.requireActual("../../node_modules/dayjs/plugin/utc");
|
|
26
|
-
_dayjs.extend(_utc);
|
|
27
|
-
return () => _dayjs("2022-01-01T00:00:00.000Z");
|
|
28
|
-
});
|
|
29
|
-
const nodemailer = {
|
|
30
|
-
smtp: {
|
|
31
|
-
sendMail: jest.fn()
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
|
-
jest.mock("../../node_modules/nodemailer", () => {
|
|
35
|
-
return {
|
|
36
|
-
createTransport: jest.fn(() => {
|
|
37
|
-
return {
|
|
38
|
-
sendMail: nodemailer.smtp.sendMail
|
|
39
|
-
};
|
|
40
|
-
})
|
|
41
|
-
};
|
|
42
|
-
});
|
|
43
|
-
jest.mock("../../node_modules/@joystick.js/ui/dist/component/generateId.js", () => {
|
|
44
|
-
return "component1234";
|
|
45
|
-
});
|
|
46
|
-
setAppSettingsForTest({
|
|
47
|
-
"config": {
|
|
48
|
-
"databases": [
|
|
49
|
-
{
|
|
50
|
-
"provider": "mongodb",
|
|
51
|
-
"users": true,
|
|
52
|
-
"options": {}
|
|
53
|
-
}
|
|
54
|
-
],
|
|
55
|
-
"i18n": {
|
|
56
|
-
"defaultLanguage": "en-US"
|
|
57
|
-
},
|
|
58
|
-
"middleware": {},
|
|
59
|
-
"email": {
|
|
60
|
-
"from": "app@test.com",
|
|
61
|
-
"smtp": {
|
|
62
|
-
"host": "fake.email.com",
|
|
63
|
-
"port": 587,
|
|
64
|
-
"username": "test",
|
|
65
|
-
"password": "password"
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
},
|
|
69
|
-
"global": {},
|
|
70
|
-
"public": {},
|
|
71
|
-
"private": {}
|
|
72
|
-
});
|
|
73
|
-
const dayjs = (await import("dayjs")).default;
|
|
74
|
-
const app = (await import("./index")).default;
|
|
75
|
-
const generateId = (await import("../lib/generateId")).default;
|
|
76
|
-
global.joystick = {
|
|
77
|
-
settings: {
|
|
78
|
-
config: {
|
|
79
|
-
databases: [{
|
|
80
|
-
"provider": "mongodb",
|
|
81
|
-
"users": true,
|
|
82
|
-
"options": {}
|
|
83
|
-
}]
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
};
|
|
87
|
-
describe("index.js", () => {
|
|
88
|
-
beforeAll(async () => {
|
|
89
|
-
process.env.PORT = 3600;
|
|
90
|
-
await startTestDatabase("mongodb");
|
|
91
|
-
});
|
|
92
|
-
afterEach(async () => {
|
|
93
|
-
await killPortProcess(process.env.PORT);
|
|
94
|
-
});
|
|
95
|
-
afterAll(async () => {
|
|
96
|
-
await stopTestDatabase();
|
|
97
|
-
});
|
|
98
|
-
test("registers render-related routes", async () => {
|
|
99
|
-
const instance = await app({});
|
|
100
|
-
if (instance?.server?.close && typeof instance.server.close === "function") {
|
|
101
|
-
instance.server.close();
|
|
102
|
-
}
|
|
103
|
-
if (instance?.app?._router) {
|
|
104
|
-
const routeRegexes = getRouteRegexes(instance.app._router.stack);
|
|
105
|
-
assertRoutesExistInRegexes(routeRegexes, [
|
|
106
|
-
"/_joystick/heartbeat",
|
|
107
|
-
"/_joystick/utils/process.js",
|
|
108
|
-
"/_joystick/index.client.js",
|
|
109
|
-
"/_joystick/index.css",
|
|
110
|
-
"/_joystick/ui",
|
|
111
|
-
"/_joystick/hmr/client.js"
|
|
112
|
-
]);
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
test("registers accounts-related routes", async () => {
|
|
116
|
-
const instance = await app({});
|
|
117
|
-
if (instance?.server?.close && typeof instance.server.close === "function") {
|
|
118
|
-
instance.server.close();
|
|
119
|
-
}
|
|
120
|
-
if (instance?.app?._router) {
|
|
121
|
-
const routeRegexes = getRouteRegexes(instance.app._router.stack);
|
|
122
|
-
assertRoutesExistInRegexes(routeRegexes, [
|
|
123
|
-
"/api/_accounts/login",
|
|
124
|
-
"/api/_accounts/logout",
|
|
125
|
-
"/api/_accounts/signup",
|
|
126
|
-
"/api/_accounts/recover-password",
|
|
127
|
-
"/api/_accounts/reset-password"
|
|
128
|
-
]);
|
|
129
|
-
}
|
|
130
|
-
});
|
|
131
|
-
test("registers getter routes", async () => {
|
|
132
|
-
const instance = await app({
|
|
133
|
-
api: {
|
|
134
|
-
getters: {
|
|
135
|
-
posts: {
|
|
136
|
-
input: {},
|
|
137
|
-
get: () => {
|
|
138
|
-
}
|
|
139
|
-
},
|
|
140
|
-
post: {
|
|
141
|
-
input: {},
|
|
142
|
-
get: () => {
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
});
|
|
148
|
-
if (instance?.server?.close && typeof instance.server.close === "function") {
|
|
149
|
-
instance.server.close();
|
|
150
|
-
}
|
|
151
|
-
if (instance?.app?._router) {
|
|
152
|
-
const routeRegexes = getRouteRegexes(instance.app._router.stack);
|
|
153
|
-
assertRoutesExistInRegexes(routeRegexes, [
|
|
154
|
-
"/api/_getters/posts",
|
|
155
|
-
"/api/_getters/post"
|
|
156
|
-
]);
|
|
157
|
-
}
|
|
158
|
-
});
|
|
159
|
-
test("registers setter routes", async () => {
|
|
160
|
-
const instance = await app({
|
|
161
|
-
api: {
|
|
162
|
-
setters: {
|
|
163
|
-
createPost: {
|
|
164
|
-
input: {},
|
|
165
|
-
set: () => {
|
|
166
|
-
}
|
|
167
|
-
},
|
|
168
|
-
updatePost: {
|
|
169
|
-
input: {},
|
|
170
|
-
set: () => {
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
});
|
|
176
|
-
if (instance?.server?.close && typeof instance.server.close === "function") {
|
|
177
|
-
instance.server.close();
|
|
178
|
-
}
|
|
179
|
-
if (instance?.app?._router) {
|
|
180
|
-
const routeRegexes = getRouteRegexes(instance.app._router.stack);
|
|
181
|
-
assertRoutesExistInRegexes(routeRegexes, [
|
|
182
|
-
"/api/_setters/createPost",
|
|
183
|
-
"/api/_setters/updatePost"
|
|
184
|
-
]);
|
|
185
|
-
}
|
|
186
|
-
});
|
|
187
|
-
test("registers custom routes if passed as function", async () => {
|
|
188
|
-
const instance = await app({
|
|
189
|
-
routes: {
|
|
190
|
-
"/latest": () => {
|
|
191
|
-
},
|
|
192
|
-
"/profile/:_id": () => {
|
|
193
|
-
},
|
|
194
|
-
"/category/:category/posts": () => {
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
});
|
|
198
|
-
if (instance?.app?._router) {
|
|
199
|
-
const routeRegexes = getRouteRegexes(instance.app._router.stack);
|
|
200
|
-
assertRoutesExistInRegexes(routeRegexes, [
|
|
201
|
-
"/latest",
|
|
202
|
-
"/profile/123",
|
|
203
|
-
"/category/tutorials/posts"
|
|
204
|
-
]);
|
|
205
|
-
}
|
|
206
|
-
if (instance?.server?.close && typeof instance.server.close === "function") {
|
|
207
|
-
instance.server.close();
|
|
208
|
-
}
|
|
209
|
-
});
|
|
210
|
-
test("does not register custom routes if passed as function not set to a function", async () => {
|
|
211
|
-
const instance = await app({
|
|
212
|
-
routes: {
|
|
213
|
-
"/latest": "function",
|
|
214
|
-
"/profile/:_id": "function",
|
|
215
|
-
"/category/:category/posts": "function"
|
|
216
|
-
}
|
|
217
|
-
});
|
|
218
|
-
if (instance?.app?._router) {
|
|
219
|
-
const routeRegexes = getRouteRegexes(instance.app._router.stack);
|
|
220
|
-
assertRoutesDoNotExistInRegexes(routeRegexes, [
|
|
221
|
-
"/latest",
|
|
222
|
-
"/profile/123",
|
|
223
|
-
"/category/tutorials/posts"
|
|
224
|
-
]);
|
|
225
|
-
}
|
|
226
|
-
if (instance?.server?.close && typeof instance.server.close === "function") {
|
|
227
|
-
instance.server.close();
|
|
228
|
-
}
|
|
229
|
-
});
|
|
230
|
-
test("registers custom routes if passed as an object", async () => {
|
|
231
|
-
const instance = await app({
|
|
232
|
-
routes: {
|
|
233
|
-
"/latest": {
|
|
234
|
-
method: "GET",
|
|
235
|
-
handler: () => {
|
|
236
|
-
}
|
|
237
|
-
},
|
|
238
|
-
"/profile/:_id": {
|
|
239
|
-
method: "POST",
|
|
240
|
-
handler: () => {
|
|
241
|
-
}
|
|
242
|
-
},
|
|
243
|
-
"/category/:category/posts": {
|
|
244
|
-
method: "PUT",
|
|
245
|
-
handler: () => {
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
});
|
|
250
|
-
if (instance?.server?.close && typeof instance.server.close === "function") {
|
|
251
|
-
instance.server.close();
|
|
252
|
-
}
|
|
253
|
-
if (instance?.app?._router) {
|
|
254
|
-
const routeRegexes = getRouteRegexes(instance.app._router.stack);
|
|
255
|
-
assertRoutesExistInRegexes(routeRegexes, [
|
|
256
|
-
"/latest",
|
|
257
|
-
"/profile/123",
|
|
258
|
-
"/category/tutorials/posts"
|
|
259
|
-
]);
|
|
260
|
-
}
|
|
261
|
-
});
|
|
262
|
-
test("does not register custom routes if passed as an object without a valid method", async () => {
|
|
263
|
-
const instance = await app({
|
|
264
|
-
routes: {
|
|
265
|
-
"/latest": {
|
|
266
|
-
method: "CAT"
|
|
267
|
-
},
|
|
268
|
-
"/profile/:_id": {
|
|
269
|
-
handler: () => {
|
|
270
|
-
}
|
|
271
|
-
},
|
|
272
|
-
"/category/:category/posts": {
|
|
273
|
-
method: "PUT",
|
|
274
|
-
handler: () => {
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
});
|
|
279
|
-
if (instance?.server?.close && typeof instance.server.close === "function") {
|
|
280
|
-
instance.server.close();
|
|
281
|
-
}
|
|
282
|
-
if (instance?.app?._router) {
|
|
283
|
-
const routeRegexes = getRouteRegexes(instance.app._router.stack);
|
|
284
|
-
assertRoutesDoNotExistInRegexes(routeRegexes, [
|
|
285
|
-
"/latest",
|
|
286
|
-
"/profile/123"
|
|
287
|
-
]);
|
|
288
|
-
assertRoutesExistInRegexes(routeRegexes, [
|
|
289
|
-
"/category/tutorials/posts"
|
|
290
|
-
]);
|
|
291
|
-
}
|
|
292
|
-
});
|
|
293
|
-
test("does not register custom routes if passed as an object without a handler function", async () => {
|
|
294
|
-
const instance = await app({
|
|
295
|
-
routes: {
|
|
296
|
-
"/latest": {
|
|
297
|
-
method: "GET"
|
|
298
|
-
},
|
|
299
|
-
"/profile/:_id": {
|
|
300
|
-
method: "POST"
|
|
301
|
-
},
|
|
302
|
-
"/category/:category/posts": {
|
|
303
|
-
method: "PUT"
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
});
|
|
307
|
-
if (instance?.server?.close && typeof instance.server.close === "function") {
|
|
308
|
-
instance.server.close();
|
|
309
|
-
}
|
|
310
|
-
if (instance?.app?._router) {
|
|
311
|
-
const routeRegexes = getRouteRegexes(instance.app._router.stack);
|
|
312
|
-
assertRoutesDoNotExistInRegexes(routeRegexes, [
|
|
313
|
-
"/latest",
|
|
314
|
-
"/profile/123",
|
|
315
|
-
"/category/tutorials/posts"
|
|
316
|
-
]);
|
|
317
|
-
}
|
|
318
|
-
});
|
|
319
|
-
test("sets build errors on process", async () => {
|
|
320
|
-
const instance = await app({});
|
|
321
|
-
const testMessage = { id: generateId() };
|
|
322
|
-
process.emit("message", JSON.stringify(testMessage));
|
|
323
|
-
if (instance?.server?.close && typeof instance.server.close === "function") {
|
|
324
|
-
instance.server.close();
|
|
325
|
-
}
|
|
326
|
-
expect(process.BUILD_ERROR).toEqual(testMessage);
|
|
327
|
-
});
|
|
328
|
-
test("if callback function is assigned to function-based route, it is called as expected", async () => {
|
|
329
|
-
const instance = await app({
|
|
330
|
-
routes: {
|
|
331
|
-
"/test": (req2, res2) => {
|
|
332
|
-
res2.status(200).send("Called as expected.");
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
});
|
|
336
|
-
const route = instance?.app?._router?.stack.find((stackItem) => {
|
|
337
|
-
return stackItem?.route?.path === "/test";
|
|
338
|
-
});
|
|
339
|
-
if (instance?.server?.close && typeof instance.server.close === "function") {
|
|
340
|
-
instance.server.close();
|
|
341
|
-
}
|
|
342
|
-
const req = mockRequest();
|
|
343
|
-
const res = mockResponse();
|
|
344
|
-
const handler = route?.route?.stack[0] && route?.route?.stack[0].handle;
|
|
345
|
-
if (handler) {
|
|
346
|
-
await handler(req, res, () => {
|
|
347
|
-
});
|
|
348
|
-
expect(res.status).toHaveBeenCalledWith(200);
|
|
349
|
-
expect(res.send).toHaveBeenCalledWith("Called as expected.");
|
|
350
|
-
}
|
|
351
|
-
});
|
|
352
|
-
test("if callback function is assigned to an object-based route, it is called as expected", async () => {
|
|
353
|
-
const instance = await app({
|
|
354
|
-
routes: {
|
|
355
|
-
"/test": {
|
|
356
|
-
method: "GET",
|
|
357
|
-
handler: (req2, res2) => {
|
|
358
|
-
res2.status(200).send("Called as expected.");
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
});
|
|
363
|
-
const route = instance?.app?._router?.stack.find((stackItem) => {
|
|
364
|
-
return stackItem?.route?.path === "/test";
|
|
365
|
-
});
|
|
366
|
-
if (instance?.server?.close && typeof instance.server.close === "function") {
|
|
367
|
-
instance.server.close();
|
|
368
|
-
}
|
|
369
|
-
const req = mockRequest();
|
|
370
|
-
const res = mockResponse();
|
|
371
|
-
const handler = route?.route?.stack[0] && route?.route?.stack[0].handle;
|
|
372
|
-
if (handler) {
|
|
373
|
-
await handler(req, res, () => {
|
|
374
|
-
});
|
|
375
|
-
expect(res.status).toHaveBeenCalledWith(200);
|
|
376
|
-
expect(res.send).toHaveBeenCalledWith("Called as expected.");
|
|
377
|
-
}
|
|
378
|
-
});
|
|
379
|
-
test("verify that a /_accounts/authenticated request will return true if passed a valid cookie", async () => {
|
|
380
|
-
const instance = await app({});
|
|
381
|
-
const route = instance?.app?._router?.stack.find((stackItem) => {
|
|
382
|
-
return stackItem?.route?.path === "/api/_accounts/authenticated";
|
|
383
|
-
});
|
|
384
|
-
const req = mockRequest({
|
|
385
|
-
cookies: {
|
|
386
|
-
joystickLoginToken: "testToken123",
|
|
387
|
-
joystickLoginTokenExpiresAt: dayjs().add(1, "minute").format()
|
|
388
|
-
}
|
|
389
|
-
});
|
|
390
|
-
const res = mockResponse();
|
|
391
|
-
const handler = route?.route?.stack[0] && route?.route?.stack[0].handle;
|
|
392
|
-
if (handler) {
|
|
393
|
-
await handler(req, res, () => {
|
|
394
|
-
});
|
|
395
|
-
expect(res.status).toHaveBeenCalledWith(200);
|
|
396
|
-
expect(res.send).toHaveBeenCalledWith('{"status":200,"authenticated":true}');
|
|
397
|
-
}
|
|
398
|
-
if (instance?.server?.close && typeof instance.server.close === "function") {
|
|
399
|
-
instance.server.close();
|
|
400
|
-
}
|
|
401
|
-
});
|
|
402
|
-
test("verify that a /_accounts/authenticated request will return false if passed an invalid cookie", async () => {
|
|
403
|
-
const instance = await app({});
|
|
404
|
-
const route = instance?.app?._router?.stack.find((stackItem) => {
|
|
405
|
-
return stackItem?.route?.path === "/api/_accounts/authenticated";
|
|
406
|
-
});
|
|
407
|
-
if (instance?.server?.close && typeof instance.server.close === "function") {
|
|
408
|
-
instance.server.close();
|
|
409
|
-
}
|
|
410
|
-
const req = mockRequest({
|
|
411
|
-
cookies: {
|
|
412
|
-
joystickLoginToken: "testToken123",
|
|
413
|
-
joystickLoginTokenExpiresAt: jest.requireActual("../../node_modules/dayjs")().subtract(1, "minute").format()
|
|
414
|
-
}
|
|
415
|
-
});
|
|
416
|
-
const res = mockResponse();
|
|
417
|
-
const handler = route?.route?.stack[0] && route?.route?.stack[0].handle;
|
|
418
|
-
if (handler) {
|
|
419
|
-
await handler(req, res, () => {
|
|
420
|
-
});
|
|
421
|
-
expect(res.status).toHaveBeenCalledWith(401);
|
|
422
|
-
expect(res.send).toHaveBeenCalledWith('{"status":401,"authenticated":false}');
|
|
423
|
-
}
|
|
424
|
-
});
|
|
425
|
-
test("verify that a /_accounts/signup request will set a cookie and return a user if passed a valid signup request", async () => {
|
|
426
|
-
const instance = await app({});
|
|
427
|
-
const route = instance?.app?._router?.stack.find((stackItem) => {
|
|
428
|
-
return stackItem?.route?.path === "/api/_accounts/signup";
|
|
429
|
-
});
|
|
430
|
-
if (instance?.server?.close && typeof instance.server.close === "function") {
|
|
431
|
-
instance.server.close();
|
|
432
|
-
}
|
|
433
|
-
const req = mockRequest({
|
|
434
|
-
body: {
|
|
435
|
-
emailAddress: "test@test.com",
|
|
436
|
-
password: "password",
|
|
437
|
-
metadata: {}
|
|
438
|
-
}
|
|
439
|
-
});
|
|
440
|
-
const res = mockResponse();
|
|
441
|
-
const handler = route?.route?.stack[0] && route?.route?.stack[0].handle;
|
|
442
|
-
if (handler) {
|
|
443
|
-
await handler(req, res, () => {
|
|
444
|
-
});
|
|
445
|
-
expect(res.cookie).toHaveBeenCalledWith("joystickLoginToken", "abc1234", { secure: true, httpOnly: true, expires: dayjs().toDate() });
|
|
446
|
-
expect(res.cookie).toHaveBeenCalledWith("joystickLoginTokenExpiresAt", "2022-01-31T00:00:00Z", { secure: true, httpOnly: true, expires: dayjs().toDate() });
|
|
447
|
-
expect(res.status).toHaveBeenCalledWith(200);
|
|
448
|
-
expect(res.send).toHaveBeenCalledWith('{"_id":"abc1234","password":"hashed$password","emailAddress":"test@test.com"}');
|
|
449
|
-
}
|
|
450
|
-
});
|
|
451
|
-
test("verify that a /_accounts/login request will set a cookie and return a user if passed a valid login request", async () => {
|
|
452
|
-
const instance = await app({});
|
|
453
|
-
const route = instance?.app?._router?.stack.find((stackItem) => {
|
|
454
|
-
return stackItem?.route?.path === "/api/_accounts/login";
|
|
455
|
-
});
|
|
456
|
-
if (instance?.server?.close && typeof instance.server.close === "function") {
|
|
457
|
-
instance.server.close();
|
|
458
|
-
}
|
|
459
|
-
const req = mockRequest({
|
|
460
|
-
body: {
|
|
461
|
-
emailAddress: "test@test.com",
|
|
462
|
-
password: "password"
|
|
463
|
-
}
|
|
464
|
-
});
|
|
465
|
-
const res = mockResponse();
|
|
466
|
-
const handler = route?.route?.stack[0] && route?.route?.stack[0].handle;
|
|
467
|
-
if (handler) {
|
|
468
|
-
await handler(req, res, () => {
|
|
469
|
-
});
|
|
470
|
-
expect(res.cookie).toHaveBeenCalledWith("joystickLoginToken", "abc1234", { secure: true, httpOnly: true, expires: dayjs().toDate() });
|
|
471
|
-
expect(res.cookie).toHaveBeenCalledWith("joystickLoginTokenExpiresAt", "2022-01-31T00:00:00Z", { secure: true, httpOnly: true, expires: dayjs().toDate() });
|
|
472
|
-
expect(res.status).toHaveBeenCalledWith(200);
|
|
473
|
-
expect(res.send).toHaveBeenCalledWith('{"_id":"abc1234","emailAddress":"test@test.com"}');
|
|
474
|
-
}
|
|
475
|
-
});
|
|
476
|
-
test("verify that a /_accounts/logout request deletes the cookie", async () => {
|
|
477
|
-
const instance = await app({});
|
|
478
|
-
const loginRoute = instance?.app?._router?.stack.find((stackItem) => {
|
|
479
|
-
return stackItem?.route?.path === "/api/_accounts/login";
|
|
480
|
-
});
|
|
481
|
-
const logoutRoute = instance?.app?._router?.stack.find((stackItem) => {
|
|
482
|
-
return stackItem?.route?.path === "/api/_accounts/logout";
|
|
483
|
-
});
|
|
484
|
-
if (instance?.server?.close && typeof instance.server.close === "function") {
|
|
485
|
-
instance.server.close();
|
|
486
|
-
}
|
|
487
|
-
const loginRequest = mockRequest({
|
|
488
|
-
body: {
|
|
489
|
-
emailAddress: "test@test.com",
|
|
490
|
-
password: "password"
|
|
491
|
-
}
|
|
492
|
-
});
|
|
493
|
-
const loginResponse = mockResponse();
|
|
494
|
-
const loginHandler = loginRoute?.route?.stack[0] && loginRoute?.route?.stack[0].handle;
|
|
495
|
-
const logoutRequest = mockRequest();
|
|
496
|
-
const logoutResponse = mockResponse();
|
|
497
|
-
const logoutHandler = logoutRoute?.route?.stack[0] && logoutRoute?.route?.stack[0].handle;
|
|
498
|
-
if (loginHandler && logoutHandler) {
|
|
499
|
-
await loginHandler(loginRequest, loginResponse, () => {
|
|
500
|
-
});
|
|
501
|
-
await logoutHandler(logoutRequest, logoutResponse, () => {
|
|
502
|
-
});
|
|
503
|
-
expect(loginResponse.cookie).toHaveBeenCalledWith("joystickLoginToken", "abc1234", { secure: true, httpOnly: true, expires: dayjs().toDate() });
|
|
504
|
-
expect(loginResponse.cookie).toHaveBeenCalledWith("joystickLoginTokenExpiresAt", "2022-01-31T00:00:00Z", { secure: true, httpOnly: true, expires: dayjs().toDate() });
|
|
505
|
-
expect(logoutResponse.cookie).toHaveBeenCalledWith("joystickLoginToken", null, { secure: true, httpOnly: true, expires: dayjs().toDate() });
|
|
506
|
-
expect(logoutResponse.cookie).toHaveBeenCalledWith("joystickLoginTokenExpiresAt", null, { secure: true, httpOnly: true, expires: dayjs().toDate() });
|
|
507
|
-
}
|
|
508
|
-
});
|
|
509
|
-
test("verify that a /_accounts/recover-password request attempts to send an email", async () => {
|
|
510
|
-
const instance = await app({});
|
|
511
|
-
const route = instance?.app?._router?.stack.find((stackItem) => {
|
|
512
|
-
return stackItem?.route?.path === "/api/_accounts/recover-password";
|
|
513
|
-
});
|
|
514
|
-
if (instance?.server?.close && typeof instance.server.close === "function") {
|
|
515
|
-
instance.server.close();
|
|
516
|
-
}
|
|
517
|
-
const req = mockRequest({
|
|
518
|
-
body: {
|
|
519
|
-
emailAddress: "test@test.com"
|
|
520
|
-
}
|
|
521
|
-
});
|
|
522
|
-
const res = mockResponse();
|
|
523
|
-
const handler = route?.route?.stack[0] && route?.route?.stack[0].handle;
|
|
524
|
-
if (handler) {
|
|
525
|
-
await handler(req, res, () => {
|
|
526
|
-
});
|
|
527
|
-
expect(nodemailer.smtp.sendMail).toHaveBeenCalledWith({
|
|
528
|
-
from: "app@test.com",
|
|
529
|
-
to: "test@test.com",
|
|
530
|
-
subject: "Reset Your Password",
|
|
531
|
-
html: `<!doctype html>
|
|
532
|
-
<html class="no-js" lang="en">
|
|
533
|
-
<head>
|
|
534
|
-
<meta charset="utf-8">
|
|
535
|
-
<title>Joystick</title>
|
|
536
|
-
|
|
537
|
-
</head>
|
|
538
|
-
<body style="color: #000; font-family: 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif; font-size: 16px; line-height: 24px;">
|
|
539
|
-
<div id="email"><div js-c="testComponent1234">
|
|
540
|
-
<p>A password reset was requested for this email address (test@test.com). If you requested this reset, click the link below to reset your password:</p>
|
|
541
|
-
<p><a href="http://localhost:3600/reset-password/abc1234">Reset Password</a></p>
|
|
542
|
-
</div></div>
|
|
543
|
-
</body>
|
|
544
|
-
</html>
|
|
545
|
-
`,
|
|
546
|
-
text: "A password reset was requested for this email address (test@test.com). If you\nrequested this reset, click the link below to reset your password:\n\nReset Password [http://localhost:3600/reset-password/abc1234]"
|
|
547
|
-
});
|
|
548
|
-
}
|
|
549
|
-
});
|
|
550
|
-
test("verify that a /_accounts/reset-password request sets a cookie and returns the user", async () => {
|
|
551
|
-
const instance = await app({});
|
|
552
|
-
const route = instance?.app?._router?.stack.find((stackItem) => {
|
|
553
|
-
return stackItem?.route?.path === "/api/_accounts/reset-password";
|
|
554
|
-
});
|
|
555
|
-
if (instance?.server?.close && typeof instance.server.close === "function") {
|
|
556
|
-
instance.server.close();
|
|
557
|
-
}
|
|
558
|
-
const req = mockRequest({
|
|
559
|
-
body: {
|
|
560
|
-
token: "abc1234",
|
|
561
|
-
password: "test@test.com"
|
|
562
|
-
}
|
|
563
|
-
});
|
|
564
|
-
const res = mockResponse();
|
|
565
|
-
const handler = route?.route?.stack[0] && route?.route?.stack[0].handle;
|
|
566
|
-
if (handler) {
|
|
567
|
-
await handler(req, res, () => {
|
|
568
|
-
});
|
|
569
|
-
expect(res.cookie).toHaveBeenCalledWith("joystickLoginToken", "abc1234", { secure: true, httpOnly: true, expires: dayjs().toDate() });
|
|
570
|
-
expect(res.cookie).toHaveBeenCalledWith("joystickLoginTokenExpiresAt", "2022-01-31T00:00:00Z", { secure: true, httpOnly: true, expires: dayjs().toDate() });
|
|
571
|
-
expect(res.status).toHaveBeenCalledWith(200);
|
|
572
|
-
expect(res.send).toHaveBeenCalledWith('{"_id":"abc1234","password":"hashed$password","emailAddress":"test@test.com","sessions":[{"token":"abc1234","tokenExpiresAt":"2022-01-31T00:00:00Z"}],"passwordResetTokens":[]}');
|
|
573
|
-
}
|
|
574
|
-
});
|
|
575
|
-
});
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
var sanitizeQueryParameters_default = (req, res, next) => {
|
|
2
|
-
const queryParameters = Object.entries(req?.query);
|
|
3
|
-
const htmlRegex = new RegExp(/<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+>/g);
|
|
4
|
-
for (let i = 0; i < queryParameters?.length; i += 1) {
|
|
5
|
-
const [key, value] = queryParameters[i];
|
|
6
|
-
const keyHTMLMatches = key?.match(htmlRegex);
|
|
7
|
-
const valueHTMLMatches = value?.match(htmlRegex);
|
|
8
|
-
if (keyHTMLMatches?.length > 0 || valueHTMLMatches?.length > 0) {
|
|
9
|
-
delete req.query[key];
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
next();
|
|
13
|
-
};
|
|
14
|
-
export {
|
|
15
|
-
sanitizeQueryParameters_default as default
|
|
16
|
-
};
|
package/dist/email/send.test.js
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { jest } from "@jest/globals";
|
|
2
|
-
import chalk from "chalk";
|
|
3
|
-
import send from "./send";
|
|
4
|
-
import setAppSettingsForTest from "../tests/lib/setAppSettingsForTest";
|
|
5
|
-
setAppSettingsForTest({
|
|
6
|
-
"config": {
|
|
7
|
-
"databases": [
|
|
8
|
-
{
|
|
9
|
-
"provider": "mongodb",
|
|
10
|
-
"users": true,
|
|
11
|
-
"options": {}
|
|
12
|
-
}
|
|
13
|
-
],
|
|
14
|
-
"i18n": {
|
|
15
|
-
"defaultLanguage": "en-US"
|
|
16
|
-
},
|
|
17
|
-
"middleware": {},
|
|
18
|
-
"email": {
|
|
19
|
-
"from": "app@test.com",
|
|
20
|
-
"smtp": {}
|
|
21
|
-
}
|
|
22
|
-
},
|
|
23
|
-
"global": {},
|
|
24
|
-
"public": {},
|
|
25
|
-
"private": {}
|
|
26
|
-
});
|
|
27
|
-
console.warn = jest.fn();
|
|
28
|
-
describe("send.js", () => {
|
|
29
|
-
test("console.warns an error when bad smtp settings are passed", async () => {
|
|
30
|
-
const result = await send({ template: "test", props: {} });
|
|
31
|
-
expect(result).toBe(null);
|
|
32
|
-
expect(console.warn.mock.calls).toEqual([
|
|
33
|
-
[chalk.redBright("Invalid SMTP settings: config.smtp not defined in settings.test.js")],
|
|
34
|
-
[chalk.redBright("Cannot send email, invalid SMTP settings.")]
|
|
35
|
-
]);
|
|
36
|
-
});
|
|
37
|
-
});
|