@directus/api 18.2.0 → 19.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 (40) hide show
  1. package/dist/app.js +0 -3
  2. package/dist/auth/drivers/ldap.js +1 -1
  3. package/dist/auth/drivers/local.js +1 -1
  4. package/dist/auth/drivers/oauth2.js +1 -1
  5. package/dist/auth/drivers/openid.js +1 -1
  6. package/dist/cache.js +1 -1
  7. package/dist/controllers/activity.js +1 -1
  8. package/dist/controllers/auth.js +7 -6
  9. package/dist/controllers/extensions.js +30 -0
  10. package/dist/controllers/fields.js +1 -3
  11. package/dist/controllers/webhooks.js +10 -74
  12. package/dist/database/migrations/20240122A-add-report-url-fields.d.ts +3 -0
  13. package/dist/database/migrations/20240122A-add-report-url-fields.js +14 -0
  14. package/dist/database/migrations/20240204A-marketplace.js +17 -5
  15. package/dist/database/migrations/20240305A-change-useragent-type.d.ts +3 -0
  16. package/dist/database/migrations/20240305A-change-useragent-type.js +19 -0
  17. package/dist/database/migrations/20240311A-deprecate-webhooks.d.ts +13 -0
  18. package/dist/database/migrations/20240311A-deprecate-webhooks.js +125 -0
  19. package/dist/extensions/manager.d.ts +1 -0
  20. package/dist/extensions/manager.js +4 -1
  21. package/dist/middleware/authenticate.js +1 -1
  22. package/dist/services/extensions.d.ts +3 -0
  23. package/dist/services/extensions.js +40 -9
  24. package/dist/services/fields.d.ts +2 -1
  25. package/dist/services/fields.js +33 -4
  26. package/dist/services/graphql/index.js +1 -1
  27. package/dist/services/relations.js +6 -0
  28. package/dist/services/webhooks.d.ts +7 -4
  29. package/dist/services/webhooks.js +15 -12
  30. package/dist/utils/get-ast-from-query.js +1 -1
  31. package/dist/utils/get-auth-providers.d.ts +3 -1
  32. package/dist/utils/get-auth-providers.js +15 -4
  33. package/dist/utils/get-schema.d.ts +1 -1
  34. package/dist/utils/get-schema.js +52 -29
  35. package/dist/websocket/controllers/base.d.ts +1 -3
  36. package/dist/websocket/controllers/base.js +12 -3
  37. package/license +1 -1
  38. package/package.json +36 -34
  39. package/dist/webhooks.d.ts +0 -4
  40. package/dist/webhooks.js +0 -80
@@ -1,4 +1,5 @@
1
1
  import { KNEX_TYPES, REGEX_BETWEEN_PARENS } from '@directus/constants';
2
+ import { ForbiddenError, InvalidPayloadError } from '@directus/errors';
2
3
  import { createInspector } from '@directus/schema';
3
4
  import { addFieldFlag, toArray } from '@directus/utils';
4
5
  import { isEqual, isNil, merge } from 'lodash-es';
@@ -8,16 +9,15 @@ import { translateDatabaseError } from '../database/errors/translate.js';
8
9
  import { getHelpers } from '../database/helpers/index.js';
9
10
  import getDatabase, { getSchemaInspector } from '../database/index.js';
10
11
  import emitter from '../emitter.js';
11
- import { ForbiddenError, InvalidPayloadError } from '@directus/errors';
12
- import { ItemsService } from './items.js';
13
- import { PayloadService } from './payload.js';
14
12
  import getDefaultValue from '../utils/get-default-value.js';
13
+ import { getSystemFieldRowsWithAuthProviders } from '../utils/get-field-system-rows.js';
15
14
  import getLocalType from '../utils/get-local-type.js';
16
15
  import { getSchema } from '../utils/get-schema.js';
17
16
  import { sanitizeColumn } from '../utils/sanitize-schema.js';
18
17
  import { shouldClearCache } from '../utils/should-clear-cache.js';
18
+ import { ItemsService } from './items.js';
19
+ import { PayloadService } from './payload.js';
19
20
  import { RelationsService } from './relations.js';
20
- import { getSystemFieldRowsWithAuthProviders } from '../utils/get-field-system-rows.js';
21
21
  const systemFieldRows = getSystemFieldRowsWithAuthProviders();
22
22
  export class FieldsService {
23
23
  knex;
@@ -406,6 +406,35 @@ export class FieldsService {
406
406
  }
407
407
  }
408
408
  }
409
+ async updateFields(collection, fields, opts) {
410
+ const nestedActionEvents = [];
411
+ try {
412
+ const fieldNames = [];
413
+ for (const field of fields) {
414
+ fieldNames.push(await this.updateField(collection, field, {
415
+ autoPurgeCache: false,
416
+ autoPurgeSystemCache: false,
417
+ bypassEmitAction: (params) => nestedActionEvents.push(params),
418
+ }));
419
+ }
420
+ return fieldNames;
421
+ }
422
+ finally {
423
+ if (shouldClearCache(this.cache, opts)) {
424
+ await this.cache.clear();
425
+ }
426
+ if (opts?.autoPurgeSystemCache !== false) {
427
+ await clearSystemCache({ autoPurgeCache: opts?.autoPurgeCache });
428
+ }
429
+ if (opts?.emitEvents !== false && nestedActionEvents.length > 0) {
430
+ const updatedSchema = await getSchema();
431
+ for (const nestedActionEvent of nestedActionEvents) {
432
+ nestedActionEvent.context.schema = updatedSchema;
433
+ emitter.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
434
+ }
435
+ }
436
+ }
437
+ }
409
438
  async deleteField(collection, field, opts) {
410
439
  if (this.accountability && this.accountability.admin !== true) {
411
440
  throw new ForbiddenError();
@@ -105,7 +105,7 @@ export class GraphQLService {
105
105
  return formattedResult;
106
106
  }
107
107
  getSchema(type = 'schema') {
108
- const key = `${type}_${this.accountability?.role}_${this.accountability?.user}`;
108
+ const key = `${this.scope}_${type}_${this.accountability?.role}_${this.accountability?.user}`;
109
109
  const cachedSchema = cache.get(key);
110
110
  if (cachedSchema)
111
111
  return cachedSchema;
@@ -161,6 +161,9 @@ export class RelationsService {
161
161
  if (relation.schema?.on_delete) {
162
162
  builder.onDelete(relation.schema.on_delete);
163
163
  }
164
+ if (relation.schema?.on_update) {
165
+ builder.onUpdate(relation.schema.on_update);
166
+ }
164
167
  });
165
168
  }
166
169
  const relationsItemService = new ItemsService('directus_relations', {
@@ -236,6 +239,9 @@ export class RelationsService {
236
239
  if (relation.schema?.on_delete) {
237
240
  builder.onDelete(relation.schema.on_delete);
238
241
  }
242
+ if (relation.schema?.on_update) {
243
+ builder.onUpdate(relation.schema.on_update);
244
+ }
239
245
  });
240
246
  }
241
247
  const relationsItemService = new ItemsService('directus_relations', {
@@ -1,11 +1,14 @@
1
+ import { type DirectusError } from '@directus/errors';
1
2
  import type { Bus } from '@directus/memory';
2
- import type { AbstractServiceOptions, Item, MutationOptions, PrimaryKey, Webhook } from '../types/index.js';
3
+ import type { AbstractServiceOptions, MutationOptions, PrimaryKey, Webhook } from '../types/index.js';
3
4
  import { ItemsService } from './items.js';
4
5
  export declare class WebhooksService extends ItemsService<Webhook> {
5
6
  messenger: Bus;
7
+ errorDeprecation: DirectusError;
6
8
  constructor(options: AbstractServiceOptions);
7
- createOne(data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
8
- createMany(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
9
- updateMany(keys: PrimaryKey[], data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey[]>;
9
+ createOne(): Promise<PrimaryKey>;
10
+ createMany(): Promise<PrimaryKey[]>;
11
+ updateBatch(): Promise<PrimaryKey[]>;
12
+ updateMany(): Promise<PrimaryKey[]>;
10
13
  deleteMany(keys: PrimaryKey[], opts?: MutationOptions): Promise<PrimaryKey[]>;
11
14
  }
@@ -1,25 +1,28 @@
1
+ import { ErrorCode, createError } from '@directus/errors';
1
2
  import { useBus } from '../bus/index.js';
3
+ import { useLogger } from '../logger.js';
2
4
  import { ItemsService } from './items.js';
5
+ const logger = useLogger();
3
6
  export class WebhooksService extends ItemsService {
4
7
  messenger;
8
+ errorDeprecation;
5
9
  constructor(options) {
6
10
  super('directus_webhooks', options);
7
11
  this.messenger = useBus();
12
+ this.errorDeprecation = new (createError(ErrorCode.MethodNotAllowed, 'Webhooks are deprecated, use Flows instead', 405))();
13
+ logger.warn('Webhooks are deprecated and the WebhooksService will be removed in an upcoming release. Creating/Updating Webhooks is disabled, use Flows instead');
8
14
  }
9
- async createOne(data, opts) {
10
- const result = await super.createOne(data, opts);
11
- this.messenger.publish('webhooks', { type: 'reload' });
12
- return result;
15
+ async createOne() {
16
+ throw this.errorDeprecation;
13
17
  }
14
- async createMany(data, opts) {
15
- const result = await super.createMany(data, opts);
16
- this.messenger.publish('webhooks', { type: 'reload' });
17
- return result;
18
+ async createMany() {
19
+ throw this.errorDeprecation;
18
20
  }
19
- async updateMany(keys, data, opts) {
20
- const result = await super.updateMany(keys, data, opts);
21
- this.messenger.publish('webhooks', { type: 'reload' });
22
- return result;
21
+ async updateBatch() {
22
+ throw this.errorDeprecation;
23
+ }
24
+ async updateMany() {
25
+ throw this.errorDeprecation;
23
26
  }
24
27
  async deleteMany(keys, opts) {
25
28
  const result = await super.deleteMany(keys, opts);
@@ -73,7 +73,7 @@ export default async function getASTFromQuery(collection, query, schema, options
73
73
  for (const fieldKey of fields) {
74
74
  let name = fieldKey;
75
75
  if (query.alias) {
76
- // check for field alias (is is one of the key)
76
+ // check for field alias (is one of the key)
77
77
  if (name in query.alias) {
78
78
  name = query.alias[fieldKey];
79
79
  }
@@ -4,5 +4,7 @@ interface AuthProvider {
4
4
  icon?: string;
5
5
  label?: string;
6
6
  }
7
- export declare function getAuthProviders(): AuthProvider[];
7
+ export declare function getAuthProviders({ sessionOnly }?: {
8
+ sessionOnly: boolean;
9
+ }): AuthProvider[];
8
10
  export {};
@@ -1,10 +1,21 @@
1
1
  import { useEnv } from '@directus/env';
2
2
  import { toArray } from '@directus/utils';
3
- export function getAuthProviders() {
3
+ export function getAuthProviders({ sessionOnly } = { sessionOnly: false }) {
4
4
  const env = useEnv();
5
- return toArray(env['AUTH_PROVIDERS'])
6
- .filter((provider) => provider && env[`AUTH_${provider.toUpperCase()}_DRIVER`])
7
- .map((provider) => ({
5
+ let providers = toArray(env['AUTH_PROVIDERS']).filter((provider) => provider && env[`AUTH_${provider.toUpperCase()}_DRIVER`]);
6
+ if (sessionOnly) {
7
+ providers = providers.filter((provider) => {
8
+ const driver = env[`AUTH_${provider.toUpperCase()}_DRIVER`];
9
+ // only the following 3 drivers require a mode selection
10
+ if (['oauth2', 'openid', 'saml'].includes(driver)) {
11
+ const mode = env[`AUTH_${provider.toUpperCase()}_MODE`];
12
+ // if mode is not defined it defaults to session
13
+ return !mode || mode === 'session';
14
+ }
15
+ return true;
16
+ });
17
+ }
18
+ return providers.map((provider) => ({
8
19
  name: provider,
9
20
  label: env[`AUTH_${provider.toUpperCase()}_LABEL`],
10
21
  driver: env[`AUTH_${provider.toUpperCase()}_DRIVER`],
@@ -7,4 +7,4 @@ export declare function getSchema(options?: {
7
7
  * Used to ensure schema snapshot/apply is not using outdated schema
8
8
  */
9
9
  bypassCache?: boolean;
10
- }): Promise<SchemaOverview>;
10
+ }, attempt?: number): Promise<SchemaOverview>;
@@ -1,47 +1,70 @@
1
1
  import { useEnv } from '@directus/env';
2
2
  import { createInspector } from '@directus/schema';
3
+ import { systemCollectionRows } from '@directus/system-data';
3
4
  import { parseJSON, toArray } from '@directus/utils';
4
5
  import { mapValues } from 'lodash-es';
6
+ import { useBus } from '../bus/index.js';
5
7
  import { getSchemaCache, setSchemaCache } from '../cache.js';
6
8
  import { ALIAS_TYPES } from '../constants.js';
7
9
  import getDatabase from '../database/index.js';
10
+ import { useLock } from '../lock/index.js';
8
11
  import { useLogger } from '../logger.js';
9
12
  import { RelationsService } from '../services/relations.js';
10
13
  import getDefaultValue from './get-default-value.js';
11
- import getLocalType from './get-local-type.js';
12
- import { systemCollectionRows } from '@directus/system-data';
13
14
  import { getSystemFieldRowsWithAuthProviders } from './get-field-system-rows.js';
15
+ import getLocalType from './get-local-type.js';
14
16
  const logger = useLogger();
15
- export async function getSchema(options) {
17
+ export async function getSchema(options, attempt = 0) {
18
+ const MAX_ATTEMPTS = 3;
16
19
  const env = useEnv();
17
- const database = options?.database || getDatabase();
18
- const schemaInspector = createInspector(database);
19
- let result;
20
- if (!options?.bypassCache && env['CACHE_SCHEMA'] !== false) {
21
- let cachedSchema;
22
- try {
23
- cachedSchema = await getSchemaCache();
24
- }
25
- catch (err) {
26
- logger.warn(err, `[schema-cache] Couldn't retrieve cache. ${err}`);
27
- }
28
- if (cachedSchema) {
29
- result = cachedSchema;
30
- }
31
- else {
32
- result = await getDatabaseSchema(database, schemaInspector);
33
- try {
34
- await setSchemaCache(result);
35
- }
36
- catch (err) {
37
- logger.warn(err, `[schema-cache] Couldn't save cache. ${err}`);
38
- }
39
- }
20
+ if (attempt >= MAX_ATTEMPTS) {
21
+ throw new Error(`Failed to get Schema information: hit infinite loop`);
40
22
  }
41
- else {
42
- result = await getDatabaseSchema(database, schemaInspector);
23
+ if (options?.bypassCache || env['CACHE_SCHEMA'] === false) {
24
+ const database = options?.database || getDatabase();
25
+ const schemaInspector = createInspector(database);
26
+ return await getDatabaseSchema(database, schemaInspector);
27
+ }
28
+ const cached = await getSchemaCache();
29
+ if (cached) {
30
+ return cached;
31
+ }
32
+ const lock = useLock();
33
+ const bus = useBus();
34
+ const lockKey = 'schemaCache--preparing';
35
+ const messageKey = 'schemaCache--done';
36
+ const processId = await lock.increment(lockKey);
37
+ const currentProcessShouldHandleOperation = processId === 1;
38
+ if (currentProcessShouldHandleOperation === false) {
39
+ logger.trace('Schema cache is prepared in another process, waiting for result.');
40
+ return new Promise((resolve) => {
41
+ const TIMEOUT = 10000;
42
+ let timeout;
43
+ const callback = async () => {
44
+ if (timeout)
45
+ clearTimeout(timeout);
46
+ const schema = await getSchema(options, attempt + 1);
47
+ resolve(schema);
48
+ bus.unsubscribe(messageKey, callback);
49
+ };
50
+ bus.subscribe(messageKey, callback);
51
+ timeout = setTimeout(async () => {
52
+ logger.trace('Did not receive schema callback message in time. Pulling schema...');
53
+ callback();
54
+ }, TIMEOUT);
55
+ });
56
+ }
57
+ try {
58
+ const database = options?.database || getDatabase();
59
+ const schemaInspector = createInspector(database);
60
+ const schema = await getDatabaseSchema(database, schemaInspector);
61
+ await setSchemaCache(schema);
62
+ return schema;
63
+ }
64
+ finally {
65
+ await lock.delete(lockKey);
66
+ bus.publish(messageKey, { ready: true });
43
67
  }
44
- return result;
45
68
  }
46
69
  async function getDatabaseSchema(database, schemaInspector) {
47
70
  const env = useEnv();
@@ -1,11 +1,9 @@
1
1
  /// <reference types="node" resolution-mode="require"/>
2
2
  /// <reference types="node" resolution-mode="require"/>
3
3
  /// <reference types="node" resolution-mode="require"/>
4
- /// <reference types="node" resolution-mode="require"/>
5
4
  /// <reference types="node/http.js" />
6
5
  /// <reference types="pino-http" />
7
6
  import type { IncomingMessage, Server as httpServer } from 'http';
8
- import type { ParsedUrlQuery } from 'querystring';
9
7
  import type { RateLimiterAbstract } from 'rate-limiter-flexible';
10
8
  import type internal from 'stream';
11
9
  import WebSocket from 'ws';
@@ -34,7 +32,7 @@ export default abstract class SocketController {
34
32
  protected getRateLimiter(): RateLimiterAbstract | null;
35
33
  private catchInvalidMessages;
36
34
  protected handleUpgrade(request: IncomingMessage, socket: internal.Duplex, head: Buffer): Promise<void>;
37
- protected handleStrictUpgrade({ request, socket, head }: UpgradeContext, query: ParsedUrlQuery): Promise<void>;
35
+ protected handleTokenUpgrade({ request, socket, head }: UpgradeContext, token: string): Promise<void>;
38
36
  protected handleHandshakeUpgrade({ request, socket, head }: UpgradeContext): Promise<void>;
39
37
  createClient(ws: WebSocket, { accountability, expires_at }: AuthenticationState): WebSocketClient;
40
38
  protected parseMessage(data: string): WebSocketMessage;
@@ -16,6 +16,7 @@ import { getExpiresAtForToken } from '../utils/get-expires-at-for-token.js';
16
16
  import { getMessageType } from '../utils/message.js';
17
17
  import { waitForAnyMessage, waitForMessageType } from '../utils/wait-for-message.js';
18
18
  import { registerWebSocketEvents } from './hooks.js';
19
+ import cookie from 'cookie';
19
20
  const TOKEN_CHECK_INTERVAL = 15 * 60 * 1000; // 15 minutes
20
21
  const logger = useLogger();
21
22
  export default class SocketController {
@@ -96,9 +97,18 @@ export default class SocketController {
96
97
  socket.destroy();
97
98
  return;
98
99
  }
100
+ const env = useEnv();
101
+ const cookies = request.headers.cookie ? cookie.parse(request.headers.cookie) : {};
99
102
  const context = { request, socket, head };
103
+ const sessionCookieName = env['SESSION_COOKIE_NAME'];
104
+ if (cookies[sessionCookieName]) {
105
+ const token = cookies[sessionCookieName];
106
+ await this.handleTokenUpgrade(context, token);
107
+ return;
108
+ }
100
109
  if (this.authentication.mode === 'strict') {
101
- await this.handleStrictUpgrade(context, query);
110
+ const token = query['access_token'];
111
+ await this.handleTokenUpgrade(context, token);
102
112
  return;
103
113
  }
104
114
  if (this.authentication.mode === 'handshake') {
@@ -111,10 +121,9 @@ export default class SocketController {
111
121
  this.server.emit('connection', ws, state);
112
122
  });
113
123
  }
114
- async handleStrictUpgrade({ request, socket, head }, query) {
124
+ async handleTokenUpgrade({ request, socket, head }, token) {
115
125
  let accountability, expires_at;
116
126
  try {
117
- const token = query['access_token'];
118
127
  accountability = await getAccountabilityForToken(token);
119
128
  expires_at = getExpiresAtForToken(token);
120
129
  }
package/license CHANGED
@@ -1,7 +1,7 @@
1
1
  Licensor: Monospace, Inc.
2
2
 
3
3
  Licensed Work: Directus
4
- The Licensed Work is Copyright © 2023 Monospace, Inc.
4
+ The Licensed Work is Copyright © 2024 Monospace, Inc.
5
5
 
6
6
  Additional Use Grant: You may use the Licensed Work in production as long as
7
7
  your Total Finances do not exceed US $5,000,000 for the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@directus/api",
3
- "version": "18.2.0",
3
+ "version": "19.0.0",
4
4
  "description": "Directus is a real-time API and App dashboard for managing SQL database content",
5
5
  "keywords": [
6
6
  "directus",
@@ -59,12 +59,12 @@
59
59
  ],
60
60
  "dependencies": {
61
61
  "@authenio/samlify-node-xmllint": "2.0.0",
62
- "@aws-sdk/client-ses": "3.529.0",
63
- "@directus/format-title": "10.1.0",
62
+ "@aws-sdk/client-ses": "3.533.0",
64
63
  "@godaddy/terminus": "4.12.1",
65
64
  "@rollup/plugin-alias": "5.1.0",
66
65
  "@rollup/plugin-node-resolve": "15.2.3",
67
66
  "@rollup/plugin-virtual": "3.0.2",
67
+ "@types/cookie": "0.6.0",
68
68
  "argon2": "0.40.1",
69
69
  "async": "3.2.5",
70
70
  "axios": "1.6.7",
@@ -75,10 +75,11 @@
75
75
  "chokidar": "3.6.0",
76
76
  "commander": "12.0.0",
77
77
  "content-disposition": "0.5.4",
78
+ "cookie": "0.6.0",
78
79
  "cookie-parser": "1.4.6",
79
80
  "cors": "2.8.5",
80
81
  "cron-parser": "4.9.0",
81
- "date-fns": "3.3.1",
82
+ "date-fns": "3.6.0",
82
83
  "deep-diff": "1.0.2",
83
84
  "destroy": "1.2.0",
84
85
  "dotenv": "16.4.5",
@@ -86,7 +87,7 @@
86
87
  "eventemitter2": "6.4.9",
87
88
  "execa": "8.0.1",
88
89
  "exif-reader": "2.0.1",
89
- "express": "4.18.3",
90
+ "express": "4.19.2",
90
91
  "flat": "6.0.1",
91
92
  "fs-extra": "11.2.0",
92
93
  "glob-to-regexp": "0.4.1",
@@ -95,7 +96,7 @@
95
96
  "graphql-ws": "5.15.0",
96
97
  "helmet": "7.1.0",
97
98
  "icc": "3.0.0",
98
- "inquirer": "9.2.15",
99
+ "inquirer": "9.2.16",
99
100
  "ioredis": "5.3.2",
100
101
  "ip-matching": "2.1.2",
101
102
  "isolated-vm": "4.7.2",
@@ -109,7 +110,7 @@
109
110
  "ldapjs": "2.3.3",
110
111
  "liquidjs": "10.10.1",
111
112
  "lodash-es": "4.17.21",
112
- "marked": "12.0.0",
113
+ "marked": "12.0.1",
113
114
  "micromustache": "8.0.3",
114
115
  "mime-types": "2.1.35",
115
116
  "minimatch": "9.0.3",
@@ -118,7 +119,7 @@
118
119
  "nanoid": "5.0.6",
119
120
  "node-machine-id": "1.1.12",
120
121
  "node-schedule": "2.1.1",
121
- "nodemailer": "6.9.11",
122
+ "nodemailer": "6.9.13",
122
123
  "object-hash": "3.0.0",
123
124
  "openapi3-ts": "4.2.2",
124
125
  "openid-client": "5.6.5",
@@ -135,7 +136,7 @@
135
136
  "rate-limiter-flexible": "5.0.0",
136
137
  "rollup": "4.12.0",
137
138
  "samlify": "2.8.11",
138
- "sanitize-html": "2.12.1",
139
+ "sanitize-html": "2.13.0",
139
140
  "sharp": "0.33.2",
140
141
  "snappy": "7.2.2",
141
142
  "stream-json": "1.8.0",
@@ -145,28 +146,29 @@
145
146
  "ws": "8.16.0",
146
147
  "zod": "3.22.4",
147
148
  "zod-validation-error": "3.0.3",
148
- "@directus/app": "11.0.3",
149
- "@directus/env": "1.1.0",
150
- "@directus/constants": "11.0.3",
149
+ "@directus/app": "12.0.0",
150
+ "@directus/env": "1.1.1",
151
151
  "@directus/errors": "0.2.4",
152
- "@directus/extensions-registry": "1.0.1",
153
- "@directus/extensions": "1.0.1",
154
- "@directus/extensions-sdk": "11.0.1",
155
- "@directus/memory": "1.0.5",
156
- "@directus/pressure": "1.0.17",
157
- "@directus/specs": "10.2.7",
152
+ "@directus/constants": "11.0.3",
153
+ "@directus/extensions-registry": "1.0.2",
154
+ "@directus/extensions-sdk": "11.0.2",
155
+ "@directus/extensions": "1.0.2",
156
+ "@directus/format-title": "10.1.1",
157
+ "@directus/memory": "1.0.6",
158
+ "@directus/pressure": "1.0.18",
158
159
  "@directus/schema": "11.0.1",
160
+ "@directus/specs": "10.2.8",
159
161
  "@directus/storage": "10.0.11",
160
- "@directus/storage-driver-azure": "10.0.18",
161
- "@directus/storage-driver-cloudinary": "10.0.18",
162
- "@directus/storage-driver-gcs": "10.0.18",
163
- "@directus/storage-driver-s3": "10.0.19",
162
+ "@directus/storage-driver-cloudinary": "10.0.19",
163
+ "@directus/storage-driver-azure": "10.0.19",
164
+ "@directus/storage-driver-gcs": "10.0.19",
164
165
  "@directus/storage-driver-local": "10.0.18",
165
- "@directus/storage-driver-supabase": "1.0.10",
166
- "@directus/system-data": "1.0.1",
167
- "@directus/utils": "11.0.6",
168
- "directus": "10.10.3",
169
- "@directus/validation": "0.0.13"
166
+ "@directus/storage-driver-s3": "10.0.20",
167
+ "@directus/storage-driver-supabase": "1.0.11",
168
+ "@directus/utils": "11.0.7",
169
+ "@directus/system-data": "1.0.2",
170
+ "@directus/validation": "0.0.14",
171
+ "directus": "10.10.5"
170
172
  },
171
173
  "devDependencies": {
172
174
  "@ngneat/falso": "7.2.0",
@@ -191,34 +193,34 @@
191
193
  "@types/lodash-es": "4.17.12",
192
194
  "@types/mime-types": "2.1.4",
193
195
  "@types/ms": "0.7.34",
194
- "@types/node": "18.19.21",
196
+ "@types/node": "18.19.26",
195
197
  "@types/node-schedule": "2.1.6",
196
198
  "@types/nodemailer": "6.4.14",
197
199
  "@types/object-hash": "3.0.6",
198
200
  "@types/papaparse": "5.3.14",
199
- "@types/qs": "6.9.12",
201
+ "@types/qs": "6.9.14",
200
202
  "@types/sanitize-html": "2.11.0",
201
203
  "@types/stream-json": "1.7.7",
202
204
  "@types/tar": "6.1.11",
203
205
  "@types/wellknown": "0.5.8",
204
206
  "@types/ws": "8.5.10",
205
- "@vitest/coverage-v8": "1.3.1",
207
+ "@vitest/coverage-v8": "1.4.0",
206
208
  "copyfiles": "2.4.1",
207
209
  "form-data": "4.0.0",
208
210
  "knex-mock-client": "2.0.1",
209
211
  "typescript": "5.3.3",
210
212
  "vitest": "1.3.1",
211
213
  "@directus/random": "0.2.7",
212
- "@directus/types": "11.0.7",
213
- "@directus/tsconfig": "1.0.1"
214
+ "@directus/tsconfig": "1.0.1",
215
+ "@directus/types": "11.0.8"
214
216
  },
215
217
  "optionalDependencies": {
216
218
  "@keyv/redis": "2.8.4",
217
219
  "mysql": "2.18.1",
218
220
  "nodemailer-mailgun-transport": "2.1.5",
219
221
  "nodemailer-sendgrid": "1.0.3",
220
- "oracledb": "6.3.0",
221
- "pg": "8.11.3",
222
+ "oracledb": "6.4.0",
223
+ "pg": "8.11.4",
222
224
  "sqlite3": "5.1.7",
223
225
  "tedious": "17.0.0"
224
226
  },
@@ -1,4 +0,0 @@
1
- export declare function init(): Promise<void>;
2
- export declare function reload(): Promise<void>;
3
- export declare function register(): Promise<void>;
4
- export declare function unregister(): void;
package/dist/webhooks.js DELETED
@@ -1,80 +0,0 @@
1
- import { useBus } from './bus/index.js';
2
- import getDatabase from './database/index.js';
3
- import emitter from './emitter.js';
4
- import { useLogger } from './logger.js';
5
- import { getAxios } from './request/index.js';
6
- import { WebhooksService } from './services/webhooks.js';
7
- import { getSchema } from './utils/get-schema.js';
8
- import { JobQueue } from './utils/job-queue.js';
9
- let registered = [];
10
- const reloadQueue = new JobQueue();
11
- export async function init() {
12
- await register();
13
- const messenger = useBus();
14
- messenger.subscribe('webhooks', (event) => {
15
- if (event['type'] === 'reload') {
16
- reloadQueue.enqueue(async () => {
17
- await reload();
18
- });
19
- }
20
- });
21
- }
22
- export async function reload() {
23
- unregister();
24
- await register();
25
- }
26
- export async function register() {
27
- const webhookService = new WebhooksService({ knex: getDatabase(), schema: await getSchema() });
28
- const webhooks = await webhookService.readByQuery({ filter: { status: { _eq: 'active' } } });
29
- for (const webhook of webhooks) {
30
- for (const action of webhook.actions) {
31
- const event = `items.${action}`;
32
- const handler = createHandler(webhook, event);
33
- emitter.onAction(event, handler);
34
- registered.push({ event, handler });
35
- }
36
- }
37
- }
38
- export function unregister() {
39
- for (const { event, handler } of registered) {
40
- emitter.offAction(event, handler);
41
- }
42
- registered = [];
43
- }
44
- function createHandler(webhook, event) {
45
- const logger = useLogger();
46
- return async (meta, context) => {
47
- if (webhook.collections.includes(meta['collection']) === false)
48
- return;
49
- const axios = await getAxios();
50
- const webhookPayload = {
51
- event,
52
- accountability: context.accountability
53
- ? {
54
- user: context.accountability.user,
55
- role: context.accountability.role,
56
- }
57
- : null,
58
- ...meta,
59
- };
60
- try {
61
- await axios({
62
- url: webhook.url,
63
- method: webhook.method,
64
- data: webhook.data ? webhookPayload : null,
65
- headers: mergeHeaders(webhook.headers),
66
- });
67
- }
68
- catch (error) {
69
- logger.warn(`Webhook "${webhook.name}" (id: ${webhook.id}) failed`);
70
- logger.warn(error);
71
- }
72
- };
73
- }
74
- function mergeHeaders(headerArray) {
75
- const headers = {};
76
- for (const { header, value } of headerArray ?? []) {
77
- headers[header] = value;
78
- }
79
- return headers;
80
- }