directus 9.18.1 → 9.19.1

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 (47) hide show
  1. package/dist/cli/commands/roles/create.js +1 -1
  2. package/dist/cli/utils/create-env/env-stub.liquid +3 -2
  3. package/dist/controllers/collections.js +18 -0
  4. package/dist/database/run-ast.js +11 -4
  5. package/dist/database/system-data/fields/activity.yaml +2 -1
  6. package/dist/database/system-data/fields/collections.yaml +8 -4
  7. package/dist/database/system-data/fields/dashboards.yaml +6 -3
  8. package/dist/database/system-data/fields/fields.yaml +18 -9
  9. package/dist/database/system-data/fields/files.yaml +14 -7
  10. package/dist/database/system-data/fields/flows.yaml +10 -5
  11. package/dist/database/system-data/fields/folders.yaml +2 -1
  12. package/dist/database/system-data/fields/operations.yaml +8 -4
  13. package/dist/database/system-data/fields/panels.yaml +8 -4
  14. package/dist/database/system-data/fields/permissions.yaml +8 -4
  15. package/dist/database/system-data/fields/presets.yaml +10 -5
  16. package/dist/database/system-data/fields/relations.yaml +2 -1
  17. package/dist/database/system-data/fields/revisions.yaml +4 -2
  18. package/dist/database/system-data/fields/roles.yaml +12 -6
  19. package/dist/database/system-data/fields/settings.yaml +10 -5
  20. package/dist/database/system-data/fields/shares.yaml +7 -3
  21. package/dist/database/system-data/fields/users.yaml +15 -7
  22. package/dist/database/system-data/fields/webhooks.yaml +8 -4
  23. package/dist/env.js +4 -2
  24. package/dist/env.test.js +1 -0
  25. package/dist/logger.d.ts +2 -2
  26. package/dist/logger.js +10 -3
  27. package/dist/messenger.d.ts +3 -3
  28. package/dist/services/collections.d.ts +4 -0
  29. package/dist/services/collections.js +50 -5
  30. package/dist/services/graphql/index.js +80 -65
  31. package/dist/services/graphql/types/date.d.ts +1 -1
  32. package/dist/services/graphql/types/geojson.d.ts +1 -1
  33. package/dist/services/graphql/types/hash.d.ts +1 -1
  34. package/dist/services/graphql/types/string-or-float.d.ts +1 -1
  35. package/dist/services/graphql/types/void.d.ts +1 -1
  36. package/dist/services/mail/templates/base.liquid +0 -1
  37. package/dist/services/payload.js +2 -2
  38. package/dist/services/payload.test.js +102 -0
  39. package/dist/types/items.d.ts +4 -0
  40. package/dist/utils/apply-query.js +10 -2
  41. package/dist/utils/get-ast-from-query.js +1 -1
  42. package/dist/utils/get-auth-providers.d.ts +1 -0
  43. package/dist/utils/get-auth-providers.js +1 -0
  44. package/dist/utils/get-auth-providers.test.d.ts +1 -0
  45. package/dist/utils/get-auth-providers.test.js +72 -0
  46. package/dist/utils/get-relation-info.js +10 -4
  47. package/package.json +121 -122
@@ -20,7 +20,9 @@ fields:
20
20
  width: half
21
21
 
22
22
  - field: password
23
- special: hash,conceal
23
+ special:
24
+ - hash
25
+ - conceal
24
26
  interface: input-hash
25
27
  options:
26
28
  iconRight: lock
@@ -50,7 +52,8 @@ fields:
50
52
 
51
53
  - field: tags
52
54
  interface: tags
53
- special: cast-json
55
+ special:
56
+ - cast-json
54
57
  width: full
55
58
  options:
56
59
  iconRight: local_offer
@@ -89,13 +92,15 @@ fields:
89
92
 
90
93
  - field: tfa_secret
91
94
  interface: system-mfa-setup
92
- special: conceal
95
+ special:
96
+ - conceal
93
97
  width: half
94
98
 
95
99
  - field: email_notifications
96
100
  interface: boolean
97
101
  width: half
98
- special: cast-boolean
102
+ special:
103
+ - cast-boolean
99
104
 
100
105
  - field: admin_divider
101
106
  interface: presentation-divider
@@ -128,7 +133,8 @@ fields:
128
133
  interface: select-dropdown-m2o
129
134
  options:
130
135
  template: '{{ name }}'
131
- special: m2o
136
+ special:
137
+ - m2o
132
138
  width: half
133
139
  display: related-values
134
140
  display_options:
@@ -136,11 +142,13 @@ fields:
136
142
 
137
143
  - field: token
138
144
  interface: system-token
139
- special: conceal
145
+ special:
146
+ - conceal
140
147
  width: full
141
148
 
142
149
  - field: id
143
- special: uuid
150
+ special:
151
+ - uuid
144
152
  interface: input
145
153
  options:
146
154
  iconRight: vpn_key
@@ -60,12 +60,14 @@ fields:
60
60
  interface: boolean
61
61
  options:
62
62
  label: $t:fields.directus_webhooks.data_label
63
- special: cast-boolean
63
+ special:
64
+ - cast-boolean
64
65
  width: half
65
66
  display: boolean
66
67
 
67
68
  - field: headers
68
- special: cast-json
69
+ special:
70
+ - cast-json
69
71
  interface: list
70
72
  options:
71
73
  template: '{{ header }}: {{ value }}'
@@ -105,7 +107,8 @@ fields:
105
107
  value: update
106
108
  - text: $t:delete_label
107
109
  value: delete
108
- special: cast-csv
110
+ special:
111
+ - cast-csv
109
112
  width: full
110
113
  display: labels
111
114
  display_options:
@@ -129,7 +132,8 @@ fields:
129
132
 
130
133
  - field: collections
131
134
  interface: system-collections
132
- special: cast-csv
135
+ special:
136
+ - cast-csv
133
137
  width: full
134
138
  display: labels
135
139
  display_options:
package/dist/env.js CHANGED
@@ -92,6 +92,7 @@ const allowedEnvironmentVars = [
92
92
  'STORAGE_.+_ENDPOINT',
93
93
  'STORAGE_.+_ACL',
94
94
  'STORAGE_.+_CONTAINER_NAME',
95
+ 'STORAGE_.+_SERVER_SIDE_ENCRYPTION',
95
96
  'STORAGE_.+_ACCOUNT_NAME',
96
97
  'STORAGE_.+_ACCOUNT_KEY',
97
98
  'STORAGE_.+_ENDPOINT',
@@ -123,6 +124,7 @@ const allowedEnvironmentVars = [
123
124
  'AUTH_.+_ALLOW_PUBLIC_REGISTRATION',
124
125
  'AUTH_.+_DEFAULT_ROLE_ID',
125
126
  'AUTH_.+_ICON',
127
+ 'AUTH_.+_LABEL',
126
128
  'AUTH_.+_PARAMS',
127
129
  'AUTH_.+_ISSUER_URL',
128
130
  'AUTH_.+_AUTH_REQUIRE_VERIFIED_EMAIL',
@@ -186,7 +188,7 @@ const defaults = {
186
188
  HOST: '0.0.0.0',
187
189
  PORT: 8055,
188
190
  PUBLIC_URL: '/',
189
- MAX_PAYLOAD_SIZE: '100kb',
191
+ MAX_PAYLOAD_SIZE: '1mb',
190
192
  MAX_RELATIONAL_DEPTH: 10,
191
193
  DB_EXCLUDE_TABLES: 'spatial_ref_sys,sysdiagrams',
192
194
  STORAGE_LOCATIONS: 'local',
@@ -351,7 +353,7 @@ function processValues(env) {
351
353
  if (key.length > 5 && key.endsWith('_FILE')) {
352
354
  newKey = key.slice(0, -5);
353
355
  if (allowedEnvironmentVars.some((pattern) => pattern.test(newKey))) {
354
- if (newKey in env) {
356
+ if (newKey in env && !(newKey in defaults && env[newKey] === defaults[newKey])) {
355
357
  throw new Error(`Duplicate environment variable encountered: you can't use "${newKey}" and "${key}" simultaneously.`);
356
358
  }
357
359
  try {
package/dist/env.test.js CHANGED
@@ -17,6 +17,7 @@ describe('env processed values', () => {
17
17
  });
18
18
  afterEach(() => {
19
19
  process.env = originalEnv;
20
+ jest.resetAllMocks();
20
21
  });
21
22
  test('Number value should be a number', () => {
22
23
  expect(env.NUMBER).toStrictEqual(1234);
package/dist/logger.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { RequestHandler } from 'express';
2
- import pino from 'pino';
3
- declare const logger: pino.Logger;
2
+ import { LoggerOptions } from 'pino';
3
+ declare const logger: import("pino").Logger<LoggerOptions & Record<string, any>>;
4
4
  export declare const expressLogger: RequestHandler<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>;
5
5
  export default logger;
package/dist/logger.js CHANGED
@@ -33,6 +33,7 @@ const get_config_from_env_1 = require("./utils/get-config-from-env");
33
33
  const url_1 = require("url");
34
34
  const env_1 = __importDefault(require("./env"));
35
35
  const utils_1 = require("@directus/shared/utils");
36
+ const lodash_1 = require("lodash");
36
37
  const pinoOptions = {
37
38
  level: env_1.default.LOG_LEVEL || 'info',
38
39
  redact: {
@@ -41,8 +42,14 @@ const pinoOptions = {
41
42
  },
42
43
  };
43
44
  if (env_1.default.LOG_STYLE !== 'raw') {
44
- pinoOptions.prettyPrint = true;
45
- pinoOptions.prettifier = require('pino-colada');
45
+ pinoOptions.transport = {
46
+ target: 'pino-http-print',
47
+ options: {
48
+ all: true,
49
+ translateTime: 'SYS:HH:MM:ss',
50
+ relativeUrl: true,
51
+ },
52
+ };
46
53
  }
47
54
  const loggerEnvConfig = (0, get_config_from_env_1.getConfigFromEnv)('LOGGER_', 'LOGGER_HTTP');
48
55
  // Expose custom log levels into formatter function
@@ -62,7 +69,7 @@ if (loggerEnvConfig.levels) {
62
69
  };
63
70
  delete loggerEnvConfig.levels;
64
71
  }
65
- const logger = (0, pino_1.default)(Object.assign(pinoOptions, loggerEnvConfig));
72
+ const logger = (0, pino_1.default)((0, lodash_1.merge)(pinoOptions, loggerEnvConfig));
66
73
  const httpLoggerEnvConfig = (0, get_config_from_env_1.getConfigFromEnv)('LOGGER_HTTP', ['LOGGER_HTTP_LOGGER']);
67
74
  exports.expressLogger = (0, pino_http_1.default)({
68
75
  logger,
@@ -1,4 +1,4 @@
1
- import IORedis from 'ioredis';
1
+ import type { Redis } from 'ioredis';
2
2
  export declare type MessengerSubscriptionCallback = (payload: Record<string, any>) => void;
3
3
  export interface Messenger {
4
4
  publish: (channel: string, payload: Record<string, any>) => void;
@@ -14,8 +14,8 @@ export declare class MessengerMemory implements Messenger {
14
14
  }
15
15
  export declare class MessengerRedis implements Messenger {
16
16
  namespace: string;
17
- pub: IORedis.Redis;
18
- sub: IORedis.Redis;
17
+ pub: Redis;
18
+ sub: Redis;
19
19
  constructor();
20
20
  publish(channel: string, payload: Record<string, any>): void;
21
21
  subscribe(channel: string, callback: MessengerSubscriptionCallback): void;
@@ -44,6 +44,10 @@ export declare class CollectionsService {
44
44
  * Update a single collection by name
45
45
  */
46
46
  updateOne(collectionKey: string, data: Partial<Collection>, opts?: MutationOptions): Promise<string>;
47
+ /**
48
+ * Update multiple collections in a single transaction
49
+ */
50
+ updateBatch(data: Partial<Collection>[], opts?: MutationOptions): Promise<string[]>;
47
51
  /**
48
52
  * Update multiple collections by name
49
53
  */
@@ -38,6 +38,7 @@ const fields_1 = require("../services/fields");
38
38
  const items_1 = require("../services/items");
39
39
  const utils_1 = require("@directus/shared/utils");
40
40
  const helpers_1 = require("../database/helpers");
41
+ const lodash_1 = require("lodash");
41
42
  class CollectionsService {
42
43
  constructor(options) {
43
44
  this.knex = options.knex || (0, database_1.default)();
@@ -145,7 +146,9 @@ class CollectionsService {
145
146
  if (this.cache && env_1.default.CACHE_AUTO_PURGE && (opts === null || opts === void 0 ? void 0 : opts.autoPurgeCache) !== false) {
146
147
  await this.cache.clear();
147
148
  }
148
- await (0, cache_1.clearSystemCache)();
149
+ if ((opts === null || opts === void 0 ? void 0 : opts.autoPurgeSystemCache) !== false) {
150
+ await (0, cache_1.clearSystemCache)();
151
+ }
149
152
  }
150
153
  }
151
154
  /**
@@ -161,7 +164,7 @@ class CollectionsService {
161
164
  });
162
165
  const collectionNames = [];
163
166
  for (const payload of payloads) {
164
- const name = await service.createOne(payload, { autoPurgeCache: false });
167
+ const name = await service.createOne(payload, { autoPurgeCache: false, autoPurgeSystemCache: false });
165
168
  collectionNames.push(name);
166
169
  }
167
170
  return collectionNames;
@@ -297,12 +300,52 @@ class CollectionsService {
297
300
  }
298
301
  return collectionKey;
299
302
  }
303
+ finally {
304
+ if (this.cache && env_1.default.CACHE_AUTO_PURGE && (opts === null || opts === void 0 ? void 0 : opts.autoPurgeCache) !== false) {
305
+ await this.cache.clear();
306
+ }
307
+ if ((opts === null || opts === void 0 ? void 0 : opts.autoPurgeSystemCache) !== false) {
308
+ await (0, cache_1.clearSystemCache)();
309
+ }
310
+ }
311
+ }
312
+ /**
313
+ * Update multiple collections in a single transaction
314
+ */
315
+ async updateBatch(data, opts) {
316
+ if (this.accountability && this.accountability.admin !== true) {
317
+ throw new exceptions_1.ForbiddenException();
318
+ }
319
+ if (!Array.isArray(data)) {
320
+ throw new exceptions_1.InvalidPayloadException('Input should be an array of collection changes.');
321
+ }
322
+ const collectionKey = 'collection';
323
+ const collectionKeys = [];
324
+ try {
325
+ await this.knex.transaction(async (trx) => {
326
+ const collectionItemsService = new CollectionsService({
327
+ knex: trx,
328
+ accountability: this.accountability,
329
+ schema: this.schema,
330
+ });
331
+ for (const payload of data) {
332
+ if (!payload[collectionKey])
333
+ throw new exceptions_1.InvalidPayloadException(`Collection in update misses collection key.`);
334
+ await collectionItemsService.updateOne(payload[collectionKey], (0, lodash_1.omit)(payload, collectionKey), {
335
+ autoPurgeCache: false,
336
+ autoPurgeSystemCache: false,
337
+ });
338
+ collectionKeys.push(payload[collectionKey]);
339
+ }
340
+ });
341
+ }
300
342
  finally {
301
343
  if (this.cache && env_1.default.CACHE_AUTO_PURGE && (opts === null || opts === void 0 ? void 0 : opts.autoPurgeCache) !== false) {
302
344
  await this.cache.clear();
303
345
  }
304
346
  await (0, cache_1.clearSystemCache)();
305
347
  }
348
+ return collectionKeys;
306
349
  }
307
350
  /**
308
351
  * Update multiple collections by name
@@ -319,7 +362,7 @@ class CollectionsService {
319
362
  knex: trx,
320
363
  });
321
364
  for (const collectionKey of collectionKeys) {
322
- await service.updateOne(collectionKey, data, { autoPurgeCache: false });
365
+ await service.updateOne(collectionKey, data, { autoPurgeCache: false, autoPurgeSystemCache: false });
323
366
  }
324
367
  });
325
368
  return collectionKeys;
@@ -413,7 +456,9 @@ class CollectionsService {
413
456
  if (this.cache && env_1.default.CACHE_AUTO_PURGE && (opts === null || opts === void 0 ? void 0 : opts.autoPurgeCache) !== false) {
414
457
  await this.cache.clear();
415
458
  }
416
- await (0, cache_1.clearSystemCache)();
459
+ if ((opts === null || opts === void 0 ? void 0 : opts.autoPurgeSystemCache) !== false) {
460
+ await (0, cache_1.clearSystemCache)();
461
+ }
417
462
  }
418
463
  }
419
464
  /**
@@ -431,7 +476,7 @@ class CollectionsService {
431
476
  knex: trx,
432
477
  });
433
478
  for (const collectionKey of collectionKeys) {
434
- await service.deleteOne(collectionKey, { autoPurgeCache: false });
479
+ await service.deleteOne(collectionKey, { autoPurgeCache: false, autoPurgeSystemCache: false });
435
480
  }
436
481
  });
437
482
  return collectionKeys;