mirror of
https://github.com/directus/directus.git
synced 2026-01-22 18:37:55 -05:00
* added emitter context * partial items tests * updated items handler tests * fixed test after merge * forgot the event context * fixed auth message parsing for graphql subscriptions * fixed type strictness * fixed graphql subscription bug * bumped websocket dependencies * touched up some dangling code * updated itemsservice usage * disabled overkill logs * double checked environment type processing * fixed missed capitalization * fixed subscription payloads * Added explicit string type casting * removed obsolete "trimUpper" utility * using the parseJSON utility consistently * pinned dependencies * parse environment variables * fixed pnpm-lock * GraphQL Subscriptions for all events * fixed typo * added event data to the graphql definition * fix payload for delete events * Added optional chaining for type to prevent fatal crashes on invalid messages * fix failing on getting type from undefined * Update api/src/websocket/exceptions.ts Co-authored-by: Azri Kahar <42867097+azrikahar@users.noreply.github.com> * Add proper ZodError handling * added the zod-validation-error parser * allow disabling the rate limiter * Update api/src/websocket/controllers/base.ts Co-authored-by: Azri Kahar <42867097+azrikahar@users.noreply.github.com> * updated starting logs * fixed email/password expiration logic * added tests for getMessageType * simplified message parsing and dropped capitalization * updated authenticate test * switched to lower cased message.type to prevent spreading "toUpperCase" around * cleaned up debug logs * cast enabled config to boolean * Update api/src/websocket/controllers/rest.ts Co-authored-by: Azri Kahar <42867097+azrikahar@users.noreply.github.com> * Update api/src/websocket/handlers/subscribe.ts Co-authored-by: Azri Kahar <42867097+azrikahar@users.noreply.github.com> * Update api/src/websocket/handlers/subscribe.ts Co-authored-by: Azri Kahar <42867097+azrikahar@users.noreply.github.com> * Update api/src/websocket/handlers/items.ts Co-authored-by: Azri Kahar <42867097+azrikahar@users.noreply.github.com> * Update api/src/websocket/controllers/base.ts Co-authored-by: Azri Kahar <42867097+azrikahar@users.noreply.github.com> * Update api/src/websocket/handlers/heartbeat.ts Co-authored-by: Azri Kahar <42867097+azrikahar@users.noreply.github.com> * Suggested fixes by Azri * removed redundant try-catch * fixed authentication timeout added returning the refresh token when authenticating * updated pnpm lock after merge * Fixed authentication modes for GraphQL according to best practices * implement useFakeTimers in heartbeat unit test * implement useFakeTimers in items unit test * Update api/src/services/server.ts Co-authored-by: Azri Kahar <42867097+azrikahar@users.noreply.github.com> * removed obsolete authentication.verbose toggle * added email flag to message validation * switched to ternary for consistency * moved getSchema out of for loop * added singleton logic to items handler * close the socket after failed auth for non-public connections * disabled system collections for rest subscriptions * re-ran pnpm i * allow for multiple subscripitions in the memory messenger * - fixed system collection subscriptions - abstracted hook message bus - fixed graphql horizontal scaling * remove logic from root context for tests * fix reading created item * fix linter * typo and extra safe guard suggested by azri * prevent setting long timeouts in favor of a shared interval * prevent unsubscribing all existing subscriptions when omitting "uid" * - extracted getService utility - block system collections mutation in the items handler - implemented the correct services for system collections * allow numeric uid's to be used * fixed the types for numeric uid's to be used * added missing await's * fixed type imports after merge * removed unused imports * Update api/src/websocket/controllers/hooks.ts Co-authored-by: Azri Kahar <42867097+azrikahar@users.noreply.github.com> * Update api/src/websocket/controllers/hooks.ts Co-authored-by: Azri Kahar <42867097+azrikahar@users.noreply.github.com> * Update api/src/messenger.ts Co-authored-by: Azri Kahar <42867097+azrikahar@users.noreply.github.com> * improved error for graphql subscriptions * fixed TS Modernization conflicts * fixed TS Modernization conflicts * fixed conflicts after merge * removed unused name property * abstracxted environment configuration * respond to ping messages when heartbeat disabled * something something merge * moved toBoolean to it's own util file * replaced old socket naming * removed old exception * fixed typo * Update api/src/env.ts Co-authored-by: ian <licitdev@gmail.com> * Update api/src/websocket/handlers/heartbeat.test.ts Co-authored-by: ian <licitdev@gmail.com> * Update api/src/websocket/handlers/heartbeat.ts Co-authored-by: ian <licitdev@gmail.com> * Update api/src/services/server.ts Co-authored-by: ian <licitdev@gmail.com> * fixed for linter * add server_info_websocket in graphql * Add base REST websocket tests * do merge things * fixing things * fixed failing unit test * Update dependencies * Move tests * Update lockfile * Use new paths when spawning * return websockets to opt-in * Enable websockets for tests * Test with ephemeral access token * no camelcasing gql subscriptions * use underscore for gql event * Remove unused import * Add base GraphQL subscription tests * Fix accidental comment * Add some relational tests * Organize imports Using VS Code's default organize import * Run ESlint formatting * One more opinionated formatting change * Formatting * Fix message sequence not in order * Remove relational batch update tests * Test horizontal scaling * using toboolean util for server_info * removed unneeded type cast * found the gql request type * extra usage of the toBoolean util * merge the authentication middleware and get-account-for-token util * updated utility test * fixed middleware unit test * Add return * Remove user filtering and close conns * Fix reused accountability * fixed failing util test * added subscription unit tests * added missing mock * trigger workflow * Revert "trigger workflow" This reverts commit4f544b0c1b. * Trigger testing for all vendors * add unsubscription confirmation * Wait for unsubscription confirmation * Fix incorrect sending of unsubscription confirmation * updated ubsubscribe logic * Update count for unsubscription message * Fix sequence for UUID pktype in MSSQL * Increase auth timeout * Add start index when getting messages * Fix subscription retrieval and cast uid to string * Remove nested ternary * Revert "Increase auth timeout" This reverts commit10707409c4. * Terminate connection instead of close * fixed merge * re-added missing packages after merge resolve * fixed type imports * Create lazy-cows-happen.md Added changeset * Minor bump for "directus" package as well * fixed "strict" auth mode for graphql subscriptions * removed nested ternary * Add websocket tests to sequential flow * Disable pressure limiter for blackbox tests * fix merge * WebSockets Documentation (#18254) * Small repsonsive styling fix on Card * REST getting started guide * Authentication guide * REST subscription guides * JS Chat guide * Sidebar websocket guides section * Added config options * Respoinding to brainslug's review * Fixed incorrect header on guides/rt/subs * Fixed spellchecker * Correct full code example on guides/rt/chat/js * Fixed JS chat tut * Order of steps in js chat guide updated for easier following-along * Realtime chat Vue Guide * feat: create react.js file * feat: add set up for directus project * docs: create react boilder plate * docs: initialize connection * docs: set up submission methods * docs: establish websocket connection * docs: subscribe to messages * docs: create new messages * docs: display new messages * docs: display historical messages * docs: next steps * docs: full code sample * docs: clean up * docs: add name to contributors * docs: add react card * docs: updates to react chat * Added live poll result guide * docs: intro * docs: before you begin * docs: install packages * docs: authenticate connection * docs: query and mutation * docs: utilize hooks * docs: subscribe to changes * docs: create helper functions * docs: display messages * docs: summary * docs: full sample code * chore: add card for webscockets with graphql * docs: intro * docs: subscribe to changes * docs: handling changes * docs: crud operations * docs: unsubscribing from changes * docs: updates * chore: add card * chore: updates to graphql docs * chore: updates to getting started * chore: updates to subscription * chore: updates to real chat guide * Added WebSockets Operations Guide * Consistent titles * Contributors component for docs * Triggering Netlify * Add operations to sidebar * Fix operations link * Small formatting changes * Clarity around property values * Removed unused values in Contributors component * Prompt for default choice * Tabs & lowercase doctypes * Semicolons * Event overwerites -> event listeners * Spacing * Flipped order of websockets guide to match GQL --------- Co-authored-by: Esther Agbaje <folasadeagbaje@gmail.com> Co-authored-by: Rijk van Zanten <rijkvanzanten@me.com> * fixed typo * removed unused import * added tests for "to-boolean" and "exceptions" * added websocket service tests * quote environment variable to satisfy dictionary * GraphQL Subscriptions update (#18804) * updated graphql subscription structure * updated graphql examples * Create hungry-points-rescue.md * using `key` instead of `ID` on the toplevel * removed changeset * fixed the graphql type after the rename to `key` * retrun data null for delete events to prevent non-nullable gql error * updated missed ID reference in the docs * updated missed ID reference in the docs * renamed "payload" to "data" in the REST Subscription response * fixed missed reference to payload * added optional event filter for REST subscriptions * updated docs for event filter * Update docs/guides/real-time/subscriptions/websockets.md Co-authored-by: ian <licitdev@gmail.com> --------- Co-authored-by: ian <licitdev@gmail.com> * added messenger unit test * always send subscription confirmation * Add event to subscription options * Update tests * Add tests for event filtering * Revert testing for all vendors * Remove obsolete console comment * Update comment * Correct event in JS WS guide * Fix collection name to match name used in subscription * Fix collection name in other guides * Fix diffs in doc & enhance chart example * Complete sentence in GraphQL guide * Small update to config description --------- Co-authored-by: Rijk van Zanten <rijkvanzanten@me.com> Co-authored-by: Azri Kahar <42867097+azrikahar@users.noreply.github.com> Co-authored-by: ian <licitdev@gmail.com> Co-authored-by: Nitwel <mail@nitwel.de> Co-authored-by: Kevin Lewis <kvn@lws.io> Co-authored-by: Pascal Jufer <pascal-jufer@bluewin.ch> Co-authored-by: Esther Agbaje <folasadeagbaje@gmail.com>
177 lines
4.5 KiB
TypeScript
177 lines
4.5 KiB
TypeScript
import type { TerminusOptions } from '@godaddy/terminus';
|
|
import { createTerminus } from '@godaddy/terminus';
|
|
import type { Request } from 'express';
|
|
import * as http from 'http';
|
|
import * as https from 'https';
|
|
import { once } from 'lodash-es';
|
|
import qs from 'qs';
|
|
import url from 'url';
|
|
import createApp from './app.js';
|
|
import getDatabase from './database/index.js';
|
|
import emitter from './emitter.js';
|
|
import env from './env.js';
|
|
import logger from './logger.js';
|
|
import { getConfigFromEnv } from './utils/get-config-from-env.js';
|
|
import {
|
|
createSubscriptionController,
|
|
createWebSocketController,
|
|
getSubscriptionController,
|
|
getWebSocketController,
|
|
} from './websocket/controllers/index.js';
|
|
import { startWebSocketHandlers } from './websocket/handlers/index.js';
|
|
import { toBoolean } from './utils/to-boolean.js';
|
|
|
|
export let SERVER_ONLINE = true;
|
|
|
|
export async function createServer(): Promise<http.Server> {
|
|
const server = http.createServer(await createApp());
|
|
|
|
Object.assign(server, getConfigFromEnv('SERVER_'));
|
|
|
|
server.on('request', function (req: http.IncomingMessage & Request, res: http.ServerResponse) {
|
|
const startTime = process.hrtime();
|
|
|
|
const complete = once(function (finished: boolean) {
|
|
const elapsedTime = process.hrtime(startTime);
|
|
const elapsedNanoseconds = elapsedTime[0] * 1e9 + elapsedTime[1];
|
|
const elapsedMilliseconds = elapsedNanoseconds / 1e6;
|
|
|
|
const previousIn = (req.socket as any)._metrics?.in || 0;
|
|
const previousOut = (req.socket as any)._metrics?.out || 0;
|
|
|
|
const metrics = {
|
|
in: req.socket.bytesRead - previousIn,
|
|
out: req.socket.bytesWritten - previousOut,
|
|
};
|
|
|
|
(req.socket as any)._metrics = {
|
|
in: req.socket.bytesRead,
|
|
out: req.socket.bytesWritten,
|
|
};
|
|
|
|
// Compatibility when supporting serving with certificates
|
|
const protocol = server instanceof https.Server ? 'https' : 'http';
|
|
|
|
// Rely on url.parse for path extraction
|
|
// Doesn't break on illegal URLs
|
|
const urlInfo = url.parse(req.originalUrl || req.url);
|
|
|
|
const info = {
|
|
finished,
|
|
request: {
|
|
aborted: req.aborted,
|
|
completed: req.complete,
|
|
method: req.method,
|
|
url: urlInfo.href,
|
|
path: urlInfo.pathname,
|
|
protocol,
|
|
host: req.headers.host,
|
|
size: metrics.in,
|
|
query: urlInfo.query ? qs.parse(urlInfo.query) : {},
|
|
headers: req.headers,
|
|
},
|
|
response: {
|
|
status: res.statusCode,
|
|
size: metrics.out,
|
|
headers: res.getHeaders(),
|
|
},
|
|
ip: req.headers['x-forwarded-for'] || req.socket?.remoteAddress,
|
|
duration: elapsedMilliseconds.toFixed(),
|
|
};
|
|
|
|
emitter.emitAction('response', info, {
|
|
database: getDatabase(),
|
|
schema: req.schema,
|
|
accountability: req.accountability ?? null,
|
|
});
|
|
});
|
|
|
|
res.once('finish', complete.bind(null, true));
|
|
res.once('close', complete.bind(null, false));
|
|
});
|
|
|
|
if (toBoolean(env['WEBSOCKETS_ENABLED']) === true) {
|
|
createSubscriptionController(server);
|
|
createWebSocketController(server);
|
|
startWebSocketHandlers();
|
|
}
|
|
|
|
const terminusOptions: TerminusOptions = {
|
|
timeout:
|
|
env['SERVER_SHUTDOWN_TIMEOUT'] >= 0 && env['SERVER_SHUTDOWN_TIMEOUT'] < Infinity
|
|
? env['SERVER_SHUTDOWN_TIMEOUT']
|
|
: 1000,
|
|
signals: ['SIGINT', 'SIGTERM', 'SIGHUP'],
|
|
beforeShutdown,
|
|
onSignal,
|
|
onShutdown,
|
|
};
|
|
|
|
createTerminus(server, terminusOptions);
|
|
|
|
return server;
|
|
|
|
async function beforeShutdown() {
|
|
if (env['NODE_ENV'] !== 'development') {
|
|
logger.info('Shutting down...');
|
|
}
|
|
|
|
SERVER_ONLINE = false;
|
|
}
|
|
|
|
async function onSignal() {
|
|
getSubscriptionController()?.terminate();
|
|
getWebSocketController()?.terminate();
|
|
const database = getDatabase();
|
|
await database.destroy();
|
|
|
|
logger.info('Database connections destroyed');
|
|
}
|
|
|
|
async function onShutdown() {
|
|
emitter.emitAction(
|
|
'server.stop',
|
|
{ server },
|
|
{
|
|
database: getDatabase(),
|
|
schema: null,
|
|
accountability: null,
|
|
}
|
|
);
|
|
|
|
if (env['NODE_ENV'] !== 'development') {
|
|
logger.info('Directus shut down OK. Bye bye!');
|
|
}
|
|
}
|
|
}
|
|
|
|
export async function startServer(): Promise<void> {
|
|
const server = await createServer();
|
|
|
|
const host = env['HOST'];
|
|
const port = env['PORT'];
|
|
|
|
server
|
|
.listen(port, host, () => {
|
|
logger.info(`Server started at http://${host}:${port}`);
|
|
|
|
emitter.emitAction(
|
|
'server.start',
|
|
{ server },
|
|
{
|
|
database: getDatabase(),
|
|
schema: null,
|
|
accountability: null,
|
|
}
|
|
);
|
|
})
|
|
.once('error', (err: any) => {
|
|
if (err?.code === 'EADDRINUSE') {
|
|
logger.error(`Port ${port} is already in use`);
|
|
process.exit(1);
|
|
} else {
|
|
throw err;
|
|
}
|
|
});
|
|
}
|