@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
|
@@ -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
|
};
|
|
@@ -8,6 +8,10 @@ import generateErrorPage from "../../lib/generateErrorPage.js";
|
|
|
8
8
|
import replaceFileProtocol from "../../lib/replaceFileProtocol.js";
|
|
9
9
|
import replaceBackslashesWithForwardSlashes from "../../lib/replaceBackslashesWithForwardSlashes.js";
|
|
10
10
|
import getBuildPath from "../../lib/getBuildPath.js";
|
|
11
|
+
import escapeHTML from "../../lib/escapeHTML.js";
|
|
12
|
+
import escapeKeyValuePair from "../../lib/escapeKeyValuePair.js";
|
|
13
|
+
import importFile from "../../lib/importFile.js";
|
|
14
|
+
import getTranslations from "./getTranslations.js";
|
|
11
15
|
const generateHash = (input = "") => {
|
|
12
16
|
return crypto.createHash("sha256").update(input).digest("hex");
|
|
13
17
|
};
|
|
@@ -65,71 +69,12 @@ const getCachedHTML = ({ cachePath, cacheFileName, currentDiff }) => {
|
|
|
65
69
|
const getUrl = (request = {}) => {
|
|
66
70
|
const [path = null] = request.url?.split("?");
|
|
67
71
|
return {
|
|
68
|
-
params: request.params,
|
|
69
|
-
query: request.query,
|
|
70
|
-
route: request.route.path,
|
|
71
|
-
path
|
|
72
|
+
params: escapeKeyValuePair(request.params),
|
|
73
|
+
query: escapeKeyValuePair(request.query),
|
|
74
|
+
route: escapeHTML(request.route.path),
|
|
75
|
+
path: escapeHTML(path)
|
|
72
76
|
};
|
|
73
77
|
};
|
|
74
|
-
const getFile = async (buildPath = "") => {
|
|
75
|
-
const file = await import(buildPath);
|
|
76
|
-
return file.default;
|
|
77
|
-
};
|
|
78
|
-
const getTranslationsFile = async (languageFilePath = "", paths = "") => {
|
|
79
|
-
const languageFile = await getFile(`${paths.build}/i18n/${languageFilePath}`);
|
|
80
|
-
const isValidLanguageFile = languageFile && isObject(languageFile);
|
|
81
|
-
if (isValidLanguageFile) {
|
|
82
|
-
const translationsForPage = languageFile[paths.page];
|
|
83
|
-
return translationsForPage ? translationsForPage : languageFile;
|
|
84
|
-
}
|
|
85
|
-
return {};
|
|
86
|
-
};
|
|
87
|
-
const getTranslations = async (paths = {}, languagePreferences = []) => {
|
|
88
|
-
const languageFiles = fs.readdirSync(`${paths.build}/i18n`);
|
|
89
|
-
let matchingFile = null;
|
|
90
|
-
for (let i = 0; i < languagePreferences.length; i += 1) {
|
|
91
|
-
const languageRegex = languagePreferences[i];
|
|
92
|
-
const match = languageFiles.find((languageFile) => !!languageFile.match(languageRegex));
|
|
93
|
-
if (match) {
|
|
94
|
-
matchingFile = match;
|
|
95
|
-
break;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
const translationsFile = await getTranslationsFile(matchingFile, paths);
|
|
99
|
-
return translationsFile;
|
|
100
|
-
};
|
|
101
|
-
const getLanguagePreferenceRegexes = (userLanguage = "", browserLanguages = []) => {
|
|
102
|
-
let languagePreferences = [];
|
|
103
|
-
if (userLanguage) {
|
|
104
|
-
languagePreferences.push(userLanguage);
|
|
105
|
-
}
|
|
106
|
-
const filteredBrowserLanguages = browserLanguages?.filter((language) => {
|
|
107
|
-
return !language?.includes("*");
|
|
108
|
-
});
|
|
109
|
-
languagePreferences.push(...filteredBrowserLanguages);
|
|
110
|
-
languagePreferences.push(settings?.config?.i18n?.defaultLanguage);
|
|
111
|
-
return languagePreferences?.flatMap((language) => {
|
|
112
|
-
const variants = [language];
|
|
113
|
-
if (language?.length === 2) {
|
|
114
|
-
variants.push(`${language.substring(0, 2)}-`);
|
|
115
|
-
}
|
|
116
|
-
if (language?.length > 2) {
|
|
117
|
-
variants.push(`${language?.split("-")[0]}`);
|
|
118
|
-
variants.push(`${language?.split("-")[0]}-`);
|
|
119
|
-
}
|
|
120
|
-
return variants;
|
|
121
|
-
})?.map((languageString) => {
|
|
122
|
-
const lastCharacter = languageString[languageString.length - 1];
|
|
123
|
-
if (lastCharacter === "-") {
|
|
124
|
-
return new RegExp(`^${languageString}[A-Z]+.js`, "g");
|
|
125
|
-
}
|
|
126
|
-
return new RegExp(`^${languageString}.js`, "g");
|
|
127
|
-
});
|
|
128
|
-
};
|
|
129
|
-
const parseBrowserLanguages = (languages = "") => {
|
|
130
|
-
const rawLanguages = languages.split(",");
|
|
131
|
-
return rawLanguages?.map((rawLanguage) => rawLanguage.split(";")[0]);
|
|
132
|
-
};
|
|
133
78
|
var render_default = (req, res, next, appInstance = {}) => {
|
|
134
79
|
res.render = async function(path = "", options = {}) {
|
|
135
80
|
const urlFormattedForCache = req?.url?.split("/")?.filter((part) => !!part)?.join("_");
|
|
@@ -167,13 +112,11 @@ var render_default = (req, res, next, appInstance = {}) => {
|
|
|
167
112
|
return res.send(cachedHTML);
|
|
168
113
|
}
|
|
169
114
|
}
|
|
170
|
-
const pageFile = await
|
|
115
|
+
const pageFile = await importFile(pagePath);
|
|
171
116
|
const Page = pageFile;
|
|
172
|
-
const layoutFile = layoutPath ? await
|
|
117
|
+
const layoutFile = layoutPath ? await importFile(layoutPath) : null;
|
|
173
118
|
const Layout = layoutFile;
|
|
174
|
-
const
|
|
175
|
-
const languagePreferenceRegexes = getLanguagePreferenceRegexes(req?.context?.user?.language, browserLanguages);
|
|
176
|
-
const translations = await getTranslations({ build: buildPath, page: path }, languagePreferenceRegexes);
|
|
119
|
+
const translations = await getTranslations({ build: buildPath, page: path }, req);
|
|
177
120
|
const url = getUrl(req);
|
|
178
121
|
const props = { ...options?.props || {} };
|
|
179
122
|
if (layoutPath && fs.existsSync(layoutPath)) {
|
|
@@ -1,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,29 @@
|
|
|
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
|
+
if (db && queue_queries_for_database_provider && typeof queue_queries_for_database_provider === "object" && !Array.isArray(queue_queries_for_database_provider)) {
|
|
24
|
+
this.db = Object.entries(queue_queries_for_database_provider || {})?.reduce((boundQueries = {}, [queryFunctionName, queryFunction]) => {
|
|
25
25
|
boundQueries[queryFunctionName] = queryFunction.bind({
|
|
26
|
+
db,
|
|
26
27
|
machineId: this.machineId,
|
|
27
28
|
queue: {
|
|
28
29
|
name: this.name,
|
|
@@ -31,16 +32,32 @@ class Queue {
|
|
|
31
32
|
});
|
|
32
33
|
return boundQueries;
|
|
33
34
|
}, {});
|
|
34
|
-
|
|
35
|
+
if (!is_external) {
|
|
36
|
+
await this.db.initializeDatabase(queuesDatabase);
|
|
37
|
+
if (this?.options?.runOnStartup) {
|
|
38
|
+
this.run();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
35
41
|
}
|
|
36
42
|
}
|
|
43
|
+
_getDatabaseConnection() {
|
|
44
|
+
if (this?.options?.database) {
|
|
45
|
+
const { provider, name } = this?.options?.database;
|
|
46
|
+
const existing_connection = process.databases && process.databases[provider] && process.databases[provider][name];
|
|
47
|
+
if (!existing_connection) {
|
|
48
|
+
console.warn(chalk.red(`Connection to database ${provider}.${name} not found on process. Cannot start queue.`));
|
|
49
|
+
}
|
|
50
|
+
return existing_connection || null;
|
|
51
|
+
}
|
|
52
|
+
return process.databases._queues;
|
|
53
|
+
}
|
|
37
54
|
add(options = {}) {
|
|
38
|
-
const
|
|
55
|
+
const nextRunAt = options?.nextRunAt === "now" || !options?.nextRunAt ? new Date().toISOString() : options?.nextRunAt;
|
|
39
56
|
this.db.addJob({
|
|
40
57
|
_id: generateId(),
|
|
41
58
|
status: "pending",
|
|
42
59
|
...options,
|
|
43
|
-
nextRunAt
|
|
60
|
+
nextRunAt
|
|
44
61
|
});
|
|
45
62
|
}
|
|
46
63
|
async _checkIfOkayToRunJobs() {
|
|
@@ -55,7 +72,7 @@ class Queue {
|
|
|
55
72
|
return;
|
|
56
73
|
}
|
|
57
74
|
if (!this.options.retryJobsRunningBeforeRestart) {
|
|
58
|
-
return this.db.
|
|
75
|
+
return this.db.deleteIncompleteJobsForMachine();
|
|
59
76
|
}
|
|
60
77
|
return this.db.setJobsForMachinePending();
|
|
61
78
|
}
|
|
@@ -69,41 +86,60 @@ class Queue {
|
|
|
69
86
|
const okayToRunJobs = await this._checkIfOkayToRunJobs();
|
|
70
87
|
if (okayToRunJobs && !process.env.HALT_QUEUES) {
|
|
71
88
|
const nextJob = await this.db.getNextJobToRun();
|
|
72
|
-
this.
|
|
89
|
+
this.handleNextJob(nextJob);
|
|
73
90
|
}
|
|
74
91
|
}, 300);
|
|
75
92
|
});
|
|
76
93
|
}
|
|
77
|
-
|
|
78
|
-
|
|
94
|
+
async handleNextJob(nextJob = {}) {
|
|
95
|
+
const job_definition = this.options.jobs[nextJob?.job];
|
|
96
|
+
if (nextJob && nextJob?.job && job_definition && typeof job_definition?.run === "function") {
|
|
79
97
|
try {
|
|
80
|
-
|
|
98
|
+
if (typeof job_definition?.preflight?.okayToRun === "function") {
|
|
99
|
+
const okay_to_run = await job_definition?.preflight?.okayToRun(nextJob?.payload, nextJob);
|
|
100
|
+
if (!okay_to_run) {
|
|
101
|
+
return this._handleRequeueJob(nextJob, timestamps.get_future_time("seconds", job_definition?.preflight?.requeueDelayInSeconds || 10));
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (!isNaN(job_definition?.maxAttempts)) {
|
|
105
|
+
if (nextJob?.attempts >= parseInt(job_definition?.maxAttempts, 10)) {
|
|
106
|
+
if (typeof job_definition?.onMaxAttemptsExhausted === "function") {
|
|
107
|
+
await job_definition.onMaxAttemptsExhausted(nextJob);
|
|
108
|
+
}
|
|
109
|
+
return this._handleDeleteJob(nextJob?._id);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
await this._logAttempt(nextJob?._id);
|
|
113
|
+
await job_definition.run(nextJob?.payload, {
|
|
81
114
|
...nextJob,
|
|
82
115
|
queue: this,
|
|
83
116
|
completed: () => this._handleJobCompleted(nextJob?._id),
|
|
84
|
-
failed: (error) => this._handleJobFailed(nextJob
|
|
117
|
+
failed: (error) => this._handleJobFailed(nextJob, job_definition, error),
|
|
85
118
|
delete: () => this._handleDeleteJob(nextJob?._id),
|
|
86
|
-
requeue: (
|
|
119
|
+
requeue: (nextRunAt = "") => this._handleRequeueJob(nextJob, nextRunAt)
|
|
87
120
|
});
|
|
88
121
|
} catch (exception) {
|
|
89
|
-
this._handleJobFailed(nextJob
|
|
90
|
-
if (this.options.jobs[nextJob.job]?.requeueOnFailure) {
|
|
91
|
-
this._handleRequeueJob(nextRunAt, dayjs().add(10, "seconds").format());
|
|
92
|
-
}
|
|
122
|
+
this._handleJobFailed(nextJob, job_definition, exception);
|
|
93
123
|
}
|
|
94
124
|
}
|
|
95
125
|
}
|
|
126
|
+
_logAttempt(jobId = "") {
|
|
127
|
+
return this.db.logAttempt(jobId);
|
|
128
|
+
}
|
|
96
129
|
_handleJobCompleted(jobId = "") {
|
|
97
130
|
return this.db.setJobCompleted(jobId);
|
|
98
131
|
}
|
|
99
|
-
_handleJobFailed(
|
|
100
|
-
|
|
132
|
+
_handleJobFailed(nextJob = {}, job_definition = {}, error = "") {
|
|
133
|
+
if (job_definition?.requeueOnFailure) {
|
|
134
|
+
return this._handleRequeueJob(nextJob, timestamps.get_future_time("seconds", 10));
|
|
135
|
+
}
|
|
136
|
+
return this.db.setJobFailed(nextJob?._id, error);
|
|
101
137
|
}
|
|
102
138
|
_handleDeleteJob(jobId = "") {
|
|
103
139
|
return this.db.deleteJob(jobId);
|
|
104
140
|
}
|
|
105
|
-
_handleRequeueJob(job = {},
|
|
106
|
-
return this.db.requeueJob(job?._id,
|
|
141
|
+
_handleRequeueJob(job = {}, nextRunAt = new Date().toISOString()) {
|
|
142
|
+
return this.db.requeueJob(job?._id, nextRunAt);
|
|
107
143
|
}
|
|
108
144
|
list(status = "") {
|
|
109
145
|
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
|
+
};
|
package/dist/app/runSetter.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 handleRunSetter = async (name = "", setterOptions = {}, input = {}, output = {}, context = {}, APIOptions = {}) => {
|
|
7
9
|
try {
|
|
8
10
|
const shouldDisableSanitizationForSetter = setterOptions?.sanitize === false;
|
|
9
11
|
const shouldSanitizeOutput = (setterOptions?.sanitize || APIOptions?.sanitize) === true || isObject(APIOptions?.sanitize || setterOptions?.sanitize);
|
|
10
|
-
const
|
|
12
|
+
const sanitizedContext = getSanitizedContext(context);
|
|
13
|
+
trackFunctionCall(`node.api.setters.${name}`, [
|
|
14
|
+
input,
|
|
15
|
+
sanitizedContext
|
|
16
|
+
]);
|
|
17
|
+
const data = await setterOptions?.set(input, context);
|
|
11
18
|
const response = output ? getOutput(data, output) : data;
|
|
12
19
|
return !shouldDisableSanitizationForSetter && shouldSanitizeOutput ? sanitizeAPIResponse(response, setterOptions?.sanitize || APIOptions?.sanitize) : response;
|
|
13
20
|
} catch (exception) {
|
|
14
21
|
throw new Error(`[runSetter.handleRunSetter] ${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.setters.${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 runSetter = async (options, { resolve, reject }) => {
|
|
|
77
89
|
}
|
|
78
90
|
}
|
|
79
91
|
if (typeof options?.setterOptions?.authorized === "function") {
|
|
80
|
-
const authorized = await handleRunAuthorization(options?.setterOptions, options?.input, options?.context);
|
|
92
|
+
const authorized = await handleRunAuthorization(options?.setterName, options?.setterOptions, options?.input, options?.context);
|
|
81
93
|
if (!authorized || typeof authorized === "string") {
|
|
82
94
|
return reject({
|
|
83
95
|
errors: [
|
|
@@ -87,7 +99,7 @@ const runSetter = async (options, { resolve, reject }) => {
|
|
|
87
99
|
}
|
|
88
100
|
}
|
|
89
101
|
if (typeof options?.setterOptions?.set === "function") {
|
|
90
|
-
const response = await handleRunSetter(options?.setterOptions, options?.input, options?.output, options?.context, options?.APIOptions);
|
|
102
|
+
const response = await handleRunSetter(options?.setterName, options?.setterOptions, options?.input, options?.output, options?.context, options?.APIOptions);
|
|
91
103
|
return resolve(response);
|
|
92
104
|
}
|
|
93
105
|
resolve();
|
|
@@ -9,7 +9,7 @@ const sanitizeAPIResponse = (data = null) => {
|
|
|
9
9
|
sanitizedData = escapeHTML(sanitizedData)?.trim();
|
|
10
10
|
}
|
|
11
11
|
if (util.isObject(sanitizedData) && !Array.isArray(sanitizedData)) {
|
|
12
|
-
sanitizedData = Object.entries(sanitizedData)?.reduce((result = {}, [key, value]) => {
|
|
12
|
+
sanitizedData = Object.entries(sanitizedData || {})?.reduce((result = {}, [key, value]) => {
|
|
13
13
|
result[key] = sanitizeAPIResponse(value);
|
|
14
14
|
return result;
|
|
15
15
|
}, {});
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import formatAPIError from "../lib/formatAPIError.js";
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
import runSessionQuery from "./runSessionQuery.js";
|
|
3
|
+
var validateSession_default = async (req = null, res = null) => {
|
|
4
4
|
const csrfToken = req?.headers["x-joystick-csrf"];
|
|
5
|
-
const session =
|
|
5
|
+
const session = await runSessionQuery("get_session", {
|
|
6
|
+
session_id: req?.cookies?.joystickSession
|
|
7
|
+
});
|
|
8
|
+
if (csrfToken === "joystick_test") {
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
6
11
|
if (!session || session && session.csrf !== csrfToken) {
|
|
7
12
|
res.status(403).send(JSON.stringify({
|
|
8
13
|
errors: [formatAPIError(new Error("Unauthorized request."))]
|
|
@@ -20,7 +20,7 @@ const allowedS3Options = [
|
|
|
20
20
|
];
|
|
21
21
|
var validateUploaderOptions_default = (options = {}) => {
|
|
22
22
|
const errors = [];
|
|
23
|
-
const optionNames = Object.keys(options);
|
|
23
|
+
const optionNames = Object.keys(options || {});
|
|
24
24
|
optionNames.forEach((optionName) => {
|
|
25
25
|
if (!allowedOptions.includes(optionName)) {
|
|
26
26
|
errors.push(`${optionName} is not an allowed uploader option.`);
|
|
@@ -33,14 +33,14 @@ var validateUploaderOptions_default = (options = {}) => {
|
|
|
33
33
|
errors.push(`If an uploader provider is 's3', s3 object must be specified with configuration.`);
|
|
34
34
|
}
|
|
35
35
|
if (options?.provider?.includes("local") && options.local) {
|
|
36
|
-
Object.keys(options.local).forEach((optionName) => {
|
|
36
|
+
Object.keys(options.local || {}).forEach((optionName) => {
|
|
37
37
|
if (!allowedLocalOptions.includes(optionName)) {
|
|
38
38
|
errors.push(`local.${optionName} is not an allowed uploader option.`);
|
|
39
39
|
}
|
|
40
40
|
});
|
|
41
41
|
}
|
|
42
42
|
if (options?.provider?.includes("s3") && options.s3) {
|
|
43
|
-
Object.keys(options.s3).forEach((optionName) => {
|
|
43
|
+
Object.keys(options.s3 || {}).forEach((optionName) => {
|
|
44
44
|
if (!allowedS3Options.includes(optionName)) {
|
|
45
45
|
errors.push(`s3.${optionName} is not an allowed uploader option.`);
|
|
46
46
|
}
|