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.
- package/.devcontainer/devcontainer.json +4 -0
- package/.devcontainer/setup.sh +11 -0
- package/.dockerignore +2 -0
- package/.github/CONTRIBUTING.md +52 -0
- package/.github/FUNDING.yml +3 -0
- package/.github/ISSUE_TEMPLATE/bug_report.yml +59 -0
- package/.github/ISSUE_TEMPLATE/config.yml +5 -0
- package/.github/ISSUE_TEMPLATE/feature_request.yml +43 -0
- package/.github/dependabot.yml +17 -0
- package/.github/workflows/codeql.yml +76 -0
- package/.github/workflows/publish_docs.yml +25 -0
- package/.github/workflows/test.yml +78 -0
- package/.nvmrc +1 -0
- package/.prettierignore +1 -0
- package/.prettierrc +1 -0
- package/.vscode/launch.json +42 -0
- package/CODE_OF_CONDUCT.md +76 -0
- package/Dockerfile +17 -0
- package/LICENSE +21 -0
- package/README.md +3 -0
- package/SECURITY.md +5 -0
- package/__tests__/actions/cacheTest.ts +58 -0
- package/__tests__/actions/randomNumber.ts +26 -0
- package/__tests__/actions/recursiveAction.ts +16 -0
- package/__tests__/actions/sleepTest.ts +24 -0
- package/__tests__/actions/status.ts +17 -0
- package/__tests__/actions/swagger.ts +76 -0
- package/__tests__/actions/validationTest.ts +63 -0
- package/__tests__/cli/cli.ts +126 -0
- package/__tests__/core/api.ts +632 -0
- package/__tests__/core/cache.ts +400 -0
- package/__tests__/core/chatRoom.ts +589 -0
- package/__tests__/core/cli.ts +349 -0
- package/__tests__/core/cluster.ts +132 -0
- package/__tests__/core/config.ts +78 -0
- package/__tests__/core/errors.ts +112 -0
- package/__tests__/core/log.ts +23 -0
- package/__tests__/core/middleware.ts +427 -0
- package/__tests__/core/plugins/partialPlugin.ts +94 -0
- package/__tests__/core/plugins/withPlugin.ts +88 -0
- package/__tests__/core/plugins/withoutPlugin.ts +81 -0
- package/__tests__/core/process.ts +42 -0
- package/__tests__/core/specHelper.ts +330 -0
- package/__tests__/core/staticFile/compression.ts +99 -0
- package/__tests__/core/staticFile/staticFile.ts +180 -0
- package/__tests__/core/tasks/customQueueFunction.ts +67 -0
- package/__tests__/core/tasks/fullWorkerFlow.ts +199 -0
- package/__tests__/core/tasks/tasks.ts +605 -0
- package/__tests__/integration/browser.ts +133 -0
- package/__tests__/integration/ioredis-mock.ts +194 -0
- package/__tests__/integration/sendBuffer.ts +97 -0
- package/__tests__/integration/sendFile.ts +24 -0
- package/__tests__/integration/sharedFingerprint.ts +82 -0
- package/__tests__/integration/taskFlow.ts +110 -0
- package/__tests__/jest.ts +5 -0
- package/__tests__/modules/action.ts +103 -0
- package/__tests__/modules/config.ts +19 -0
- package/__tests__/modules/utils/ensureNoTsHeaderOrSpecFiles.ts +24 -0
- package/__tests__/servers/web/allowedRequestHosts.ts +88 -0
- package/__tests__/servers/web/enableMultiples.ts +83 -0
- package/__tests__/servers/web/fileUpload.ts +79 -0
- package/__tests__/servers/web/jsonp.ts +57 -0
- package/__tests__/servers/web/nonMultiples.ts +83 -0
- package/__tests__/servers/web/rawBody.ts +208 -0
- package/__tests__/servers/web/returnErrorCodes.ts +55 -0
- package/__tests__/servers/web/routes/deepRoutes.ts +96 -0
- package/__tests__/servers/web/routes/routes.ts +579 -0
- package/__tests__/servers/web/routes/veryDeepRoutes.ts +92 -0
- package/__tests__/servers/web/web.ts +1031 -0
- package/__tests__/servers/websocket.ts +795 -0
- package/__tests__/tasks/runAction.ts +37 -0
- package/__tests__/template.ts.example +20 -0
- package/__tests__/testCliCommands/hello.ts +44 -0
- package/__tests__/testPlugin/public/plugin.html +1 -0
- package/__tests__/testPlugin/src/actions/pluginAction.ts +14 -0
- package/__tests__/testPlugin/src/bin/hello.ts +22 -0
- package/__tests__/testPlugin/src/initializers/pluginInitializer.ts +17 -0
- package/__tests__/testPlugin/src/tasks/pluginTask.ts +15 -0
- package/__tests__/testPlugin/tsconfig.json +10 -0
- package/__tests__/utils/utils.ts +492 -0
- package/app.json +23 -0
- package/bin/deploy-docs +39 -0
- package/client/ActionheroWebsocketClient.js +277 -0
- package/docker-compose.yml +73 -0
- package/package.json +24 -0
- package/public/chat.html +194 -0
- package/public/css/cosmo.css +12 -0
- package/public/favicon.ico +0 -0
- package/public/index.html +115 -0
- package/public/javascript/.gitkeep +0 -0
- package/public/linkedSession.html +80 -0
- package/public/logo/actionhero-small.png +0 -0
- package/public/logo/actionhero.png +0 -0
- package/public/pixel.gif +0 -0
- package/public/simple.html +2 -0
- package/public/swagger.html +32 -0
- package/public/websocketLoadTest.html +322 -0
- package/src/actions/cacheTest.ts +58 -0
- package/src/actions/createChatRoom.ts +20 -0
- package/src/actions/randomNumber.ts +17 -0
- package/src/actions/recursiveAction.ts +13 -0
- package/src/actions/sendFile.ts +12 -0
- package/src/actions/sleepTest.ts +40 -0
- package/src/actions/status.ts +73 -0
- package/src/actions/swagger.ts +155 -0
- package/src/actions/validationTest.ts +36 -0
- package/src/bin/actionhero.ts +225 -0
- package/src/bin/methods/actions/list.ts +30 -0
- package/src/bin/methods/console.ts +26 -0
- package/src/bin/methods/generate/action.ts +58 -0
- package/src/bin/methods/generate/cli.ts +51 -0
- package/src/bin/methods/generate/initializer.ts +54 -0
- package/src/bin/methods/generate/plugin.ts +57 -0
- package/src/bin/methods/generate/server.ts +38 -0
- package/src/bin/methods/generate/task.ts +68 -0
- package/src/bin/methods/generate.ts +176 -0
- package/src/bin/methods/task/enqueue.ts +35 -0
- package/src/classes/action.ts +98 -0
- package/src/classes/actionProcessor.ts +463 -0
- package/src/classes/api.ts +51 -0
- package/src/classes/cli.ts +67 -0
- package/src/classes/config.ts +15 -0
- package/src/classes/connection.ts +321 -0
- package/src/classes/exceptionReporter.ts +9 -0
- package/src/classes/initializer.ts +59 -0
- package/src/classes/initializers.ts +5 -0
- package/src/classes/input.ts +9 -0
- package/src/classes/inputs.ts +34 -0
- package/src/classes/process/actionheroVersion.ts +15 -0
- package/src/classes/process/env.ts +16 -0
- package/src/classes/process/id.ts +34 -0
- package/src/classes/process/pid.ts +32 -0
- package/src/classes/process/projectRoot.ts +16 -0
- package/src/classes/process/typescript.ts +47 -0
- package/src/classes/process.ts +479 -0
- package/src/classes/server.ts +251 -0
- package/src/classes/task.ts +87 -0
- package/src/config/api.ts +107 -0
- package/src/config/errors.ts +162 -0
- package/src/config/logger.ts +113 -0
- package/src/config/plugins.ts +37 -0
- package/src/config/redis.ts +78 -0
- package/src/config/routes.ts +44 -0
- package/src/config/tasks.ts +84 -0
- package/src/config/web.ts +136 -0
- package/src/config/websocket.ts +62 -0
- package/src/index.ts +46 -0
- package/src/initializers/actions.ts +125 -0
- package/src/initializers/chatRoom.ts +214 -0
- package/src/initializers/connections.ts +124 -0
- package/src/initializers/exceptions.ts +155 -0
- package/src/initializers/params.ts +52 -0
- package/src/initializers/redis.ts +191 -0
- package/src/initializers/resque.ts +248 -0
- package/src/initializers/routes.ts +229 -0
- package/src/initializers/servers.ts +134 -0
- package/src/initializers/specHelper.ts +195 -0
- package/src/initializers/staticFile.ts +253 -0
- package/src/initializers/tasks.ts +188 -0
- package/src/modules/action.ts +89 -0
- package/src/modules/cache.ts +326 -0
- package/src/modules/chatRoom.ts +321 -0
- package/src/modules/config.ts +246 -0
- package/src/modules/log.ts +62 -0
- package/src/modules/redis.ts +93 -0
- package/src/modules/route.ts +59 -0
- package/src/modules/specHelper.ts +182 -0
- package/src/modules/task.ts +527 -0
- package/src/modules/utils/argv.ts +3 -0
- package/src/modules/utils/arrayStartingMatch.ts +21 -0
- package/src/modules/utils/arrayUnique.ts +15 -0
- package/src/modules/utils/collapseObjectToArray.ts +33 -0
- package/src/modules/utils/deepCopy.ts +3 -0
- package/src/modules/utils/ensureNoTsHeaderOrSpecFiles.ts +19 -0
- package/src/modules/utils/eventLoopDelay.ts +34 -0
- package/src/modules/utils/fileUtils.ts +119 -0
- package/src/modules/utils/filterObjectForLogging.ts +51 -0
- package/src/modules/utils/filterResponseForLogging.ts +53 -0
- package/src/modules/utils/getExternalIPAddress.ts +17 -0
- package/src/modules/utils/hashMerge.ts +63 -0
- package/src/modules/utils/isPlainObject.ts +45 -0
- package/src/modules/utils/isRunning.ts +7 -0
- package/src/modules/utils/parseCookies.ts +20 -0
- package/src/modules/utils/parseHeadersForClientAddress.ts +53 -0
- package/src/modules/utils/parseIPv6URI.ts +24 -0
- package/src/modules/utils/replaceDistWithSrc.ts +9 -0
- package/src/modules/utils/safeGlob.ts +6 -0
- package/src/modules/utils/sleep.ts +8 -0
- package/src/modules/utils/sortGlobalMiddleware.ts +17 -0
- package/src/modules/utils/sourceRelativeLinkPath.ts +29 -0
- package/src/modules/utils.ts +66 -0
- package/src/server.ts +20 -0
- package/src/servers/web.ts +894 -0
- package/src/servers/websocket.ts +304 -0
- package/src/tasks/runAction.ts +29 -0
- package/tea.yaml +9 -0
- package/templates/README.md.template +17 -0
- package/templates/action.ts.template +15 -0
- package/templates/boot.js.template +9 -0
- package/templates/cli.ts.template +15 -0
- package/templates/gitignore.template +23 -0
- package/templates/initializer.ts.template +17 -0
- package/templates/package-plugin.json.template +12 -0
- package/templates/package.json.template +45 -0
- package/templates/projectMap.txt +39 -0
- package/templates/projectServer.ts.template +20 -0
- package/templates/server.ts.template +37 -0
- package/templates/task.ts.template +16 -0
- package/templates/test/action.ts.template +13 -0
- package/templates/test/task.ts.template +20 -0
- package/tsconfig.json +11 -0
@@ -0,0 +1,479 @@
|
|
1
|
+
import * as path from "path";
|
2
|
+
import * as fs from "fs";
|
3
|
+
import { config } from "..";
|
4
|
+
import { log } from "../modules/log";
|
5
|
+
import { Initializer } from "./initializer";
|
6
|
+
import { Initializers } from "./initializers";
|
7
|
+
import { utils } from "../modules/utils";
|
8
|
+
import { id } from "./process/id";
|
9
|
+
import { env } from "./process/env";
|
10
|
+
import { writePidFile, clearPidFile } from "./process/pid";
|
11
|
+
import { api } from "../index";
|
12
|
+
import { rebuildConfig } from "../modules/config";
|
13
|
+
import { safeGlobSync } from "../modules/utils/safeGlob";
|
14
|
+
|
15
|
+
export const fatalErrorCode = "FATAL_ACTIONHERO_ERROR";
|
16
|
+
|
17
|
+
export class Process {
|
18
|
+
running: boolean;
|
19
|
+
initialized: boolean;
|
20
|
+
started: boolean;
|
21
|
+
stopped: boolean;
|
22
|
+
stopReasons?: string[];
|
23
|
+
shuttingDown: boolean;
|
24
|
+
bootTime: number;
|
25
|
+
initializers: Initializers;
|
26
|
+
startCount: number;
|
27
|
+
loadInitializers: Initializer["initialize"][];
|
28
|
+
startInitializers: Initializer["start"][];
|
29
|
+
stopInitializers: Initializer["stop"][];
|
30
|
+
|
31
|
+
constructor() {
|
32
|
+
this.initialized = false;
|
33
|
+
this.started = false;
|
34
|
+
this.stopped = false;
|
35
|
+
this.initializers = {};
|
36
|
+
this.loadInitializers = [];
|
37
|
+
this.startInitializers = [];
|
38
|
+
this.stopInitializers = [];
|
39
|
+
this.stopReasons = [];
|
40
|
+
this.startCount = 0;
|
41
|
+
|
42
|
+
api.process = this;
|
43
|
+
|
44
|
+
api.commands.initialize = this.initialize;
|
45
|
+
api.commands.start = this.start;
|
46
|
+
api.commands.stop = this.stop;
|
47
|
+
api.commands.restart = this.restart;
|
48
|
+
}
|
49
|
+
|
50
|
+
async initialize() {
|
51
|
+
const loadInitializerRankings: {
|
52
|
+
[rank: number]: Initializer["initialize"][];
|
53
|
+
} = {};
|
54
|
+
const startInitializerRankings: {
|
55
|
+
[rank: number]: Initializer["start"][];
|
56
|
+
} = {};
|
57
|
+
const stopInitializerRankings: {
|
58
|
+
[rank: number]: Initializer["stop"][];
|
59
|
+
} = {};
|
60
|
+
|
61
|
+
let initializerFiles: Array<string> = [];
|
62
|
+
|
63
|
+
// load initializers from core
|
64
|
+
initializerFiles = initializerFiles.concat(
|
65
|
+
safeGlobSync(
|
66
|
+
path.join(__dirname, "..", "initializers", "**", "**/*(*.js|*.ts)"),
|
67
|
+
),
|
68
|
+
);
|
69
|
+
|
70
|
+
// load initializers from project
|
71
|
+
config.general.paths.initializer.forEach((startPath: string) => {
|
72
|
+
initializerFiles = initializerFiles.concat(
|
73
|
+
safeGlobSync(path.join(startPath, "**", "**/*(*.js|*.ts)")),
|
74
|
+
);
|
75
|
+
});
|
76
|
+
|
77
|
+
// load initializers from plugins
|
78
|
+
for (const plugin of Object.values(config.plugins)) {
|
79
|
+
const pluginPath: string = path.normalize(plugin.path);
|
80
|
+
if (!fs.existsSync(pluginPath)) {
|
81
|
+
throw new Error(`plugin path does not exist: ${pluginPath}`);
|
82
|
+
}
|
83
|
+
|
84
|
+
if (plugin.initializers !== false) {
|
85
|
+
// old style at the root of the project
|
86
|
+
initializerFiles = initializerFiles.concat(
|
87
|
+
safeGlobSync(path.join(pluginPath, "initializers", "**", "*.js")),
|
88
|
+
);
|
89
|
+
|
90
|
+
// new TS dist files
|
91
|
+
initializerFiles = initializerFiles.concat(
|
92
|
+
safeGlobSync(
|
93
|
+
path.join(pluginPath, "dist", "initializers", "**", "*.js"),
|
94
|
+
),
|
95
|
+
);
|
96
|
+
}
|
97
|
+
}
|
98
|
+
|
99
|
+
initializerFiles = utils.arrayUnique(initializerFiles);
|
100
|
+
initializerFiles = utils.ensureNoTsHeaderOrSpecFiles(initializerFiles);
|
101
|
+
|
102
|
+
for (const i in initializerFiles) {
|
103
|
+
const f = initializerFiles[i];
|
104
|
+
const file = path.normalize(f);
|
105
|
+
if (require.cache[require.resolve(file)]) {
|
106
|
+
delete require.cache[require.resolve(file)];
|
107
|
+
}
|
108
|
+
|
109
|
+
let exportedClasses = await import(file);
|
110
|
+
|
111
|
+
// allow for old-js style single default exports
|
112
|
+
if (typeof exportedClasses === "function") {
|
113
|
+
exportedClasses = { default: exportedClasses };
|
114
|
+
}
|
115
|
+
|
116
|
+
if (Object.keys(exportedClasses).length === 0) {
|
117
|
+
this.fatalError(
|
118
|
+
new Error(`no exported initializers found in ${file}`),
|
119
|
+
file,
|
120
|
+
);
|
121
|
+
}
|
122
|
+
|
123
|
+
for (const exportKey in exportedClasses) {
|
124
|
+
let initializer: Initializer;
|
125
|
+
let InitializerClass = exportedClasses[exportKey];
|
126
|
+
try {
|
127
|
+
initializer = new InitializerClass();
|
128
|
+
|
129
|
+
// check if initializer already exists (exclude utils and config)
|
130
|
+
if (this.initializers[initializer.name]) {
|
131
|
+
const warningMessage = `an existing initializer with the same name \`${initializer.name}\` will be overridden by the file ${file}`;
|
132
|
+
log(warningMessage, "warning");
|
133
|
+
} else {
|
134
|
+
initializer.validate();
|
135
|
+
this.initializers[initializer.name] = initializer;
|
136
|
+
}
|
137
|
+
} catch (error) {
|
138
|
+
this.fatalError(error, file);
|
139
|
+
}
|
140
|
+
|
141
|
+
function decorateInitError(
|
142
|
+
error: NodeJS.ErrnoException & Record<string, any>,
|
143
|
+
type: string,
|
144
|
+
) {
|
145
|
+
error["data"] = error["data"] ?? {};
|
146
|
+
error["data"].name = initializer.name;
|
147
|
+
error["data"].file = file;
|
148
|
+
error["data"].type = type;
|
149
|
+
}
|
150
|
+
|
151
|
+
const initializeFunction = async () => {
|
152
|
+
if (typeof initializer.initialize === "function") {
|
153
|
+
log(`Loading initializer: ${initializer.name}`, "debug", file);
|
154
|
+
|
155
|
+
try {
|
156
|
+
await initializer.initialize();
|
157
|
+
log(`Loaded initializer: ${initializer.name}`, "debug", file);
|
158
|
+
} catch (error) {
|
159
|
+
decorateInitError(error, "initialize");
|
160
|
+
throw error;
|
161
|
+
}
|
162
|
+
}
|
163
|
+
};
|
164
|
+
|
165
|
+
const startFunction = async () => {
|
166
|
+
if (typeof initializer.start === "function") {
|
167
|
+
log(`Starting initializer: ${initializer.name}`, "debug", file);
|
168
|
+
|
169
|
+
try {
|
170
|
+
await initializer.start();
|
171
|
+
log(`Started initializer: ${initializer.name}`, "debug", file);
|
172
|
+
} catch (error) {
|
173
|
+
decorateInitError(error, "start");
|
174
|
+
throw error;
|
175
|
+
}
|
176
|
+
}
|
177
|
+
};
|
178
|
+
|
179
|
+
const stopFunction = async () => {
|
180
|
+
if (typeof initializer.stop === "function") {
|
181
|
+
log(`Stopping initializer: ${initializer.name}`, "debug", file);
|
182
|
+
|
183
|
+
try {
|
184
|
+
await initializer.stop();
|
185
|
+
log(`Stopped initializer: ${initializer.name}`, "debug", file);
|
186
|
+
} catch (error) {
|
187
|
+
decorateInitError(error, "stop");
|
188
|
+
throw error;
|
189
|
+
}
|
190
|
+
}
|
191
|
+
};
|
192
|
+
|
193
|
+
if (!loadInitializerRankings[initializer.loadPriority]) {
|
194
|
+
loadInitializerRankings[initializer.loadPriority] = [];
|
195
|
+
}
|
196
|
+
if (!startInitializerRankings[initializer.startPriority]) {
|
197
|
+
startInitializerRankings[initializer.startPriority] = [];
|
198
|
+
}
|
199
|
+
if (!stopInitializerRankings[initializer.stopPriority]) {
|
200
|
+
stopInitializerRankings[initializer.stopPriority] = [];
|
201
|
+
}
|
202
|
+
|
203
|
+
if (initializer.loadPriority > 0) {
|
204
|
+
loadInitializerRankings[initializer.loadPriority].push(
|
205
|
+
initializeFunction,
|
206
|
+
);
|
207
|
+
}
|
208
|
+
if (initializer.startPriority > 0) {
|
209
|
+
startInitializerRankings[initializer.startPriority].push(
|
210
|
+
startFunction,
|
211
|
+
);
|
212
|
+
}
|
213
|
+
if (initializer.stopPriority > 0) {
|
214
|
+
stopInitializerRankings[initializer.stopPriority].push(stopFunction);
|
215
|
+
}
|
216
|
+
}
|
217
|
+
}
|
218
|
+
|
219
|
+
// flatten all the ordered initializer methods
|
220
|
+
this.loadInitializers = this.flattenOrderedInitializer(
|
221
|
+
loadInitializerRankings,
|
222
|
+
);
|
223
|
+
this.startInitializers = this.flattenOrderedInitializer(
|
224
|
+
startInitializerRankings,
|
225
|
+
);
|
226
|
+
this.stopInitializers = this.flattenOrderedInitializer(
|
227
|
+
stopInitializerRankings,
|
228
|
+
);
|
229
|
+
|
230
|
+
try {
|
231
|
+
for (const loader of this.loadInitializers) await loader();
|
232
|
+
} catch (error) {
|
233
|
+
return this.fatalError(error, "initialize");
|
234
|
+
}
|
235
|
+
|
236
|
+
this.initialized = true;
|
237
|
+
}
|
238
|
+
|
239
|
+
/**
|
240
|
+
* Start the Actionhero Process
|
241
|
+
*/
|
242
|
+
async start() {
|
243
|
+
if (!this.initialized) await this.initialize();
|
244
|
+
const serverName = config.general.serverName;
|
245
|
+
|
246
|
+
writePidFile();
|
247
|
+
this.running = true;
|
248
|
+
api.running = true;
|
249
|
+
log(`environment: ${env}`, "notice");
|
250
|
+
log(`*** Starting ${serverName} ***`, "info");
|
251
|
+
|
252
|
+
this.startInitializers.push(async () => {
|
253
|
+
this.bootTime = new Date().getTime();
|
254
|
+
if (this.startCount === 0) {
|
255
|
+
log(`server ID: ${id}`, "notice");
|
256
|
+
log(`*** ${serverName} Started ***`, "notice");
|
257
|
+
this.startCount++;
|
258
|
+
} else {
|
259
|
+
log(`*** ${serverName} Restarted ***`, "notice");
|
260
|
+
}
|
261
|
+
});
|
262
|
+
|
263
|
+
try {
|
264
|
+
for (const starter of this.startInitializers) await starter();
|
265
|
+
} catch (error) {
|
266
|
+
return this.fatalError(error, "start");
|
267
|
+
}
|
268
|
+
|
269
|
+
this.started = true;
|
270
|
+
}
|
271
|
+
|
272
|
+
/**
|
273
|
+
* Stop the Actionhero Process
|
274
|
+
*/
|
275
|
+
async stop(stopReasons: string | string[] = []) {
|
276
|
+
const serverName = config.general.serverName;
|
277
|
+
|
278
|
+
if (this.running) {
|
279
|
+
this.shuttingDown = true;
|
280
|
+
this.running = false;
|
281
|
+
this.initialized = false;
|
282
|
+
this.started = false;
|
283
|
+
this.stopReasons = Array.isArray(stopReasons)
|
284
|
+
? stopReasons
|
285
|
+
: [stopReasons];
|
286
|
+
|
287
|
+
log("stopping process...", "notice");
|
288
|
+
if (this.stopReasons?.length > 0) {
|
289
|
+
log(`stop reasons: ${this.stopReasons.join(", ")}`, "debug");
|
290
|
+
}
|
291
|
+
|
292
|
+
await utils.sleep(100);
|
293
|
+
|
294
|
+
this.stopInitializers.push(async () => {
|
295
|
+
clearPidFile();
|
296
|
+
log(`*** ${serverName} Stopped ***`, "notice");
|
297
|
+
delete this.shuttingDown;
|
298
|
+
// reset initializers to prevent duplicate check on restart
|
299
|
+
this.initializers = {};
|
300
|
+
api.running = false;
|
301
|
+
await utils.sleep(100);
|
302
|
+
});
|
303
|
+
|
304
|
+
try {
|
305
|
+
for (const stopper of this.stopInitializers) await stopper();
|
306
|
+
} catch (error) {
|
307
|
+
return this.fatalError(error, "stop");
|
308
|
+
}
|
309
|
+
|
310
|
+
this.stopped = true;
|
311
|
+
} else if (this.shuttingDown === true) {
|
312
|
+
// double sigterm; ignore it
|
313
|
+
} else {
|
314
|
+
const message = `Cannot shut down ${serverName}, not running`;
|
315
|
+
log(message, "crit");
|
316
|
+
}
|
317
|
+
}
|
318
|
+
|
319
|
+
/**
|
320
|
+
* Restart the Actionhero Process
|
321
|
+
*/
|
322
|
+
async restart() {
|
323
|
+
if (this.running === true) {
|
324
|
+
await this.stop();
|
325
|
+
rebuildConfig();
|
326
|
+
await this.start();
|
327
|
+
} else {
|
328
|
+
await this.start();
|
329
|
+
}
|
330
|
+
}
|
331
|
+
|
332
|
+
/**
|
333
|
+
* Register listeners for process signals and uncaught exceptions & rejections.
|
334
|
+
* Try to gracefully shut down when signaled to do so
|
335
|
+
*/
|
336
|
+
registerProcessSignals(stopCallback = (exitCode?: number) => {}) {
|
337
|
+
const timeout = process.env.ACTIONHERO_SHUTDOWN_TIMEOUT
|
338
|
+
? parseInt(process.env.ACTIONHERO_SHUTDOWN_TIMEOUT)
|
339
|
+
: 1000 * 30;
|
340
|
+
|
341
|
+
function awaitHardStop() {
|
342
|
+
return setTimeout(() => {
|
343
|
+
console.error(
|
344
|
+
`Process did not terminate within ${timeout}ms. Stopping now!`,
|
345
|
+
);
|
346
|
+
process.nextTick(process.exit(1));
|
347
|
+
}, timeout);
|
348
|
+
}
|
349
|
+
|
350
|
+
// handle errors & rejections
|
351
|
+
process.once("uncaughtException", async (error: NodeJS.ErrnoException) => {
|
352
|
+
if (error.code !== fatalErrorCode) {
|
353
|
+
if (api.exceptionHandlers) {
|
354
|
+
api.exceptionHandlers.report(
|
355
|
+
error,
|
356
|
+
"uncaught",
|
357
|
+
"Exception",
|
358
|
+
{},
|
359
|
+
"emerg",
|
360
|
+
);
|
361
|
+
} else {
|
362
|
+
console.error(error);
|
363
|
+
}
|
364
|
+
}
|
365
|
+
|
366
|
+
if (this.shuttingDown !== true) {
|
367
|
+
let timer = awaitHardStop();
|
368
|
+
if (this.running) await this.stop();
|
369
|
+
clearTimeout(timer);
|
370
|
+
stopCallback(1);
|
371
|
+
}
|
372
|
+
});
|
373
|
+
|
374
|
+
process.once(
|
375
|
+
"unhandledRejection",
|
376
|
+
async (rejection: NodeJS.ErrnoException) => {
|
377
|
+
if (rejection.code !== fatalErrorCode) {
|
378
|
+
if (api.exceptionHandlers) {
|
379
|
+
api.exceptionHandlers.report(
|
380
|
+
rejection,
|
381
|
+
"uncaught",
|
382
|
+
"Rejection",
|
383
|
+
{},
|
384
|
+
"emerg",
|
385
|
+
);
|
386
|
+
} else {
|
387
|
+
console.error(rejection);
|
388
|
+
}
|
389
|
+
}
|
390
|
+
|
391
|
+
if (!this.shuttingDown) {
|
392
|
+
let timer = awaitHardStop();
|
393
|
+
if (this.running) await this.stop();
|
394
|
+
clearTimeout(timer);
|
395
|
+
stopCallback(1);
|
396
|
+
}
|
397
|
+
},
|
398
|
+
);
|
399
|
+
|
400
|
+
// handle signals
|
401
|
+
process.on("SIGINT", async () => {
|
402
|
+
log(`[ SIGNAL ] - SIGINT`, "notice");
|
403
|
+
let timer = awaitHardStop();
|
404
|
+
if (this.running) await this.stop();
|
405
|
+
if (!this.shuttingDown) {
|
406
|
+
clearTimeout(timer);
|
407
|
+
stopCallback(0);
|
408
|
+
}
|
409
|
+
});
|
410
|
+
|
411
|
+
process.on("SIGTERM", async () => {
|
412
|
+
log(`[ SIGNAL ] - SIGTERM`, "notice");
|
413
|
+
let timer = awaitHardStop();
|
414
|
+
if (this.running) await this.stop();
|
415
|
+
if (!this.shuttingDown) {
|
416
|
+
clearTimeout(timer);
|
417
|
+
stopCallback(0);
|
418
|
+
}
|
419
|
+
});
|
420
|
+
|
421
|
+
process.on("SIGUSR2", async () => {
|
422
|
+
log(`[ SIGNAL ] - SIGUSR2`, "notice");
|
423
|
+
let timer = awaitHardStop();
|
424
|
+
await this.restart();
|
425
|
+
clearTimeout(timer);
|
426
|
+
});
|
427
|
+
}
|
428
|
+
|
429
|
+
// HELPERS
|
430
|
+
async fatalError(
|
431
|
+
errors: NodeJS.ErrnoException | NodeJS.ErrnoException[] = [],
|
432
|
+
type: string,
|
433
|
+
) {
|
434
|
+
if (!(errors instanceof Array)) errors = [errors];
|
435
|
+
|
436
|
+
if (errors) {
|
437
|
+
const showStack = process.env.ACTIONHERO_FATAL_ERROR_STACK_DISPLAY
|
438
|
+
? process.env.ACTIONHERO_FATAL_ERROR_STACK_DISPLAY === "true"
|
439
|
+
: true;
|
440
|
+
|
441
|
+
errors.forEach((error) => {
|
442
|
+
if (!showStack) delete error.stack;
|
443
|
+
if (api.exceptionHandlers) {
|
444
|
+
api.exceptionHandlers.report(error, "initializer", type);
|
445
|
+
} else {
|
446
|
+
console.error(error);
|
447
|
+
}
|
448
|
+
});
|
449
|
+
|
450
|
+
if (this.running) {
|
451
|
+
await this.stop(errors.map((e) => e.message ?? e.toString())); // stop and set the stopReasons
|
452
|
+
}
|
453
|
+
await utils.sleep(100); // allow time for console.log to print
|
454
|
+
|
455
|
+
if (!errors[0].code) errors[0].code = fatalErrorCode;
|
456
|
+
throw errors[0];
|
457
|
+
}
|
458
|
+
}
|
459
|
+
|
460
|
+
flattenOrderedInitializer<T>(collection: { [rank: number]: T[] }) {
|
461
|
+
const output: T[] = [];
|
462
|
+
const keys = [];
|
463
|
+
|
464
|
+
for (const key in collection) keys.push(parseInt(key, 10));
|
465
|
+
keys.sort(sortNumber);
|
466
|
+
|
467
|
+
keys.forEach((key) => {
|
468
|
+
collection[key].forEach((d) => {
|
469
|
+
output.push(d);
|
470
|
+
});
|
471
|
+
});
|
472
|
+
|
473
|
+
return output;
|
474
|
+
}
|
475
|
+
}
|
476
|
+
|
477
|
+
function sortNumber(a: number, b: number) {
|
478
|
+
return a - b;
|
479
|
+
}
|