@raketa-cloud/admin 0.0.93 → 0.0.94
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/jest.config.js +2 -2
- package/package.json +2 -2
- package/src/handlers/RedirectsHandler.js +1 -1
- package/src/handlers/TagsHandler.js +1 -1
- package/src/handlers/categories/index.js +1 -1
- package/src/helpers/HTMLBuilder.js +1 -1
- package/src/setupAuthentication.js +5 -2
- package/src/simpleCipher.js +23 -13
- package/src/tests/Logger.test.js +33 -0
- package/src/tests/handlers/RedirectsHandler.test/destroy.test.js +1 -1
- package/src/tests/handlers/TagsHandler.test/destroy.test.js +1 -1
- package/src/tests/handlers/categories/destroy.test.js +16 -12
- package/src/tests/helpers/HTMLBuilder.test.js +44 -0
package/jest.config.js
CHANGED
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@raketa-cloud/admin",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.94",
|
|
4
4
|
"main": "src/index.js",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "npx tailwindcss -i ./src/styles/application.css -o ../../apps/admin/static/application.css --watch",
|
|
7
|
-
"test": "jest --coverage --coverageReporters=
|
|
7
|
+
"test": "jest --coverage --coverageReporters=html --coverageReporters=\"text-summary\""
|
|
8
8
|
},
|
|
9
9
|
"devDependencies": {
|
|
10
10
|
"@babel/core": "7.24.7",
|
|
@@ -31,7 +31,7 @@ export default class HTMLBuilder {
|
|
|
31
31
|
}, "");
|
|
32
32
|
|
|
33
33
|
if (children || nonVoid)
|
|
34
|
-
return `<${tag} ${htmlAttributes}>${children}</${tag}>`;
|
|
34
|
+
return `<${tag} ${htmlAttributes}>${children ? children : ""}</${tag}>`;
|
|
35
35
|
|
|
36
36
|
return `<${tag} ${htmlAttributes} />`;
|
|
37
37
|
}
|
|
@@ -5,13 +5,16 @@ import currentPath from "./currentPath.cjs";
|
|
|
5
5
|
import AuthHelper from "./helpers/AuthHelper";
|
|
6
6
|
// TODO: Maybe change the encryption mechanism?
|
|
7
7
|
import simpleCipher from "./simpleCipher";
|
|
8
|
+
import getConfig from "../../../apps/admin/src/getConfig";
|
|
8
9
|
|
|
9
10
|
// Generate a random signing secret on every server restart
|
|
10
|
-
const SIGNING_SECRET = randomBytes(64).toString("hex");
|
|
11
|
+
// const SIGNING_SECRET = randomBytes(64).toString("hex");
|
|
11
12
|
const DELIMITER = ",";
|
|
12
13
|
|
|
13
14
|
function calculateHash(text) {
|
|
14
|
-
return createHmac("sha256",
|
|
15
|
+
return createHmac("sha256", getConfig().secrets.signing_secret)
|
|
16
|
+
.update(text)
|
|
17
|
+
.digest("hex");
|
|
15
18
|
}
|
|
16
19
|
|
|
17
20
|
function obfuscateToken(token) {
|
package/src/simpleCipher.js
CHANGED
|
@@ -1,14 +1,8 @@
|
|
|
1
|
+
import getConfig from "../../../apps/admin/src/getConfig";
|
|
2
|
+
|
|
1
3
|
const ALPHABET =
|
|
2
4
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789,.";
|
|
3
5
|
|
|
4
|
-
function generateKey() {
|
|
5
|
-
const shuffled = ALPHABET.split("")
|
|
6
|
-
.sort(() => Math.random() - 0.5)
|
|
7
|
-
.join("");
|
|
8
|
-
|
|
9
|
-
return shuffled;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
6
|
function generateEncryptionLookup(key) {
|
|
13
7
|
const lookup = {};
|
|
14
8
|
|
|
@@ -29,22 +23,38 @@ function generateDecryptionLookup(key) {
|
|
|
29
23
|
return lookup;
|
|
30
24
|
}
|
|
31
25
|
|
|
26
|
+
// function generateKey() {
|
|
27
|
+
// const shuffled = ALPHABET.split("")
|
|
28
|
+
// .sort(() => Math.random() - 0.5)
|
|
29
|
+
// .join("");
|
|
30
|
+
|
|
31
|
+
// return shuffled;
|
|
32
|
+
// }
|
|
33
|
+
|
|
32
34
|
// Scramble lookup maps on every server restart
|
|
33
|
-
const CIPHER_KEY = generateKey();
|
|
34
|
-
const ENCRYPT_LOOKUP = generateEncryptionLookup(CIPHER_KEY);
|
|
35
|
-
const DECRYPT_LOOKUP = generateDecryptionLookup(CIPHER_KEY);
|
|
35
|
+
// const CIPHER_KEY = generateKey();
|
|
36
|
+
// const ENCRYPT_LOOKUP = generateEncryptionLookup(CIPHER_KEY);
|
|
37
|
+
// const DECRYPT_LOOKUP = generateDecryptionLookup(CIPHER_KEY);
|
|
36
38
|
|
|
37
39
|
function encrypt(plaintext) {
|
|
40
|
+
const ecryptLookup = generateEncryptionLookup(
|
|
41
|
+
getConfig().secrets.crypto_secret
|
|
42
|
+
);
|
|
43
|
+
|
|
38
44
|
return plaintext
|
|
39
45
|
.split("")
|
|
40
|
-
.map((char) =>
|
|
46
|
+
.map((char) => ecryptLookup[char] || char)
|
|
41
47
|
.join("");
|
|
42
48
|
}
|
|
43
49
|
|
|
44
50
|
function decrypt(ciphertext) {
|
|
51
|
+
const decryptLookup = generateDecryptionLookup(
|
|
52
|
+
getConfig().secrets.crypto_secret
|
|
53
|
+
);
|
|
54
|
+
|
|
45
55
|
return ciphertext
|
|
46
56
|
.split("")
|
|
47
|
-
.map((char) =>
|
|
57
|
+
.map((char) => decryptLookup[char] || char)
|
|
48
58
|
.join("");
|
|
49
59
|
}
|
|
50
60
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import Logger from "../Logger";
|
|
2
|
+
|
|
3
|
+
describe("Logger", () => {
|
|
4
|
+
describe("error", () => {
|
|
5
|
+
test("Logs an error to the console", () => {
|
|
6
|
+
const logSpy = jest.spyOn(global.console, "error");
|
|
7
|
+
|
|
8
|
+
Logger.error({ foo: "BAR" });
|
|
9
|
+
|
|
10
|
+
expect(logSpy).toHaveBeenCalledWith("[ERROR]", "{ foo: 'BAR' }");
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
describe("info", () => {
|
|
15
|
+
test("Logs info to the console", () => {
|
|
16
|
+
const logSpy = jest.spyOn(global.console, "info");
|
|
17
|
+
|
|
18
|
+
Logger.info({ foo: "BAR" });
|
|
19
|
+
|
|
20
|
+
expect(logSpy).toHaveBeenCalledWith("[INFO]", "{ foo: 'BAR' }");
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe("debug", () => {
|
|
25
|
+
test("Logs debug information to the console", () => {
|
|
26
|
+
const logSpy = jest.spyOn(global.console, "debug");
|
|
27
|
+
|
|
28
|
+
Logger.debug({ foo: "BAR" });
|
|
29
|
+
|
|
30
|
+
expect(logSpy).toHaveBeenCalledWith("[DEBUG]", "{ foo: 'BAR' }");
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
});
|
|
@@ -1,34 +1,38 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import CategoriesHandler from "../../../handlers/categories";
|
|
2
|
+
import Categories from "../../../cloud/Categories";
|
|
3
3
|
|
|
4
4
|
import Factory from "../../support/factory";
|
|
5
5
|
|
|
6
6
|
function buildCtx() {
|
|
7
7
|
return {
|
|
8
8
|
client: {
|
|
9
|
-
|
|
9
|
+
categories: new Categories(() => ({}), "token"),
|
|
10
10
|
},
|
|
11
11
|
params: { id: 1 },
|
|
12
12
|
locale: "en",
|
|
13
|
+
resourceConfig: {
|
|
14
|
+
categoryType: "cat",
|
|
15
|
+
categoryTypePlural: "cats",
|
|
16
|
+
},
|
|
13
17
|
};
|
|
14
18
|
}
|
|
15
19
|
|
|
16
|
-
describe("
|
|
20
|
+
describe("Categories", () => {
|
|
17
21
|
test("destroy successful", async () => {
|
|
18
22
|
const redirect = Factory.build("redirect");
|
|
19
23
|
const ctx = buildCtx();
|
|
20
24
|
|
|
21
25
|
const mockDelete = jest
|
|
22
|
-
.spyOn(ctx.client.
|
|
26
|
+
.spyOn(ctx.client.categories, "delete")
|
|
23
27
|
.mockImplementation(() => ({ redirect: redirect, errors: null }));
|
|
24
28
|
|
|
25
|
-
const result = await
|
|
29
|
+
const result = await CategoriesHandler.destroy(ctx);
|
|
26
30
|
|
|
27
|
-
expect(mockDelete).toHaveBeenCalledWith(1);
|
|
31
|
+
expect(mockDelete).toHaveBeenCalledWith(1, "en");
|
|
28
32
|
|
|
29
33
|
expect(result).toEqual({
|
|
30
34
|
headers: {
|
|
31
|
-
"HX-Redirect": "/en/
|
|
35
|
+
"HX-Redirect": "/en/cats",
|
|
32
36
|
},
|
|
33
37
|
json: {},
|
|
34
38
|
});
|
|
@@ -38,16 +42,16 @@ describe("Redirects", () => {
|
|
|
38
42
|
const ctx = buildCtx();
|
|
39
43
|
|
|
40
44
|
const mockDelete = jest
|
|
41
|
-
.spyOn(ctx.client.
|
|
45
|
+
.spyOn(ctx.client.categories, "delete")
|
|
42
46
|
.mockImplementation(() => ({
|
|
43
47
|
redirect: null,
|
|
44
48
|
errors: [{ key: "generalError", message: "Not found" }],
|
|
45
49
|
}));
|
|
46
50
|
|
|
47
|
-
await expect(() =>
|
|
48
|
-
"Failed to delete
|
|
51
|
+
await expect(() => CategoriesHandler.destroy(ctx)).rejects.toEqual(
|
|
52
|
+
"Failed to delete category #1."
|
|
49
53
|
);
|
|
50
54
|
|
|
51
|
-
expect(mockDelete).toHaveBeenCalledWith(1);
|
|
55
|
+
expect(mockDelete).toHaveBeenCalledWith(1, "en");
|
|
52
56
|
});
|
|
53
57
|
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import HTMLBuilder from "../../helpers/HTMLBuilder";
|
|
2
|
+
|
|
3
|
+
describe("HTMLBuilder", () => {
|
|
4
|
+
function normalizeHTML(html) {
|
|
5
|
+
return html.replace(/>\s+</g, "><").replace(/\s+/g, " ").trim();
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
describe("tag", () => {
|
|
9
|
+
test("renders a self closing tag", () => {
|
|
10
|
+
const builder = new HTMLBuilder();
|
|
11
|
+
|
|
12
|
+
const result = builder.tag("img", null, { src: "/some-image.jpg" });
|
|
13
|
+
|
|
14
|
+
expect(normalizeHTML(result)).toEqual(
|
|
15
|
+
normalizeHTML('<img src="/some-image.jpg" />')
|
|
16
|
+
);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test("renders a label tag", () => {
|
|
20
|
+
const builder = new HTMLBuilder();
|
|
21
|
+
|
|
22
|
+
const result = builder.tag("label", "Heelo", { for: "some-input-id" });
|
|
23
|
+
|
|
24
|
+
expect(normalizeHTML(result)).toEqual(
|
|
25
|
+
normalizeHTML('<label for="some-input-id">Heelo</label>')
|
|
26
|
+
);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test("renders a textarea tag", () => {
|
|
30
|
+
const builder = new HTMLBuilder();
|
|
31
|
+
|
|
32
|
+
const result = builder.tag(
|
|
33
|
+
"textarea",
|
|
34
|
+
undefined,
|
|
35
|
+
{ id: "some-input-id", value: "Text?" },
|
|
36
|
+
{ nonVoid: true }
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
expect(normalizeHTML(result)).toEqual(
|
|
40
|
+
normalizeHTML('<textarea id="some-input-id" value="Text?"></textarea>')
|
|
41
|
+
);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
});
|