@joystick.js/node-canary 0.0.0-canary.33 → 0.0.0-canary.331
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 -3
- 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 +60 -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 +51 -28
- 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 +36 -0
- package/dist/app/databases/postgresql/index.js +88 -2
- package/dist/app/databases/postgresql/queries/accounts.js +5 -2
- package/dist/app/databases/postgresql/queries/queues.js +83 -38
- 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 +229 -78
- 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 +8 -66
- package/dist/app/middleware/session.js +12 -11
- package/dist/app/queues/index.js +82 -32
- 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/validateInstanceToken.js +16 -0
- 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 +35 -17
- 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 -2
- 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 -26
- package/dist/email/send.test.js +0 -37
- package/dist/validation/index.test.js +0 -463
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
var get_insecure_landing_page_html_default = (hostname = "", path = "") => {
|
|
2
|
+
const url = `https://${hostname}${path}`;
|
|
3
|
+
return `
|
|
4
|
+
<html>
|
|
5
|
+
<head>
|
|
6
|
+
<title>Insecure Connection</title>
|
|
7
|
+
<style type="text/css">
|
|
8
|
+
body {
|
|
9
|
+
font-family: "Helvetica Neue", "Helvetica", "Arial", sans-serif;
|
|
10
|
+
font-size: 16px;
|
|
11
|
+
line-height: 24px;
|
|
12
|
+
background: #fafafa;
|
|
13
|
+
display: flex;
|
|
14
|
+
margin: 0;
|
|
15
|
+
padding: 20px;
|
|
16
|
+
justify-content: center;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.warning {
|
|
20
|
+
width: 100%;
|
|
21
|
+
max-width: 500px;
|
|
22
|
+
background: #fff;
|
|
23
|
+
border: 1px solid #eee;
|
|
24
|
+
border-radius: 3px;
|
|
25
|
+
padding: 35px;
|
|
26
|
+
align-self: flex-start;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.warning h1 {
|
|
30
|
+
color: #000;
|
|
31
|
+
font-size: 22px;
|
|
32
|
+
line-height: 30px;
|
|
33
|
+
margin: 0;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.warning p {
|
|
37
|
+
font-size: 16px;
|
|
38
|
+
line-height: 25px;
|
|
39
|
+
font-weight: 400;
|
|
40
|
+
color: #555;
|
|
41
|
+
margin: 10px 0 0;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.warning p a {
|
|
45
|
+
color: #555;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@media screen and (min-width: 768px) {
|
|
49
|
+
body {
|
|
50
|
+
padding: 40px;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
</style>
|
|
54
|
+
</head>
|
|
55
|
+
<body>
|
|
56
|
+
<div class="warning">
|
|
57
|
+
<h1>Insecure Connection<h1>
|
|
58
|
+
<p>The site at this URL requires an SSL-secured connection. Please visit <a href="${url}">${url}</a> instead. You will be automatically redirected in 15 seconds.</p>
|
|
59
|
+
</div>
|
|
60
|
+
<script>
|
|
61
|
+
setTimeout(() => {
|
|
62
|
+
location.href = '${url}';
|
|
63
|
+
}, 15 * 1000);
|
|
64
|
+
<\/script>
|
|
65
|
+
</body>
|
|
66
|
+
</html>
|
|
67
|
+
`;
|
|
68
|
+
};
|
|
69
|
+
export {
|
|
70
|
+
get_insecure_landing_page_html_default as default
|
|
71
|
+
};
|
|
@@ -58,7 +58,7 @@ var client_default = (() => websocketClient({
|
|
|
58
58
|
location.reload();
|
|
59
59
|
}
|
|
60
60
|
if (message?.settings) {
|
|
61
|
-
window.__joystick_settings__ =
|
|
61
|
+
window.__joystick_settings__ = message?.settings;
|
|
62
62
|
window.joystick.settings = message?.settings;
|
|
63
63
|
}
|
|
64
64
|
if (clientIndex) {
|
|
@@ -71,32 +71,36 @@ var client_default = (() => websocketClient({
|
|
|
71
71
|
if (CSS) {
|
|
72
72
|
const updatedCSS = document.createElement("link");
|
|
73
73
|
updatedCSS.setAttribute("rel", "stylesheet");
|
|
74
|
-
updatedCSS.setAttribute("href",
|
|
74
|
+
updatedCSS.setAttribute("href", `/_joystick/index.css?v=${new Date().getTime()}`);
|
|
75
75
|
document.head.appendChild(updatedCSS);
|
|
76
76
|
}
|
|
77
77
|
if (isFileChange && isPageInLayout) {
|
|
78
78
|
(async () => {
|
|
79
79
|
window.__joystick_childrenBeforeHMRUpdate__ = window.joystick?._internal?.tree?.instance?.children;
|
|
80
|
-
const layoutComponentFile = await import(`${window.__joystick_layout__}?
|
|
81
|
-
|
|
80
|
+
const layoutComponentFile = await import(`${window.__joystick_layout__}?v=${new Date().getTime()}`).catch(() => {
|
|
81
|
+
location.reload();
|
|
82
|
+
});
|
|
83
|
+
const pageComponentFile = await import(`${window.window.__joystick_layout_page_url__}?t=${new Date().getTime()}`).catch(() => {
|
|
84
|
+
location.reload();
|
|
85
|
+
});
|
|
82
86
|
const layout = layoutComponentFile.default;
|
|
83
87
|
const page = pageComponentFile.default;
|
|
84
88
|
window.joystick.mount(layout, Object.assign({ page }, window.__joystick_ssr_props__), document.getElementById("app"));
|
|
85
89
|
if (connection.send) {
|
|
86
|
-
|
|
87
|
-
connection.send({ type: "HMR_UPDATE_COMPLETE", sessions });
|
|
90
|
+
connection.send({ type: "HMR_UPDATE_COMPLETE" });
|
|
88
91
|
}
|
|
89
92
|
})();
|
|
90
93
|
}
|
|
91
94
|
if (isFileChange && !isPageInLayout) {
|
|
92
95
|
(async () => {
|
|
93
96
|
window.__joystick_childrenBeforeHMRUpdate__ = window.joystick?._internal?.tree?.instance?.children;
|
|
94
|
-
const pageComponentFile = await import(`${window.__joystick_page_url__}?
|
|
97
|
+
const pageComponentFile = await import(`${window.__joystick_page_url__}?v=${new Date().getTime()}`).catch(() => {
|
|
98
|
+
location.reload();
|
|
99
|
+
});
|
|
95
100
|
const page = pageComponentFile.default;
|
|
96
101
|
window.joystick.mount(page, Object.assign({}, window.__joystick_ssr_props__), document.getElementById("app"));
|
|
97
102
|
if (connection.send) {
|
|
98
|
-
|
|
99
|
-
connection.send({ type: "HMR_UPDATE_COMPLETE", sessions });
|
|
103
|
+
connection.send({ type: "HMR_UPDATE_COMPLETE" });
|
|
100
104
|
}
|
|
101
105
|
})();
|
|
102
106
|
}
|
|
@@ -15,11 +15,10 @@ import runUserQuery from "../accounts/runUserQuery.js";
|
|
|
15
15
|
import replaceBackslashesWithForwardSlashes from "../../lib/replaceBackslashesWithForwardSlashes.js";
|
|
16
16
|
import replaceFileProtocol from "../../lib/replaceFileProtocol.js";
|
|
17
17
|
import getBuildPath from "../../lib/getBuildPath.js";
|
|
18
|
-
import sanitizeRequestParameters from "./sanitizeRequestParameters.js";
|
|
19
18
|
import session from "./session.js";
|
|
20
19
|
import csp from "./csp.js";
|
|
21
20
|
const cwd = replaceFileProtocol(replaceBackslashesWithForwardSlashes(process.cwd()));
|
|
22
|
-
const faviconPath =
|
|
21
|
+
const faviconPath = "public/favicon.ico";
|
|
23
22
|
var middleware_default = ({
|
|
24
23
|
app,
|
|
25
24
|
port,
|
|
@@ -27,7 +26,7 @@ var middleware_default = ({
|
|
|
27
26
|
appInstance,
|
|
28
27
|
cspConfig
|
|
29
28
|
}) => {
|
|
30
|
-
if (process.env.NODE_ENV
|
|
29
|
+
if (process.env.NODE_ENV !== "development") {
|
|
31
30
|
app.use(insecure);
|
|
32
31
|
}
|
|
33
32
|
const buildPath = getBuildPath();
|
|
@@ -42,8 +41,8 @@ var middleware_default = ({
|
|
|
42
41
|
}
|
|
43
42
|
next();
|
|
44
43
|
});
|
|
45
|
-
app.use(sanitizeRequestParameters);
|
|
46
44
|
app.use(requestMethods);
|
|
45
|
+
app.enable("trust proxy");
|
|
47
46
|
if (cspConfig) {
|
|
48
47
|
app.use((req, res, next) => csp(req, res, next, cspConfig));
|
|
49
48
|
}
|
|
@@ -73,7 +72,9 @@ var middleware_default = ({
|
|
|
73
72
|
app.use(cookieParser());
|
|
74
73
|
app.use(bodyParser(middlewareConfig?.bodyParser));
|
|
75
74
|
app.use(cors(middlewareConfig?.cors, port));
|
|
76
|
-
|
|
75
|
+
if (process.databases?._sessions) {
|
|
76
|
+
app.use((req, res, next) => session(req, res, next));
|
|
77
|
+
}
|
|
77
78
|
app.use(async (req, res, next) => {
|
|
78
79
|
const loginTokenHasExpired = await hasLoginTokenExpired(res, req?.cookies?.joystickLoginToken, req?.cookies?.joystickLoginTokenExpiresAt);
|
|
79
80
|
req.context = {
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
+
import get_insecure_landing_page_html from "./get_insecure_landing_page_html.js";
|
|
1
2
|
var insecure_default = (req, res, next) => {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
if (process.env.NODE_ENV != "development" && !req.secure && !isForwardedFromHTTPS) {
|
|
5
|
-
return res.redirect("https://" + req.headers.host + req.url);
|
|
3
|
+
if (!req.secure) {
|
|
4
|
+
return res.send(get_insecure_landing_page_html(req?.headers?.host, req?.url));
|
|
6
5
|
}
|
|
7
6
|
next();
|
|
8
7
|
};
|
|
@@ -9,6 +9,9 @@ import replaceFileProtocol from "../../lib/replaceFileProtocol.js";
|
|
|
9
9
|
import replaceBackslashesWithForwardSlashes from "../../lib/replaceBackslashesWithForwardSlashes.js";
|
|
10
10
|
import getBuildPath from "../../lib/getBuildPath.js";
|
|
11
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";
|
|
12
15
|
const generateHash = (input = "") => {
|
|
13
16
|
return crypto.createHash("sha256").update(input).digest("hex");
|
|
14
17
|
};
|
|
@@ -66,71 +69,12 @@ const getCachedHTML = ({ cachePath, cacheFileName, currentDiff }) => {
|
|
|
66
69
|
const getUrl = (request = {}) => {
|
|
67
70
|
const [path = null] = request.url?.split("?");
|
|
68
71
|
return {
|
|
69
|
-
params:
|
|
70
|
-
query:
|
|
72
|
+
params: escapeKeyValuePair(request.params),
|
|
73
|
+
query: escapeKeyValuePair(request.query),
|
|
71
74
|
route: escapeHTML(request.route.path),
|
|
72
75
|
path: escapeHTML(path)
|
|
73
76
|
};
|
|
74
77
|
};
|
|
75
|
-
const getFile = async (buildPath = "") => {
|
|
76
|
-
const file = await import(buildPath);
|
|
77
|
-
return file.default;
|
|
78
|
-
};
|
|
79
|
-
const getTranslationsFile = async (languageFilePath = "", paths = "") => {
|
|
80
|
-
const languageFile = await getFile(`${paths.build}/i18n/${languageFilePath}`);
|
|
81
|
-
const isValidLanguageFile = languageFile && isObject(languageFile);
|
|
82
|
-
if (isValidLanguageFile) {
|
|
83
|
-
const translationsForPage = languageFile[paths.page];
|
|
84
|
-
return translationsForPage ? translationsForPage : languageFile;
|
|
85
|
-
}
|
|
86
|
-
return {};
|
|
87
|
-
};
|
|
88
|
-
const getTranslations = async (paths = {}, languagePreferences = []) => {
|
|
89
|
-
const languageFiles = fs.readdirSync(`${paths.build}/i18n`);
|
|
90
|
-
let matchingFile = null;
|
|
91
|
-
for (let i = 0; i < languagePreferences.length; i += 1) {
|
|
92
|
-
const languageRegex = languagePreferences[i];
|
|
93
|
-
const match = languageFiles.find((languageFile) => !!languageFile.match(languageRegex));
|
|
94
|
-
if (match) {
|
|
95
|
-
matchingFile = match;
|
|
96
|
-
break;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
const translationsFile = await getTranslationsFile(matchingFile, paths);
|
|
100
|
-
return translationsFile;
|
|
101
|
-
};
|
|
102
|
-
const getLanguagePreferenceRegexes = (userLanguage = "", browserLanguages = []) => {
|
|
103
|
-
let languagePreferences = [];
|
|
104
|
-
if (userLanguage) {
|
|
105
|
-
languagePreferences.push(userLanguage);
|
|
106
|
-
}
|
|
107
|
-
const filteredBrowserLanguages = browserLanguages?.filter((language) => {
|
|
108
|
-
return !language?.includes("*");
|
|
109
|
-
});
|
|
110
|
-
languagePreferences.push(...filteredBrowserLanguages);
|
|
111
|
-
languagePreferences.push(settings?.config?.i18n?.defaultLanguage);
|
|
112
|
-
return languagePreferences?.flatMap((language) => {
|
|
113
|
-
const variants = [language];
|
|
114
|
-
if (language?.length === 2) {
|
|
115
|
-
variants.push(`${language.substring(0, 2)}-`);
|
|
116
|
-
}
|
|
117
|
-
if (language?.length > 2) {
|
|
118
|
-
variants.push(`${language?.split("-")[0]}`);
|
|
119
|
-
variants.push(`${language?.split("-")[0]}-`);
|
|
120
|
-
}
|
|
121
|
-
return variants;
|
|
122
|
-
})?.map((languageString) => {
|
|
123
|
-
const lastCharacter = languageString[languageString.length - 1];
|
|
124
|
-
if (lastCharacter === "-") {
|
|
125
|
-
return new RegExp(`^${languageString}[A-Z]+.js`, "g");
|
|
126
|
-
}
|
|
127
|
-
return new RegExp(`^${languageString}.js`, "g");
|
|
128
|
-
});
|
|
129
|
-
};
|
|
130
|
-
const parseBrowserLanguages = (languages = "") => {
|
|
131
|
-
const rawLanguages = languages.split(",");
|
|
132
|
-
return rawLanguages?.map((rawLanguage) => rawLanguage.split(";")[0]);
|
|
133
|
-
};
|
|
134
78
|
var render_default = (req, res, next, appInstance = {}) => {
|
|
135
79
|
res.render = async function(path = "", options = {}) {
|
|
136
80
|
const urlFormattedForCache = req?.url?.split("/")?.filter((part) => !!part)?.join("_");
|
|
@@ -168,13 +112,11 @@ var render_default = (req, res, next, appInstance = {}) => {
|
|
|
168
112
|
return res.send(cachedHTML);
|
|
169
113
|
}
|
|
170
114
|
}
|
|
171
|
-
const pageFile = await
|
|
115
|
+
const pageFile = await importFile(pagePath);
|
|
172
116
|
const Page = pageFile;
|
|
173
|
-
const layoutFile = layoutPath ? await
|
|
117
|
+
const layoutFile = layoutPath ? await importFile(layoutPath) : null;
|
|
174
118
|
const Layout = layoutFile;
|
|
175
|
-
const
|
|
176
|
-
const languagePreferenceRegexes = getLanguagePreferenceRegexes(req?.context?.user?.language, browserLanguages);
|
|
177
|
-
const translations = await getTranslations({ build: buildPath, page: path }, languagePreferenceRegexes);
|
|
119
|
+
const translations = await getTranslations({ build: buildPath, page: path }, req);
|
|
178
120
|
const url = getUrl(req);
|
|
179
121
|
const props = { ...options?.props || {} };
|
|
180
122
|
if (layoutPath && fs.existsSync(layoutPath)) {
|
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
import setCookie from "../../lib/setCookie.js";
|
|
2
|
-
import
|
|
3
|
-
var session_default = (req, res, next
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
}
|
|
9
|
-
if (!
|
|
10
|
-
|
|
2
|
+
import runSessionQuery from "../runSessionQuery.js";
|
|
3
|
+
var session_default = async (req, res, next) => {
|
|
4
|
+
await runSessionQuery("delete_expired_sessions");
|
|
5
|
+
let session_id = req?.cookies?.joystickSession;
|
|
6
|
+
const existing_session = session_id ? await runSessionQuery("get_session", {
|
|
7
|
+
session_id
|
|
8
|
+
}) : null;
|
|
9
|
+
if (!existing_session) {
|
|
10
|
+
session_id = await runSessionQuery("create_session");
|
|
11
|
+
setCookie(res, "joystickSession", session_id);
|
|
11
12
|
}
|
|
12
13
|
req.cookies = {
|
|
13
14
|
...req?.cookies || {},
|
|
14
|
-
joystickSession:
|
|
15
|
+
joystickSession: session_id
|
|
15
16
|
};
|
|
16
17
|
req.context = {
|
|
17
18
|
...req?.context || {},
|
|
18
|
-
session:
|
|
19
|
+
session: await runSessionQuery("get_session", { session_id })
|
|
19
20
|
};
|
|
20
21
|
next();
|
|
21
22
|
};
|
package/dist/app/queues/index.js
CHANGED
|
@@ -1,28 +1,34 @@
|
|
|
1
|
-
import dayjs from "dayjs";
|
|
2
1
|
import fs from "fs";
|
|
3
2
|
import os from "os";
|
|
4
3
|
import generateId from "../../lib/generateId";
|
|
5
4
|
import getTargetDatabaseProvider from "../databases/getTargetDatabaseProvider";
|
|
6
5
|
import queryMap from "../databases/queryMap";
|
|
6
|
+
import chalk from "chalk";
|
|
7
|
+
import timestamps from "../../lib/timestamps";
|
|
7
8
|
class Queue {
|
|
8
9
|
constructor(queueName = "", queueOptions = {}) {
|
|
10
|
+
this._initDatabase = this._initDatabase.bind(this);
|
|
9
11
|
this.machineId = fs.readFileSync(`${os.homedir()}/.cheatcode/MACHINE_ID`, "utf-8")?.trim().replace(/\n/g, "");
|
|
10
12
|
this.name = queueName;
|
|
11
13
|
this.options = {
|
|
12
14
|
concurrentJobs: 1,
|
|
13
15
|
...queueOptions
|
|
14
16
|
};
|
|
15
|
-
this._initDatabase();
|
|
16
|
-
if (queueOptions?.runOnStartup) {
|
|
17
|
-
this.run();
|
|
18
|
-
}
|
|
17
|
+
this._initDatabase(this?.options?.external, this?.options?.database?.provider);
|
|
19
18
|
}
|
|
20
|
-
async _initDatabase() {
|
|
21
|
-
const queuesDatabase = getTargetDatabaseProvider("queues");
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
async _initDatabase(is_external = false, database_provider = null) {
|
|
20
|
+
const queuesDatabase = database_provider || getTargetDatabaseProvider("queues");
|
|
21
|
+
const queue_queries_for_database_provider = queryMap[queuesDatabase]?.queues;
|
|
22
|
+
const db = this._getDatabaseConnection();
|
|
23
|
+
console.log({
|
|
24
|
+
queuesDatabase,
|
|
25
|
+
queue_queries_for_database_provider,
|
|
26
|
+
db: db.namespace
|
|
27
|
+
});
|
|
28
|
+
if (db && queue_queries_for_database_provider && typeof queue_queries_for_database_provider === "object" && !Array.isArray(queue_queries_for_database_provider)) {
|
|
29
|
+
this.db = Object.entries(queue_queries_for_database_provider || {})?.reduce((boundQueries = {}, [queryFunctionName, queryFunction]) => {
|
|
25
30
|
boundQueries[queryFunctionName] = queryFunction.bind({
|
|
31
|
+
db,
|
|
26
32
|
machineId: this.machineId,
|
|
27
33
|
queue: {
|
|
28
34
|
name: this.name,
|
|
@@ -30,18 +36,43 @@ class Queue {
|
|
|
30
36
|
}
|
|
31
37
|
});
|
|
32
38
|
return boundQueries;
|
|
33
|
-
}, {});
|
|
34
|
-
|
|
39
|
+
}, { _connection: db });
|
|
40
|
+
if (!is_external) {
|
|
41
|
+
await this.db.initializeDatabase(queuesDatabase);
|
|
42
|
+
if (this?.options?.runOnStartup) {
|
|
43
|
+
this.run();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
35
46
|
}
|
|
36
47
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
48
|
+
_getDatabaseConnection() {
|
|
49
|
+
if (this?.options?.database) {
|
|
50
|
+
const { provider, name } = this?.options?.database;
|
|
51
|
+
const existing_connection = process.databases && process.databases[provider] && process.databases[provider][name];
|
|
52
|
+
if (!existing_connection) {
|
|
53
|
+
console.warn(chalk.red(`Connection to database ${provider}.${name} not found on process. Cannot start queue.`));
|
|
54
|
+
}
|
|
55
|
+
return existing_connection || null;
|
|
56
|
+
}
|
|
57
|
+
return process.databases._queues;
|
|
58
|
+
}
|
|
59
|
+
async add(options = {}) {
|
|
60
|
+
const nextRunAt = options?.nextRunAt === "now" || !options?.nextRunAt ? new Date().toISOString() : options?.nextRunAt;
|
|
61
|
+
const job_to_add = {
|
|
40
62
|
_id: generateId(),
|
|
41
63
|
status: "pending",
|
|
64
|
+
environment: process.env.NODE_ENV,
|
|
42
65
|
...options,
|
|
43
|
-
nextRunAt
|
|
44
|
-
}
|
|
66
|
+
nextRunAt
|
|
67
|
+
};
|
|
68
|
+
const job_definition = this.options.jobs[options?.job];
|
|
69
|
+
if (job_definition && typeof job_definition?.preflight?.onBeforeAdd === "function") {
|
|
70
|
+
const canAddJob = await job_definition?.preflight?.onBeforeAdd(job_to_add, this.db._connection, `queue_${this.name}`);
|
|
71
|
+
if (!canAddJob) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
this.db.addJob(job_to_add);
|
|
45
76
|
}
|
|
46
77
|
async _checkIfOkayToRunJobs() {
|
|
47
78
|
const jobsRunning = await this._getNumberOfJobsRunning();
|
|
@@ -55,7 +86,7 @@ class Queue {
|
|
|
55
86
|
return;
|
|
56
87
|
}
|
|
57
88
|
if (!this.options.retryJobsRunningBeforeRestart) {
|
|
58
|
-
return this.db.
|
|
89
|
+
return this.db.deleteIncompleteJobsForMachine();
|
|
59
90
|
}
|
|
60
91
|
return this.db.setJobsForMachinePending();
|
|
61
92
|
}
|
|
@@ -69,41 +100,60 @@ class Queue {
|
|
|
69
100
|
const okayToRunJobs = await this._checkIfOkayToRunJobs();
|
|
70
101
|
if (okayToRunJobs && !process.env.HALT_QUEUES) {
|
|
71
102
|
const nextJob = await this.db.getNextJobToRun();
|
|
72
|
-
this.
|
|
103
|
+
this.handleNextJob(nextJob);
|
|
73
104
|
}
|
|
74
105
|
}, 300);
|
|
75
106
|
});
|
|
76
107
|
}
|
|
77
|
-
|
|
78
|
-
|
|
108
|
+
async handleNextJob(nextJob = {}) {
|
|
109
|
+
const job_definition = this.options.jobs[nextJob?.job];
|
|
110
|
+
if (nextJob && nextJob?.job && job_definition && typeof job_definition?.run === "function") {
|
|
79
111
|
try {
|
|
80
|
-
|
|
112
|
+
if (typeof job_definition?.preflight?.okayToRun === "function") {
|
|
113
|
+
const okay_to_run = await job_definition?.preflight?.okayToRun(nextJob?.payload, nextJob);
|
|
114
|
+
if (!okay_to_run) {
|
|
115
|
+
return this._handleRequeueJob(nextJob, timestamps.get_future_time("seconds", job_definition?.preflight?.requeueDelayInSeconds || 10));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (!isNaN(job_definition?.maxAttempts)) {
|
|
119
|
+
if (nextJob?.attempts >= parseInt(job_definition?.maxAttempts, 10)) {
|
|
120
|
+
if (typeof job_definition?.onMaxAttemptsExhausted === "function") {
|
|
121
|
+
await job_definition.onMaxAttemptsExhausted(nextJob);
|
|
122
|
+
}
|
|
123
|
+
return this._handleDeleteJob(nextJob?._id);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
await this._logAttempt(nextJob?._id);
|
|
127
|
+
await job_definition.run(nextJob?.payload, {
|
|
81
128
|
...nextJob,
|
|
82
129
|
queue: this,
|
|
83
130
|
completed: () => this._handleJobCompleted(nextJob?._id),
|
|
84
|
-
failed: (error) => this._handleJobFailed(nextJob
|
|
131
|
+
failed: (error) => this._handleJobFailed(nextJob, job_definition, error),
|
|
85
132
|
delete: () => this._handleDeleteJob(nextJob?._id),
|
|
86
|
-
requeue: (
|
|
133
|
+
requeue: (nextRunAt = "") => this._handleRequeueJob(nextJob, nextRunAt)
|
|
87
134
|
});
|
|
88
135
|
} catch (exception) {
|
|
89
|
-
this._handleJobFailed(nextJob
|
|
90
|
-
if (this.options.jobs[nextJob.job]?.requeueOnFailure) {
|
|
91
|
-
this._handleRequeueJob(nextRunAt, dayjs().add(10, "seconds").format());
|
|
92
|
-
}
|
|
136
|
+
this._handleJobFailed(nextJob, job_definition, exception);
|
|
93
137
|
}
|
|
94
138
|
}
|
|
95
139
|
}
|
|
140
|
+
_logAttempt(jobId = "") {
|
|
141
|
+
return this.db.logAttempt(jobId);
|
|
142
|
+
}
|
|
96
143
|
_handleJobCompleted(jobId = "") {
|
|
97
144
|
return this.db.setJobCompleted(jobId);
|
|
98
145
|
}
|
|
99
|
-
_handleJobFailed(
|
|
100
|
-
|
|
146
|
+
_handleJobFailed(nextJob = {}, job_definition = {}, error = "") {
|
|
147
|
+
if (job_definition?.requeueOnFailure) {
|
|
148
|
+
return this._handleRequeueJob(nextJob, timestamps.get_future_time("seconds", 10));
|
|
149
|
+
}
|
|
150
|
+
return this.db.setJobFailed(nextJob?._id, error);
|
|
101
151
|
}
|
|
102
152
|
_handleDeleteJob(jobId = "") {
|
|
103
153
|
return this.db.deleteJob(jobId);
|
|
104
154
|
}
|
|
105
|
-
_handleRequeueJob(job = {},
|
|
106
|
-
return this.db.requeueJob(job?._id,
|
|
155
|
+
_handleRequeueJob(job = {}, nextRunAt = new Date().toISOString()) {
|
|
156
|
+
return this.db.requeueJob(job?._id, nextRunAt);
|
|
107
157
|
}
|
|
108
158
|
list(status = "") {
|
|
109
159
|
const query = {};
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import getAPIURLComponent from "./getAPIURLComponent";
|
|
2
2
|
import getAPIContext from "./getAPIContext";
|
|
3
3
|
import formatAPIError from "../lib/formatAPIError";
|
|
4
|
-
import validate from "../validation/index.js";
|
|
5
|
-
import getOutput from "./getOutput";
|
|
6
|
-
import sanitizeAPIResponse from "./sanitizeAPIResponse";
|
|
7
|
-
import { isObject } from "../validation/lib/typeValidators";
|
|
8
4
|
import validateSession from "./validateSession.js";
|
|
9
5
|
import runGetter from "./runGetter.js";
|
|
10
6
|
var registerGetters_default = (express, getters = [], context = {}, APIOptions = {}, appInstance = {}) => {
|
|
@@ -12,7 +8,7 @@ var registerGetters_default = (express, getters = [], context = {}, APIOptions =
|
|
|
12
8
|
if (app) {
|
|
13
9
|
for (const [getterName, getterOptions] of getters) {
|
|
14
10
|
app.get(`/api/_getters/${getAPIURLComponent(getterName)}`, ...Array.isArray(getterOptions?.middleware) ? getterOptions?.middleware : [], async (req, res) => {
|
|
15
|
-
const isValidSession = validateSession(req, res
|
|
11
|
+
const isValidSession = await validateSession(req, res);
|
|
16
12
|
if (!isValidSession) {
|
|
17
13
|
return;
|
|
18
14
|
}
|
|
@@ -30,7 +26,10 @@ var registerGetters_default = (express, getters = [], context = {}, APIOptions =
|
|
|
30
26
|
return res.status(200).send(JSON.stringify(response));
|
|
31
27
|
}).catch((error) => {
|
|
32
28
|
if (typeof error === "string") {
|
|
33
|
-
|
|
29
|
+
const sanitized_error = error?.replace("[runGetter] ", "")?.replace("[runGetter.handleRunGetter] ", "");
|
|
30
|
+
return res.status(500).send(JSON.stringify({
|
|
31
|
+
errors: [formatAPIError(new Error(sanitized_error))]
|
|
32
|
+
}));
|
|
34
33
|
}
|
|
35
34
|
if (typeof error === "object" && !Array.isArray(error)) {
|
|
36
35
|
return res.status(error?.errors && error?.errors[0]?.status || 400).send(JSON.stringify(error));
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import getAPIURLComponent from "./getAPIURLComponent.js";
|
|
2
2
|
import getAPIContext from "./getAPIContext.js";
|
|
3
3
|
import formatAPIError from "../lib/formatAPIError.js";
|
|
4
|
-
import validate from "../validation/index.js";
|
|
5
|
-
import getOutput from "./getOutput.js";
|
|
6
|
-
import sanitizeAPIResponse from "./sanitizeAPIResponse.js";
|
|
7
|
-
import { isObject } from "../validation/lib/typeValidators.js";
|
|
8
4
|
import validateSession from "./validateSession.js";
|
|
9
5
|
import runSetter from "./runSetter.js";
|
|
10
6
|
var registerSetters_default = (express, setters = [], context = {}, APIOptions = {}, appInstance = {}) => {
|
|
@@ -12,7 +8,7 @@ var registerSetters_default = (express, setters = [], context = {}, APIOptions =
|
|
|
12
8
|
if (app) {
|
|
13
9
|
for (const [setterName, setterOptions] of setters) {
|
|
14
10
|
app.post(`/api/_setters/${getAPIURLComponent(setterName)}`, ...Array.isArray(setterOptions?.middleware) ? setterOptions?.middleware : [], async (req, res) => {
|
|
15
|
-
const isValidSession = validateSession(req, res
|
|
11
|
+
const isValidSession = await validateSession(req, res);
|
|
16
12
|
if (!isValidSession) {
|
|
17
13
|
return;
|
|
18
14
|
}
|
|
@@ -30,7 +26,10 @@ var registerSetters_default = (express, setters = [], context = {}, APIOptions =
|
|
|
30
26
|
return res.status(200).send(JSON.stringify(response));
|
|
31
27
|
}).catch((error) => {
|
|
32
28
|
if (typeof error === "string") {
|
|
33
|
-
|
|
29
|
+
const sanitized_error = error?.replace("[runSetter] ", "")?.replace("[runSetter.handleRunSetter] ", "");
|
|
30
|
+
return res.status(500).send(JSON.stringify({
|
|
31
|
+
errors: [formatAPIError(new Error(sanitized_error))]
|
|
32
|
+
}));
|
|
34
33
|
}
|
|
35
34
|
if (typeof error === "object" && !Array.isArray(error)) {
|
|
36
35
|
return res.status(error?.errors && error?.errors[0]?.status || 400).send(JSON.stringify(error));
|
package/dist/app/runGetter.js
CHANGED
|
@@ -3,19 +3,31 @@ import formatAPIError from "../lib/formatAPIError.js";
|
|
|
3
3
|
import { isObject } from "../validation/lib/typeValidators.js";
|
|
4
4
|
import getOutput from "./getOutput.js";
|
|
5
5
|
import sanitizeAPIResponse from "./sanitizeAPIResponse.js";
|
|
6
|
-
|
|
6
|
+
import trackFunctionCall from "../test/trackFunctionCall.js";
|
|
7
|
+
import getSanitizedContext from "../../getSanitizedContext.js";
|
|
8
|
+
const handleRunGetter = async (name = "", getterOptions = {}, input = {}, output = {}, context = {}, APIOptions = {}) => {
|
|
7
9
|
try {
|
|
8
10
|
const shouldDisableSanitizationForGetter = getterOptions?.sanitize === false;
|
|
9
11
|
const shouldSanitizeOutput = (getterOptions?.sanitize || APIOptions?.sanitize) === true || isObject(APIOptions?.sanitize || getterOptions?.sanitize);
|
|
10
|
-
const
|
|
12
|
+
const sanitizedContext = getSanitizedContext(context);
|
|
13
|
+
trackFunctionCall(`node.api.getters.${name}`, [
|
|
14
|
+
input,
|
|
15
|
+
sanitizedContext
|
|
16
|
+
]);
|
|
17
|
+
const data = await getterOptions?.get(input, context);
|
|
11
18
|
const response = output ? getOutput(data, output) : data;
|
|
12
19
|
return !shouldDisableSanitizationForGetter && shouldSanitizeOutput ? sanitizeAPIResponse(response, getterOptions?.sanitize || APIOptions?.sanitize) : response;
|
|
13
20
|
} catch (exception) {
|
|
14
21
|
throw new Error(`[runGetter.handleRunGetter] ${exception.message}`);
|
|
15
22
|
}
|
|
16
23
|
};
|
|
17
|
-
const handleRunAuthorization = async (getterOptions = {}, input = {}, context = {}) => {
|
|
24
|
+
const handleRunAuthorization = async (name = "", getterOptions = {}, input = {}, context = {}) => {
|
|
18
25
|
try {
|
|
26
|
+
const sanitizedContext = getSanitizedContext(context);
|
|
27
|
+
trackFunctionCall(`node.api.getters.${name}.authorized`, [
|
|
28
|
+
input,
|
|
29
|
+
sanitizedContext
|
|
30
|
+
]);
|
|
19
31
|
const authorization = await getterOptions?.authorized(input, context);
|
|
20
32
|
if (typeof authorization === "boolean") {
|
|
21
33
|
return authorization;
|
|
@@ -77,7 +89,7 @@ const runGetter = async (options, { resolve, reject }) => {
|
|
|
77
89
|
}
|
|
78
90
|
}
|
|
79
91
|
if (typeof options?.getterOptions?.authorized === "function") {
|
|
80
|
-
const authorized = await handleRunAuthorization(options?.getterOptions, options?.input, options?.context);
|
|
92
|
+
const authorized = await handleRunAuthorization(options?.getterName, options?.getterOptions, options?.input, options?.context);
|
|
81
93
|
if (!authorized || typeof authorized === "string") {
|
|
82
94
|
return reject({
|
|
83
95
|
errors: [
|
|
@@ -87,7 +99,7 @@ const runGetter = async (options, { resolve, reject }) => {
|
|
|
87
99
|
}
|
|
88
100
|
}
|
|
89
101
|
if (typeof options?.getterOptions?.get === "function") {
|
|
90
|
-
const response = await handleRunGetter(options?.getterOptions, options?.input, options?.output, options?.context, options?.APIOptions);
|
|
102
|
+
const response = await handleRunGetter(options?.getterName, options?.getterOptions, options?.input, options?.output, options?.context, options?.APIOptions);
|
|
91
103
|
return resolve(response);
|
|
92
104
|
}
|
|
93
105
|
resolve();
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import queryMap from "./databases/queryMap";
|
|
2
|
+
import getTargetDatabaseConnection from "./databases/getTargetDatabaseConnection.js";
|
|
3
|
+
var runSessionQuery_default = async (queryName = "", inputs = {}) => {
|
|
4
|
+
const sessionsDatabase = getTargetDatabaseConnection("sessions");
|
|
5
|
+
const queryMapForDatabase = sessionsDatabase && queryMap && queryMap[sessionsDatabase?.provider] && queryMap[sessionsDatabase?.provider]?.sessions;
|
|
6
|
+
const query = queryMapForDatabase && queryMapForDatabase[queryName];
|
|
7
|
+
if (sessionsDatabase?.connection && query) {
|
|
8
|
+
const response = await queryMapForDatabase[queryName](inputs, sessionsDatabase?.connection);
|
|
9
|
+
return Promise.resolve(response);
|
|
10
|
+
}
|
|
11
|
+
return null;
|
|
12
|
+
};
|
|
13
|
+
export {
|
|
14
|
+
runSessionQuery_default as default
|
|
15
|
+
};
|