0z2i6v3u5t 1.0.0

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.
Files changed (211) hide show
  1. package/.devcontainer/devcontainer.json +4 -0
  2. package/.devcontainer/setup.sh +11 -0
  3. package/.dockerignore +2 -0
  4. package/.github/CONTRIBUTING.md +52 -0
  5. package/.github/FUNDING.yml +3 -0
  6. package/.github/ISSUE_TEMPLATE/bug_report.yml +59 -0
  7. package/.github/ISSUE_TEMPLATE/config.yml +5 -0
  8. package/.github/ISSUE_TEMPLATE/feature_request.yml +43 -0
  9. package/.github/dependabot.yml +17 -0
  10. package/.github/workflows/codeql.yml +76 -0
  11. package/.github/workflows/publish_docs.yml +25 -0
  12. package/.github/workflows/test.yml +78 -0
  13. package/.nvmrc +1 -0
  14. package/.prettierignore +1 -0
  15. package/.prettierrc +1 -0
  16. package/.vscode/launch.json +42 -0
  17. package/CODE_OF_CONDUCT.md +76 -0
  18. package/Dockerfile +17 -0
  19. package/LICENSE +21 -0
  20. package/README.md +3 -0
  21. package/SECURITY.md +5 -0
  22. package/__tests__/actions/cacheTest.ts +58 -0
  23. package/__tests__/actions/randomNumber.ts +26 -0
  24. package/__tests__/actions/recursiveAction.ts +16 -0
  25. package/__tests__/actions/sleepTest.ts +24 -0
  26. package/__tests__/actions/status.ts +17 -0
  27. package/__tests__/actions/swagger.ts +76 -0
  28. package/__tests__/actions/validationTest.ts +63 -0
  29. package/__tests__/cli/cli.ts +126 -0
  30. package/__tests__/core/api.ts +632 -0
  31. package/__tests__/core/cache.ts +400 -0
  32. package/__tests__/core/chatRoom.ts +589 -0
  33. package/__tests__/core/cli.ts +349 -0
  34. package/__tests__/core/cluster.ts +132 -0
  35. package/__tests__/core/config.ts +78 -0
  36. package/__tests__/core/errors.ts +112 -0
  37. package/__tests__/core/log.ts +23 -0
  38. package/__tests__/core/middleware.ts +427 -0
  39. package/__tests__/core/plugins/partialPlugin.ts +94 -0
  40. package/__tests__/core/plugins/withPlugin.ts +88 -0
  41. package/__tests__/core/plugins/withoutPlugin.ts +81 -0
  42. package/__tests__/core/process.ts +42 -0
  43. package/__tests__/core/specHelper.ts +330 -0
  44. package/__tests__/core/staticFile/compression.ts +99 -0
  45. package/__tests__/core/staticFile/staticFile.ts +180 -0
  46. package/__tests__/core/tasks/customQueueFunction.ts +67 -0
  47. package/__tests__/core/tasks/fullWorkerFlow.ts +199 -0
  48. package/__tests__/core/tasks/tasks.ts +605 -0
  49. package/__tests__/integration/browser.ts +133 -0
  50. package/__tests__/integration/ioredis-mock.ts +194 -0
  51. package/__tests__/integration/sendBuffer.ts +97 -0
  52. package/__tests__/integration/sendFile.ts +24 -0
  53. package/__tests__/integration/sharedFingerprint.ts +82 -0
  54. package/__tests__/integration/taskFlow.ts +110 -0
  55. package/__tests__/jest.ts +5 -0
  56. package/__tests__/modules/action.ts +103 -0
  57. package/__tests__/modules/config.ts +19 -0
  58. package/__tests__/modules/utils/ensureNoTsHeaderOrSpecFiles.ts +24 -0
  59. package/__tests__/servers/web/allowedRequestHosts.ts +88 -0
  60. package/__tests__/servers/web/enableMultiples.ts +83 -0
  61. package/__tests__/servers/web/fileUpload.ts +79 -0
  62. package/__tests__/servers/web/jsonp.ts +57 -0
  63. package/__tests__/servers/web/nonMultiples.ts +83 -0
  64. package/__tests__/servers/web/rawBody.ts +208 -0
  65. package/__tests__/servers/web/returnErrorCodes.ts +55 -0
  66. package/__tests__/servers/web/routes/deepRoutes.ts +96 -0
  67. package/__tests__/servers/web/routes/routes.ts +579 -0
  68. package/__tests__/servers/web/routes/veryDeepRoutes.ts +92 -0
  69. package/__tests__/servers/web/web.ts +1031 -0
  70. package/__tests__/servers/websocket.ts +795 -0
  71. package/__tests__/tasks/runAction.ts +37 -0
  72. package/__tests__/template.ts.example +20 -0
  73. package/__tests__/testCliCommands/hello.ts +44 -0
  74. package/__tests__/testPlugin/public/plugin.html +1 -0
  75. package/__tests__/testPlugin/src/actions/pluginAction.ts +14 -0
  76. package/__tests__/testPlugin/src/bin/hello.ts +22 -0
  77. package/__tests__/testPlugin/src/initializers/pluginInitializer.ts +17 -0
  78. package/__tests__/testPlugin/src/tasks/pluginTask.ts +15 -0
  79. package/__tests__/testPlugin/tsconfig.json +10 -0
  80. package/__tests__/utils/utils.ts +492 -0
  81. package/app.json +23 -0
  82. package/bin/deploy-docs +39 -0
  83. package/client/ActionheroWebsocketClient.js +277 -0
  84. package/docker-compose.yml +73 -0
  85. package/package.json +24 -0
  86. package/public/chat.html +194 -0
  87. package/public/css/cosmo.css +12 -0
  88. package/public/favicon.ico +0 -0
  89. package/public/index.html +115 -0
  90. package/public/javascript/.gitkeep +0 -0
  91. package/public/linkedSession.html +80 -0
  92. package/public/logo/actionhero-small.png +0 -0
  93. package/public/logo/actionhero.png +0 -0
  94. package/public/pixel.gif +0 -0
  95. package/public/simple.html +2 -0
  96. package/public/swagger.html +32 -0
  97. package/public/websocketLoadTest.html +322 -0
  98. package/src/actions/cacheTest.ts +58 -0
  99. package/src/actions/createChatRoom.ts +20 -0
  100. package/src/actions/randomNumber.ts +17 -0
  101. package/src/actions/recursiveAction.ts +13 -0
  102. package/src/actions/sendFile.ts +12 -0
  103. package/src/actions/sleepTest.ts +40 -0
  104. package/src/actions/status.ts +73 -0
  105. package/src/actions/swagger.ts +155 -0
  106. package/src/actions/validationTest.ts +36 -0
  107. package/src/bin/actionhero.ts +225 -0
  108. package/src/bin/methods/actions/list.ts +30 -0
  109. package/src/bin/methods/console.ts +26 -0
  110. package/src/bin/methods/generate/action.ts +58 -0
  111. package/src/bin/methods/generate/cli.ts +51 -0
  112. package/src/bin/methods/generate/initializer.ts +54 -0
  113. package/src/bin/methods/generate/plugin.ts +57 -0
  114. package/src/bin/methods/generate/server.ts +38 -0
  115. package/src/bin/methods/generate/task.ts +68 -0
  116. package/src/bin/methods/generate.ts +176 -0
  117. package/src/bin/methods/task/enqueue.ts +35 -0
  118. package/src/classes/action.ts +98 -0
  119. package/src/classes/actionProcessor.ts +463 -0
  120. package/src/classes/api.ts +51 -0
  121. package/src/classes/cli.ts +67 -0
  122. package/src/classes/config.ts +15 -0
  123. package/src/classes/connection.ts +321 -0
  124. package/src/classes/exceptionReporter.ts +9 -0
  125. package/src/classes/initializer.ts +59 -0
  126. package/src/classes/initializers.ts +5 -0
  127. package/src/classes/input.ts +9 -0
  128. package/src/classes/inputs.ts +34 -0
  129. package/src/classes/process/actionheroVersion.ts +15 -0
  130. package/src/classes/process/env.ts +16 -0
  131. package/src/classes/process/id.ts +34 -0
  132. package/src/classes/process/pid.ts +32 -0
  133. package/src/classes/process/projectRoot.ts +16 -0
  134. package/src/classes/process/typescript.ts +47 -0
  135. package/src/classes/process.ts +479 -0
  136. package/src/classes/server.ts +251 -0
  137. package/src/classes/task.ts +87 -0
  138. package/src/config/api.ts +107 -0
  139. package/src/config/errors.ts +162 -0
  140. package/src/config/logger.ts +113 -0
  141. package/src/config/plugins.ts +37 -0
  142. package/src/config/redis.ts +78 -0
  143. package/src/config/routes.ts +44 -0
  144. package/src/config/tasks.ts +84 -0
  145. package/src/config/web.ts +136 -0
  146. package/src/config/websocket.ts +62 -0
  147. package/src/index.ts +46 -0
  148. package/src/initializers/actions.ts +125 -0
  149. package/src/initializers/chatRoom.ts +214 -0
  150. package/src/initializers/connections.ts +124 -0
  151. package/src/initializers/exceptions.ts +155 -0
  152. package/src/initializers/params.ts +52 -0
  153. package/src/initializers/redis.ts +191 -0
  154. package/src/initializers/resque.ts +248 -0
  155. package/src/initializers/routes.ts +229 -0
  156. package/src/initializers/servers.ts +134 -0
  157. package/src/initializers/specHelper.ts +195 -0
  158. package/src/initializers/staticFile.ts +253 -0
  159. package/src/initializers/tasks.ts +188 -0
  160. package/src/modules/action.ts +89 -0
  161. package/src/modules/cache.ts +326 -0
  162. package/src/modules/chatRoom.ts +321 -0
  163. package/src/modules/config.ts +246 -0
  164. package/src/modules/log.ts +62 -0
  165. package/src/modules/redis.ts +93 -0
  166. package/src/modules/route.ts +59 -0
  167. package/src/modules/specHelper.ts +182 -0
  168. package/src/modules/task.ts +527 -0
  169. package/src/modules/utils/argv.ts +3 -0
  170. package/src/modules/utils/arrayStartingMatch.ts +21 -0
  171. package/src/modules/utils/arrayUnique.ts +15 -0
  172. package/src/modules/utils/collapseObjectToArray.ts +33 -0
  173. package/src/modules/utils/deepCopy.ts +3 -0
  174. package/src/modules/utils/ensureNoTsHeaderOrSpecFiles.ts +19 -0
  175. package/src/modules/utils/eventLoopDelay.ts +34 -0
  176. package/src/modules/utils/fileUtils.ts +119 -0
  177. package/src/modules/utils/filterObjectForLogging.ts +51 -0
  178. package/src/modules/utils/filterResponseForLogging.ts +53 -0
  179. package/src/modules/utils/getExternalIPAddress.ts +17 -0
  180. package/src/modules/utils/hashMerge.ts +63 -0
  181. package/src/modules/utils/isPlainObject.ts +45 -0
  182. package/src/modules/utils/isRunning.ts +7 -0
  183. package/src/modules/utils/parseCookies.ts +20 -0
  184. package/src/modules/utils/parseHeadersForClientAddress.ts +53 -0
  185. package/src/modules/utils/parseIPv6URI.ts +24 -0
  186. package/src/modules/utils/replaceDistWithSrc.ts +9 -0
  187. package/src/modules/utils/safeGlob.ts +6 -0
  188. package/src/modules/utils/sleep.ts +8 -0
  189. package/src/modules/utils/sortGlobalMiddleware.ts +17 -0
  190. package/src/modules/utils/sourceRelativeLinkPath.ts +29 -0
  191. package/src/modules/utils.ts +66 -0
  192. package/src/server.ts +20 -0
  193. package/src/servers/web.ts +894 -0
  194. package/src/servers/websocket.ts +304 -0
  195. package/src/tasks/runAction.ts +29 -0
  196. package/tea.yaml +9 -0
  197. package/templates/README.md.template +17 -0
  198. package/templates/action.ts.template +15 -0
  199. package/templates/boot.js.template +9 -0
  200. package/templates/cli.ts.template +15 -0
  201. package/templates/gitignore.template +23 -0
  202. package/templates/initializer.ts.template +17 -0
  203. package/templates/package-plugin.json.template +12 -0
  204. package/templates/package.json.template +45 -0
  205. package/templates/projectMap.txt +39 -0
  206. package/templates/projectServer.ts.template +20 -0
  207. package/templates/server.ts.template +37 -0
  208. package/templates/task.ts.template +16 -0
  209. package/templates/test/action.ts.template +13 -0
  210. package/templates/test/task.ts.template +20 -0
  211. package/tsconfig.json +11 -0
@@ -0,0 +1,103 @@
1
+ import {
2
+ action,
3
+ Process,
4
+ api,
5
+ Connection,
6
+ ActionProcessor,
7
+ } from "./../../src/index";
8
+
9
+ const actionhero = new Process();
10
+
11
+ describe("Modules", () => {
12
+ describe("action", () => {
13
+ beforeAll(async () => {
14
+ await actionhero.start();
15
+ });
16
+
17
+ afterAll(async () => {
18
+ await actionhero.stop();
19
+ });
20
+
21
+ test("actions can be called in-line", async () => {
22
+ const response = await action.run("randomNumber");
23
+ expect(response.randomNumber).toBeGreaterThanOrEqual(0);
24
+ expect(response.stringRandomNumber).toMatch(/Your random number is/);
25
+ });
26
+
27
+ test("in-line actions can accept params", async () => {
28
+ // @ts-ignore
29
+ const response = await action.run("cacheTest", null, {
30
+ key: "testKey",
31
+ value: "testValue",
32
+ });
33
+ expect(response.cacheTestResults.saveResp).toBe(true);
34
+ expect(response.cacheTestResults.deleteResp).toBe(true);
35
+ });
36
+
37
+ test("in-line actions will fail when required params are missing", async () => {
38
+ await expect(action.run("cacheTest")).rejects.toThrow(
39
+ /key is a required parameter for this action/,
40
+ );
41
+ });
42
+
43
+ describe("middleware", () => {
44
+ afterEach(() => {
45
+ api.actions.middleware = {};
46
+ api.actions.globalMiddleware = [];
47
+ });
48
+
49
+ test("in-line actions run middleware", async () => {
50
+ action.addMiddleware({
51
+ name: "test middleware",
52
+ global: true,
53
+ preProcessor: (data: ActionProcessor<any>) => {
54
+ data.response._preProcessorNote = "note";
55
+ },
56
+ });
57
+
58
+ const response = await action.run("randomNumber");
59
+ expect(response.randomNumber).toBeGreaterThanOrEqual(0);
60
+ expect(response._preProcessorNote).toBe("note");
61
+ });
62
+
63
+ test("throwing in middleware halts in-line actions", async () => {
64
+ action.addMiddleware({
65
+ name: "test middleware",
66
+ global: true,
67
+ preProcessor: () => {
68
+ throw new Error("nope");
69
+ },
70
+ });
71
+
72
+ await expect(action.run("randomNumber")).rejects.toThrow(/nope/);
73
+ });
74
+ });
75
+
76
+ describe("connectionProperties", () => {
77
+ afterEach(() => {
78
+ api.actions.middleware = {};
79
+ api.actions.globalMiddleware = [];
80
+ });
81
+
82
+ test("connection properties can be assigned", async () => {
83
+ action.addMiddleware({
84
+ name: "test middleware",
85
+ global: true,
86
+ preProcessor: ({ connection }: { connection: Connection }) => {
87
+ if (!connection?.session?.userId) throw new Error("not logged in");
88
+ },
89
+ });
90
+
91
+ await expect(action.run("randomNumber")).rejects.toThrow(
92
+ /not logged in/,
93
+ );
94
+
95
+ // @ts-ignore
96
+ const response = await action.run("randomNumber", null, null, {
97
+ session: { userId: 123 },
98
+ });
99
+ expect(response.randomNumber).toBeGreaterThanOrEqual(0);
100
+ });
101
+ });
102
+ });
103
+ });
@@ -0,0 +1,19 @@
1
+ import { config } from "../../src/index";
2
+ import { rebuildConfig } from "../../src/modules/config";
3
+
4
+ describe("config class", () => {
5
+ it("loads config", () => {
6
+ // statically defined
7
+ expect(config.general!.cachePrefix).toBe("actionhero:cache:");
8
+ // generated from another config file
9
+ expect(config.web!.httpHeaders!["X-Powered-By"]).toBe("actionhero");
10
+ });
11
+
12
+ it("updates config on 'restart'", () => {
13
+ const port = "2020";
14
+ process.env.PORT = port;
15
+ expect(config.web!.port).not.toBe(port);
16
+ rebuildConfig();
17
+ expect(config.web!.port).toBe(port);
18
+ });
19
+ });
@@ -0,0 +1,24 @@
1
+ import { ensureNoTsHeaderOrSpecFiles } from "../../../src/modules/utils/ensureNoTsHeaderOrSpecFiles";
2
+
3
+ describe("ensureNoTsHeaderOrSpecFiles", () => {
4
+ it("filters header files", () => {
5
+ const filtered = ensureNoTsHeaderOrSpecFiles([
6
+ "afdasf.d.ts",
7
+ "some-file.ts",
8
+ ]);
9
+ expect(filtered).toEqual(["some-file.ts"]);
10
+ });
11
+
12
+ it("filters test/spec files", () => {
13
+ const filtered = ensureNoTsHeaderOrSpecFiles([
14
+ "afdasf.spec.ts",
15
+ "afdasfsdfa.spec.js",
16
+ "spec.js",
17
+ "test-my-api.js",
18
+ "afdasfsdfa.test.js",
19
+ "afdasfsdfa.test.ts",
20
+ "some-file.ts",
21
+ ]);
22
+ expect(filtered).toEqual(["spec.js", "test-my-api.js", "some-file.ts"]);
23
+ });
24
+ });
@@ -0,0 +1,88 @@
1
+ import axios, { AxiosError } from "axios";
2
+ import { Process, config } from "./../../../src/index";
3
+
4
+ const actionhero = new Process();
5
+ let url: string;
6
+
7
+ jest.mock("./../../../src/config/web.ts", () => ({
8
+ __esModule: true,
9
+ test: {
10
+ web: () => {
11
+ return {
12
+ enabled: true,
13
+ secure: false,
14
+ automaticRoutes: ["get"],
15
+ allowedRequestHosts: ["https://www.site.com"],
16
+ urlPathForActions: "api",
17
+ urlPathForFiles: "public",
18
+ rootEndpointType: "file",
19
+ port: 18080 + parseInt(process.env.JEST_WORKER_ID || "0"),
20
+ matchExtensionMime: true,
21
+ metadataOptions: {
22
+ serverInformation: true,
23
+ requesterInformation: false,
24
+ },
25
+ fingerprintOptions: {
26
+ cookieKey: "sessionID",
27
+ },
28
+ };
29
+ },
30
+ },
31
+ }));
32
+
33
+ describe("Server: Web", () => {
34
+ beforeAll(async () => {
35
+ await actionhero.start();
36
+ url = "http://localhost:" + config.web!.port;
37
+ });
38
+
39
+ afterAll(async () => await actionhero.stop());
40
+
41
+ describe("request redirection (allowedRequestHosts)", () => {
42
+ test("will redirect clients if they do not request the proper host", async () => {
43
+ try {
44
+ await axios.get(url + "/api/randomNumber", {
45
+ maxRedirects: 0,
46
+ headers: { Host: "lalala.site.com" },
47
+ });
48
+ throw new Error("should not get here");
49
+ } catch (error) {
50
+ if (error instanceof AxiosError) {
51
+ expect(error.response?.status).toEqual(302);
52
+ expect(error.response?.data).toMatch(
53
+ /You are being redirected to https:\/\/www.site.com\/api\/randomNumber/,
54
+ );
55
+ } else throw error;
56
+ }
57
+ });
58
+
59
+ test("will redirect clients if they do not request the proper protocol", async () => {
60
+ try {
61
+ await axios.get(url + "/api/randomNumber", {
62
+ maxRedirects: 0,
63
+ headers: { Host: "www.site.com" },
64
+ });
65
+ throw new Error("should not get here");
66
+ } catch (error) {
67
+ if (error instanceof AxiosError) {
68
+ expect(error.response?.status).toEqual(302);
69
+ expect(error.response?.data).toMatch(
70
+ /You are being redirected to https:\/\/www.site.com\/api\/randomNumber/,
71
+ );
72
+ } else throw error;
73
+ }
74
+ });
75
+
76
+ test("will allow API access from the proper hosts", async () => {
77
+ const response = await axios.get(url + "/api/randomNumber", {
78
+ maxRedirects: 0,
79
+ headers: {
80
+ Host: "www.site.com",
81
+ "x-forwarded-proto": "https",
82
+ },
83
+ });
84
+
85
+ expect(response.data["randomNumber"]).not.toBeUndefined();
86
+ });
87
+ });
88
+ });
@@ -0,0 +1,83 @@
1
+ import { PassThrough } from "stream";
2
+ import axios, { AxiosError } from "axios";
3
+ import { api, Process, config } from "./../../../src/index";
4
+ import * as FormData from "form-data";
5
+
6
+ const actionhero = new Process();
7
+ let url: string;
8
+
9
+ jest.mock("./../../../src/config/web.ts", () => ({
10
+ __esModule: true,
11
+ test: {
12
+ web: () => {
13
+ return {
14
+ enabled: true,
15
+ saveRawBody: true,
16
+ automaticRoutes: ["post"],
17
+ secure: false,
18
+ urlPathForActions: "api",
19
+ urlPathForFiles: "public",
20
+ rootEndpointType: "file",
21
+ port: 18080 + parseInt(process.env.JEST_WORKER_ID || "0"),
22
+ matchExtensionMime: true,
23
+ metadataOptions: {
24
+ serverInformation: true,
25
+ requesterInformation: false,
26
+ },
27
+ fingerprintOptions: {
28
+ cookieKey: "sessionID",
29
+ },
30
+ formOptions: { multiples: true },
31
+ };
32
+ },
33
+ },
34
+ }));
35
+
36
+ describe("Server: Web", () => {
37
+ beforeAll(async () => {
38
+ await actionhero.start();
39
+ url = "http://localhost:" + config.web!.port;
40
+ });
41
+
42
+ afterAll(async () => await actionhero.stop());
43
+
44
+ describe("connection.rawConnection.rawBody", () => {
45
+ beforeAll(() => {
46
+ api.actions.versions.paramTestAction = [1];
47
+ api.actions.actions.paramTestAction = {
48
+ // @ts-ignore
49
+ 1: {
50
+ name: "paramTestAction",
51
+ description: "I return connection.rawConnection.params",
52
+ version: 1,
53
+ inputs: { key: { required: true } },
54
+ run: async (data) => {
55
+ data.response!.data = data.params;
56
+ },
57
+ },
58
+ };
59
+
60
+ api.routes.loadRoutes();
61
+ });
62
+
63
+ afterAll(() => {
64
+ delete api.actions.actions.paramTestAction;
65
+ delete api.actions.versions.paramTestAction;
66
+ });
67
+
68
+ test(".rawBody will contain the raw POST body without parsing", async () => {
69
+ var bodyFormData = new FormData();
70
+ bodyFormData.append("key", "value");
71
+ bodyFormData.append("key", "value1");
72
+
73
+ const response = await axios.post(
74
+ url + "/api/paramTestAction",
75
+ bodyFormData,
76
+ {
77
+ headers: { "Content-type": "multipart/form-data" },
78
+ },
79
+ );
80
+ expect(response.data.data.key).toEqual(["value", "value1"]);
81
+ });
82
+ });
83
+ });
@@ -0,0 +1,79 @@
1
+ process.env.AUTOMATIC_ROUTES = "head,get,post,put,delete";
2
+ // process.env.LOG_LEVEL = "info";
3
+
4
+ import axios, { AxiosError } from "axios";
5
+ import * as FormData from "form-data";
6
+ import * as fs from "fs";
7
+ import * as path from "path";
8
+ import { api, Process, config } from "./../../../src/index";
9
+ import { sleep } from "../../../src/modules/utils/sleep";
10
+
11
+ describe("Server: Web", () => {
12
+ const actionhero = new Process();
13
+ let url: string;
14
+
15
+ beforeAll(async () => {
16
+ await actionhero.start();
17
+ url = "http://localhost:" + config.web!.port;
18
+ });
19
+
20
+ afterAll(async () => await actionhero.stop());
21
+
22
+ beforeAll(() => {
23
+ config.web!.returnErrorCodes = true;
24
+ api.actions.versions.uploadAction = [1];
25
+ api.actions.actions.uploadAction = {
26
+ // @ts-ignore
27
+ 1: {
28
+ name: "uploadAction",
29
+ description: "I am a test",
30
+ version: 1,
31
+ inputs: {
32
+ file: { required: true },
33
+ stringParam: { required: true },
34
+ },
35
+ outputExample: {},
36
+ run: async (data) => {
37
+ await sleep(1000 * 1);
38
+ return { params: data.params };
39
+ },
40
+ },
41
+ };
42
+
43
+ api.routes.loadRoutes();
44
+ });
45
+
46
+ afterAll(() => {
47
+ delete api.actions.actions.uploadAction;
48
+ delete api.actions.versions.uploadAction;
49
+ });
50
+
51
+ test("files can be uploaded", async () => {
52
+ const form = new FormData();
53
+ form.append("stringParam", "hello world");
54
+ form.append(
55
+ "file",
56
+ fs.createReadStream(
57
+ path.join(
58
+ __dirname,
59
+ "..",
60
+ "..",
61
+ "..",
62
+ "public",
63
+ "logo",
64
+ "actionhero.png",
65
+ ),
66
+ ),
67
+ );
68
+
69
+ const response = await axios.post(`${url}/api/uploadAction`, form);
70
+ expect(response.data.params.stringParam).toEqual("hello world");
71
+ expect(response.data.params.file).toEqual(
72
+ expect.objectContaining({
73
+ originalFilename: "actionhero.png",
74
+ mimetype: "image/png",
75
+ size: 59273,
76
+ }),
77
+ );
78
+ });
79
+ });
@@ -0,0 +1,57 @@
1
+ import axios, { AxiosError } from "axios";
2
+ import { Process, config } from "./../../../src/index";
3
+
4
+ const actionhero = new Process();
5
+ let url: string;
6
+
7
+ jest.mock("./../../../src/config/web.ts", () => ({
8
+ __esModule: true,
9
+ test: {
10
+ web: () => {
11
+ return {
12
+ enabled: true,
13
+ automaticRoutes: ["get", "post"],
14
+ secure: false,
15
+ urlPathForActions: "api",
16
+ urlPathForFiles: "public",
17
+ rootEndpointType: "file",
18
+ port: 18080 + parseInt(process.env.JEST_WORKER_ID || "0"),
19
+ matchExtensionMime: true,
20
+ metadataOptions: {
21
+ serverInformation: true,
22
+ requesterInformation: false,
23
+ },
24
+ fingerprintOptions: {
25
+ cookieKey: "sessionID",
26
+ },
27
+ };
28
+ },
29
+ },
30
+ }));
31
+
32
+ describe("Server: Web", () => {
33
+ beforeAll(async () => {
34
+ await actionhero.start();
35
+ url = "http://localhost:" + config.web!.port;
36
+ });
37
+
38
+ afterAll(async () => await actionhero.stop());
39
+
40
+ describe("JSONp", () => {
41
+ test("can ask for JSONp responses", async () => {
42
+ const response = await axios.get(
43
+ url + "/api/randomNumber?callback=myCallback",
44
+ );
45
+ expect(response.data).toContain("myCallback({");
46
+ expect(response.data).toContain("Your random number is");
47
+ });
48
+
49
+ test("JSONp responses cannot be used for XSS", async () => {
50
+ const response = await axios.get(
51
+ url + "/api/randomNumber?callback=alert(%27hi%27);foo",
52
+ );
53
+ expect(response.data).not.toMatch(/alert\(/);
54
+ expect(response.data.indexOf("alert&#39;hi&#39;;foo(")).toEqual(0);
55
+ });
56
+ });
57
+ });
@@ -0,0 +1,83 @@
1
+ import { PassThrough } from "stream";
2
+ import axios, { AxiosError } from "axios";
3
+ import { api, Process, config } from "./../../../src/index";
4
+ import * as FormData from "form-data";
5
+
6
+ const actionhero = new Process();
7
+ let url: string;
8
+
9
+ jest.mock("./../../../src/config/web.ts", () => ({
10
+ __esModule: true,
11
+ test: {
12
+ web: () => {
13
+ return {
14
+ enabled: true,
15
+ saveRawBody: true,
16
+ automaticRoutes: ["post"],
17
+ secure: false,
18
+ urlPathForActions: "api",
19
+ urlPathForFiles: "public",
20
+ rootEndpointType: "file",
21
+ port: 18080 + parseInt(process.env.JEST_WORKER_ID || "0"),
22
+ matchExtensionMime: true,
23
+ metadataOptions: {
24
+ serverInformation: true,
25
+ requesterInformation: false,
26
+ },
27
+ fingerprintOptions: {
28
+ cookieKey: "sessionID",
29
+ },
30
+ formOptions: { multiples: false },
31
+ };
32
+ },
33
+ },
34
+ }));
35
+
36
+ describe("Server: Web", () => {
37
+ beforeAll(async () => {
38
+ await actionhero.start();
39
+ url = "http://localhost:" + config.web!.port;
40
+ });
41
+
42
+ afterAll(async () => await actionhero.stop());
43
+
44
+ describe("connection.rawConnection.rawBody", () => {
45
+ beforeAll(() => {
46
+ api.actions.versions.paramTestAction = [1];
47
+ api.actions.actions.paramTestAction = {
48
+ // @ts-ignore
49
+ 1: {
50
+ name: "paramTestAction",
51
+ description: "I return connection.rawConnection.params",
52
+ version: 1,
53
+ inputs: { key: { required: true } },
54
+ run: async (data) => {
55
+ data.response!.data = data.params;
56
+ },
57
+ },
58
+ };
59
+
60
+ api.routes.loadRoutes();
61
+ });
62
+
63
+ afterAll(() => {
64
+ delete api.actions.actions.paramTestAction;
65
+ delete api.actions.versions.paramTestAction;
66
+ });
67
+
68
+ test(".rawBody will contain the raw POST body without parsing", async () => {
69
+ var bodyFormData = new FormData();
70
+ bodyFormData.append("key", "value");
71
+ bodyFormData.append("key", "value1");
72
+
73
+ const response = await axios.post(
74
+ url + "/api/paramTestAction",
75
+ bodyFormData,
76
+ {
77
+ headers: { "Content-type": "multipart/form-data" },
78
+ },
79
+ );
80
+ expect(response.data.data.key).toEqual("value1");
81
+ });
82
+ });
83
+ });