Merge pull request #607 from WoLfulus/feat/route-hooks

Add init/destroy hooks
This commit is contained in:
Rijk van Zanten
2020-10-15 12:51:54 -04:00
committed by GitHub
8 changed files with 209 additions and 108 deletions

View File

@@ -51,9 +51,9 @@
"directus": "cli.js"
},
"scripts": {
"start": "cross-env NODE_ENV=production node dist/server.js",
"start": "npx directus start",
"build": "rm -rf dist && tsc --build && copyfiles \"src/**/*.*\" -e \"src/**/*.ts\" -u 1 dist",
"dev": "cross-env NODE_ENV=development LOG_LEVEL=trace ts-node-dev --files src/server.ts --respawn --watch \"src/**/*.ts\" --transpile-only",
"dev": "cross-env NODE_ENV=development LOG_LEVEL=trace ts-node-dev --files src/start.ts --respawn --watch \"src/**/*.ts\" --transpile-only",
"cli": "cross-env NODE_ENV=development ts-node --script-mode --transpile-only src/cli/index.ts",
"prepublishOnly": "npm run build"
},
@@ -67,6 +67,7 @@
"@directus/app": "file:../app",
"@directus/format-title": "file:../packages/format-title",
"@directus/specs": "file:../packages/spec",
"@godaddy/terminus": "^4.4.1",
"@slynova/flydrive": "^1.0.2",
"@slynova/flydrive-gcs": "^1.0.2",
"@slynova/flydrive-s3": "^1.0.2",

View File

@@ -137,6 +137,7 @@ registerExtensions(customRouter);
track('serverStarted');
emitter.emitAsync('server.started').catch((err) => logger.warn(err));
emitter.emit('init.before', { app });
emitter.emitAsync('init').catch((err) => logger.warn(err));
export default app;

View File

@@ -1,47 +0,0 @@
import knex from 'knex';
import logger from '../../logger';
import { Express } from 'express';
export default async function start() {
const { default: env } = require('../../env');
const database = require('../../database');
const connection = database.default as knex;
await database.validateDBConnection();
const app: Express = require('../../app').default;
const port = env.PORT;
const server = app.listen(port, () => {
logger.info(`Server started at port ${port}`);
});
const signals: NodeJS.Signals[] = ['SIGHUP', 'SIGINT', 'SIGTERM'];
signals.forEach((signal) => {
process.on(signal, () =>
server.close((err) => {
if (err) {
logger.error(`Failed to close server: ${err.message}`, {
err,
});
process.exit(1);
}
logger.info('Server stopped.');
connection
.destroy()
.then(() => {
logger.info('Database connection stopped.');
process.exit(0);
})
.catch((err) => {
logger.info(`Failed to destroy database connections: ${err.message}`, {
err,
});
process.exit(1);
});
})
);
});
}

View File

@@ -4,7 +4,7 @@ import program from 'commander';
const pkg = require('../../package.json');
import start from './commands/start';
import start from '../start';
import init from './commands/init';
import dbInstall from './commands/database/install';
import dbMigrate from './commands/database/migrate';

View File

@@ -1,16 +1,43 @@
import app from './app';
import logger from './logger';
import env from './env';
import { validateDBConnection } from './database';
import { createTerminus, TerminusOptions } from '@godaddy/terminus';
import http from 'http';
import emitter from './emitter';
import database from './database';
import app from './app';
export default async function start() {
await validateDBConnection();
const server = http.createServer(app);
const port = env.NODE_ENV === 'development' ? 8055 : env.PORT;
const terminusOptions: TerminusOptions = {
timeout: 1000,
signals: ['SIGINT', 'SIGTERM', 'SIGHUP'],
beforeShutdown,
onSignal,
onShutdown,
};
app.listen(port, () => {
logger.info(`Server started at port ${port}`);
});
createTerminus(server, terminusOptions);
export default server;
async function beforeShutdown() {
await emitter.emitAsync('server.stop.before', { server });
if (process.env.NODE_ENV === 'development') {
logger.info('Restarting...');
} else {
logger.info('Shutting down...');
}
}
start();
async function onSignal() {
await database.destroy();
logger.info('Database connections destroyed');
}
async function onShutdown() {
emitter.emitAsync('server.stop').catch((err) => logger.warn(err));
if (process.env.NODE_ENV !== 'development') {
logger.info('Directus shut down OK. Bye bye!');
}
}

33
api/src/start.ts Normal file
View File

@@ -0,0 +1,33 @@
import emitter from './emitter';
import env from './env';
import logger from './logger';
// If this file is called directly using node, start the server
if (require.main === module) {
start();
}
export default async function start() {
const server = require('./server').default;
const { validateDBConnection } = require('./database');
await validateDBConnection();
await emitter.emitAsync('server.start.before', { server });
const port = env.PORT;
server
.listen(port, () => {
logger.info(`Server started at port ${port}`);
emitter.emitAsync('server.start').catch((err) => logger.warn(err));
})
.once('error', (err: any) => {
if (err?.code === 'EADDRINUSE') {
logger.fatal(`Port ${port} is already in use`);
process.exit(1);
} else {
throw err;
}
});
}

View File

@@ -24,7 +24,6 @@ Next, you will want to define your event. You can trigger your custom hook with
<scope>.<action>(.<before>)
// eg: items.create
// eg: files.create
// eg: server.start
// eg: collections.*
// eg: users.update.before
```
@@ -84,9 +83,10 @@ module.exports = function registerHook({ exceptions }) {
| `settings` | `create`, `update` and `delete` | Optional |
| `users` | `create`, `update` and `delete` | Optional |
| `webhooks` | `create`, `update` and `delete` | Optional |
| `server` | `start` and `error`† | No |
| `response` | | No† |
| `auth` | `success`†, `fail`† and `refresh`† | No |
| `init` | | Optional |
| `server` | `start` and `stop` | Optional |
† TBD

176
package-lock.json generated
View File

@@ -8200,6 +8200,13 @@
"supports-color": "^5.3.0"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true,
"optional": true
},
"commander": {
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
@@ -8237,6 +8244,83 @@
"worker-rpc": "^0.1.0"
}
},
"fork-ts-checker-webpack-plugin-v5": {
"version": "npm:fork-ts-checker-webpack-plugin@5.2.0",
"resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-5.2.0.tgz",
"integrity": "sha512-NEKcI0+osT5bBFZ1SFGzJMQETjQWZrSvMO1g0nAR/w0t328Z41eN8BJEIZyFCl2HsuiJpa9AN474Nh2qLVwGLQ==",
"dev": true,
"optional": true,
"requires": {
"@babel/code-frame": "^7.8.3",
"@types/json-schema": "^7.0.5",
"chalk": "^4.1.0",
"cosmiconfig": "^6.0.0",
"deepmerge": "^4.2.2",
"fs-extra": "^9.0.0",
"memfs": "^3.1.2",
"minimatch": "^3.0.4",
"schema-utils": "2.7.0",
"semver": "^7.3.2",
"tapable": "^1.0.0"
},
"dependencies": {
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"optional": true,
"requires": {
"color-convert": "^2.0.1"
}
},
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"optional": true,
"requires": {
"color-name": "~1.1.4"
}
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
"optional": true
},
"semver": {
"version": "7.3.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",
"integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==",
"dev": true,
"optional": true
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"optional": true,
"requires": {
"has-flag": "^4.0.0"
}
}
}
},
"globby": {
"version": "9.2.0",
"resolved": "https://registry.npmjs.org/globby/-/globby-9.2.0.tgz",
@@ -8282,6 +8366,18 @@
}
}
},
"schema-utils": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz",
"integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==",
"dev": true,
"optional": true,
"requires": {
"@types/json-schema": "^7.0.4",
"ajv": "^6.12.2",
"ajv-keywords": "^3.4.1"
}
},
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
@@ -15912,6 +16008,7 @@
"resolve-cwd": "^3.0.0",
"sharp": "^0.25.4",
"sqlite3": "^5.0.0",
"terminus": "^1.1.0",
"uuid": "^8.3.0",
"uuid-validate": "0.0.3"
},
@@ -18454,51 +18551,6 @@
}
}
},
"fork-ts-checker-webpack-plugin-v5": {
"version": "npm:fork-ts-checker-webpack-plugin@5.2.0",
"resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-5.2.0.tgz",
"integrity": "sha512-NEKcI0+osT5bBFZ1SFGzJMQETjQWZrSvMO1g0nAR/w0t328Z41eN8BJEIZyFCl2HsuiJpa9AN474Nh2qLVwGLQ==",
"dev": true,
"optional": true,
"requires": {
"@babel/code-frame": "^7.8.3",
"@types/json-schema": "^7.0.5",
"chalk": "^4.1.0",
"cosmiconfig": "^6.0.0",
"deepmerge": "^4.2.2",
"fs-extra": "^9.0.0",
"memfs": "^3.1.2",
"minimatch": "^3.0.4",
"schema-utils": "2.7.0",
"semver": "^7.3.2",
"tapable": "^1.0.0"
},
"dependencies": {
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"schema-utils": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz",
"integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==",
"dev": true,
"optional": true,
"requires": {
"@types/json-schema": "^7.0.4",
"ajv": "^6.12.2",
"ajv-keywords": "^3.4.1"
}
}
}
},
"form-data": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
@@ -37046,6 +37098,40 @@
"supports-hyperlinks": "^2.0.0"
}
},
"terminus": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/terminus/-/terminus-1.1.0.tgz",
"integrity": "sha1-ZH44xEYE+vYQtiOXT+8nRQIBrIQ=",
"requires": {
"readable-stream": "~2.0.4",
"xtend": "~4.0.0"
},
"dependencies": {
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
},
"process-nextick-args": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
"integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M="
},
"readable-stream": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz",
"integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=",
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.1",
"isarray": "~1.0.0",
"process-nextick-args": "~1.0.6",
"string_decoder": "~0.10.x",
"util-deprecate": "~1.0.1"
}
}
}
},
"terser": {
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz",