directus 9.21.2 → 9.22.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 (103) hide show
  1. package/dist/app.js +9 -5
  2. package/dist/app.test.d.ts +1 -0
  3. package/dist/cli/commands/bootstrap/index.js +2 -2
  4. package/dist/cli/commands/security/secret.js +2 -2
  5. package/dist/cli/utils/create-env/env-stub.liquid +3 -3
  6. package/dist/cli/utils/create-env/index.js +5 -8
  7. package/dist/constants.d.ts +1 -0
  8. package/dist/constants.js +2 -1
  9. package/dist/controllers/assets.js +9 -7
  10. package/dist/controllers/files.js +2 -1
  11. package/dist/controllers/utils.js +2 -2
  12. package/dist/database/helpers/fn/dialects/mssql.js +2 -1
  13. package/dist/database/helpers/fn/dialects/mysql.js +2 -1
  14. package/dist/database/helpers/fn/dialects/oracle.js +2 -1
  15. package/dist/database/helpers/fn/dialects/postgres.js +2 -1
  16. package/dist/database/helpers/fn/dialects/sqlite.js +2 -1
  17. package/dist/database/helpers/fn/types.d.ts +1 -0
  18. package/dist/database/helpers/fn/types.js +5 -4
  19. package/dist/database/helpers/index.d.ts +1 -1
  20. package/dist/database/helpers/schema/dialects/mssql.d.ts +6 -0
  21. package/dist/database/helpers/schema/dialects/mssql.js +14 -0
  22. package/dist/database/helpers/schema/dialects/mysql.d.ts +5 -0
  23. package/dist/database/helpers/schema/dialects/mysql.js +19 -0
  24. package/dist/database/helpers/schema/dialects/oracle.d.ts +1 -0
  25. package/dist/database/helpers/schema/dialects/oracle.js +3 -0
  26. package/dist/database/helpers/schema/index.d.ts +2 -2
  27. package/dist/database/helpers/schema/index.js +4 -4
  28. package/dist/database/helpers/schema/types.d.ts +5 -0
  29. package/dist/database/helpers/schema/types.js +13 -0
  30. package/dist/database/index.d.ts +6 -0
  31. package/dist/database/index.js +20 -1
  32. package/dist/database/migrations/20211007A-update-presets.js +2 -2
  33. package/dist/database/migrations/run.js +7 -31
  34. package/dist/database/run-ast.js +132 -6
  35. package/dist/database/system-data/fields/index.js +2 -1
  36. package/dist/env.js +3 -2
  37. package/dist/exceptions/range-not-satisfiable.d.ts +1 -1
  38. package/dist/extensions.d.ts +7 -1
  39. package/dist/extensions.js +41 -15
  40. package/dist/logger.js +27 -1
  41. package/dist/operations/request/index.d.ts +1 -2
  42. package/dist/operations/request/index.js +2 -2
  43. package/dist/services/assets.d.ts +4 -3
  44. package/dist/services/assets.js +13 -11
  45. package/dist/services/authentication.js +4 -3
  46. package/dist/services/authorization.js +1 -1
  47. package/dist/services/files.d.ts +4 -3
  48. package/dist/services/files.js +92 -68
  49. package/dist/services/flows.d.ts +0 -2
  50. package/dist/services/flows.js +0 -14
  51. package/dist/services/flows.test.d.ts +1 -0
  52. package/dist/services/graphql/index.d.ts +5 -1
  53. package/dist/services/graphql/index.js +29 -31
  54. package/dist/services/import-export.d.ts +4 -3
  55. package/dist/services/items.js +7 -1
  56. package/dist/services/meta.js +2 -2
  57. package/dist/services/operations.d.ts +0 -2
  58. package/dist/services/operations.js +0 -12
  59. package/dist/services/operations.test.d.ts +1 -0
  60. package/dist/services/permissions.d.ts +0 -5
  61. package/dist/services/permissions.js +0 -25
  62. package/dist/services/permissions.test.d.ts +1 -0
  63. package/dist/services/roles.js +0 -3
  64. package/dist/services/roles.test.d.ts +1 -0
  65. package/dist/services/server.js +8 -6
  66. package/dist/services/shares.js +2 -2
  67. package/dist/services/specifications.js +12 -1
  68. package/dist/services/webhooks.d.ts +0 -2
  69. package/dist/services/webhooks.js +0 -10
  70. package/dist/services/webhooks.test.d.ts +1 -0
  71. package/dist/storage/get-storage-driver.d.ts +3 -0
  72. package/dist/storage/get-storage-driver.js +20 -0
  73. package/dist/storage/get-storage-driver.test.d.ts +1 -0
  74. package/dist/storage/index.d.ts +5 -0
  75. package/dist/storage/index.js +20 -0
  76. package/dist/storage/index.test.d.ts +1 -0
  77. package/dist/storage/register-drivers.d.ts +2 -0
  78. package/dist/storage/register-drivers.js +22 -0
  79. package/dist/storage/register-drivers.test.d.ts +1 -0
  80. package/dist/storage/register-locations.d.ts +2 -0
  81. package/dist/storage/register-locations.js +17 -0
  82. package/dist/storage/register-locations.test.d.ts +1 -0
  83. package/dist/utils/apply-query.d.ts +27 -3
  84. package/dist/utils/apply-query.js +180 -127
  85. package/dist/utils/dynamic-import.d.ts +1 -0
  86. package/dist/utils/dynamic-import.js +7 -0
  87. package/dist/utils/get-collection-from-alias.d.ts +6 -0
  88. package/dist/utils/get-collection-from-alias.js +15 -0
  89. package/dist/utils/get-collection-from-alias.test.d.ts +1 -0
  90. package/dist/utils/get-column-path.d.ts +14 -8
  91. package/dist/utils/get-column-path.js +24 -7
  92. package/dist/utils/get-column.d.ts +8 -1
  93. package/dist/utils/get-column.js +10 -3
  94. package/dist/utils/get-config-from-env.js +3 -2
  95. package/dist/utils/get-default-value.d.ts +1 -1
  96. package/dist/utils/parse-image-metadata.d.ts +3 -0
  97. package/dist/utils/parse-image-metadata.js +73 -0
  98. package/dist/utils/track.js +2 -2
  99. package/dist/utils/validate-env.js +3 -2
  100. package/dist/webhooks.js +2 -2
  101. package/package.json +17 -11
  102. package/dist/storage.d.ts +0 -3
  103. package/dist/storage.js +0 -61
@@ -1,37 +1,16 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
26
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
27
4
  };
28
5
  Object.defineProperty(exports, "__esModule", { value: true });
29
- const format_title_1 = __importDefault(require("@directus/format-title"));
30
6
  const fs_extra_1 = __importDefault(require("fs-extra"));
7
+ const lodash_1 = require("lodash");
31
8
  const path_1 = __importDefault(require("path"));
32
9
  const env_1 = __importDefault(require("../../env"));
33
10
  const logger_1 = __importDefault(require("../../logger"));
34
- const lodash_1 = require("lodash");
11
+ const dynamic_import_1 = require("../../utils/dynamic-import");
12
+ // @ts-ignore
13
+ const format_title_1 = __importDefault(require("@directus/format-title"));
35
14
  async function run(database, direction, log = true) {
36
15
  let migrationFiles = await fs_extra_1.default.readdir(__dirname);
37
16
  const customMigrationsPath = path_1.default.resolve(env_1.default.EXTENSIONS_PATH, 'migrations');
@@ -65,7 +44,6 @@ async function run(database, direction, log = true) {
65
44
  if (direction === 'latest')
66
45
  await latest();
67
46
  async function up() {
68
- var _a;
69
47
  const currentVersion = completedMigrations[completedMigrations.length - 1];
70
48
  let nextVersion;
71
49
  if (!currentVersion) {
@@ -79,7 +57,7 @@ async function run(database, direction, log = true) {
79
57
  if (!nextVersion) {
80
58
  throw Error('Nothing to upgrade');
81
59
  }
82
- const { up } = await (_a = nextVersion.file, Promise.resolve().then(() => __importStar(require(_a))));
60
+ const { up } = await (0, dynamic_import_1.dynamicImport)(nextVersion.file);
83
61
  if (log) {
84
62
  logger_1.default.info(`Applying ${nextVersion.name}...`);
85
63
  }
@@ -87,7 +65,6 @@ async function run(database, direction, log = true) {
87
65
  await database.insert({ version: nextVersion.version, name: nextVersion.name }).into('directus_migrations');
88
66
  }
89
67
  async function down() {
90
- var _a;
91
68
  const lastAppliedMigration = (0, lodash_1.orderBy)(completedMigrations, ['timestamp', 'version'], ['desc', 'desc'])[0];
92
69
  if (!lastAppliedMigration) {
93
70
  throw Error('Nothing to downgrade');
@@ -96,7 +73,7 @@ async function run(database, direction, log = true) {
96
73
  if (!migration) {
97
74
  throw new Error("Couldn't find migration");
98
75
  }
99
- const { down } = await (_a = migration.file, Promise.resolve().then(() => __importStar(require(_a))));
76
+ const { down } = await (0, dynamic_import_1.dynamicImport)(migration.file);
100
77
  if (log) {
101
78
  logger_1.default.info(`Undoing ${migration.name}...`);
102
79
  }
@@ -104,10 +81,9 @@ async function run(database, direction, log = true) {
104
81
  await database('directus_migrations').delete().where({ version: migration.version });
105
82
  }
106
83
  async function latest() {
107
- var _a;
108
84
  for (const migration of migrations) {
109
85
  if (migration.completed === false) {
110
- const { up } = await (_a = migration.file, Promise.resolve().then(() => __importStar(require(_a))));
86
+ const { up } = await (0, dynamic_import_1.dynamicImport)(migration.file);
111
87
  if (log) {
112
88
  logger_1.default.info(`Applying ${migration.name}...`);
113
89
  }
@@ -1,4 +1,27 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
2
25
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
27
  };
@@ -10,7 +33,8 @@ const helpers_1 = require("../database/helpers");
10
33
  const env_1 = __importDefault(require("../env"));
11
34
  const payload_1 = require("../services/payload");
12
35
  const apply_function_to_column_name_1 = require("../utils/apply-function-to-column-name");
13
- const apply_query_1 = __importDefault(require("../utils/apply-query"));
36
+ const apply_query_1 = __importStar(require("../utils/apply-query"));
37
+ const get_collection_from_alias_1 = require("../utils/get-collection-from-alias");
14
38
  const get_column_1 = require("../utils/get-column");
15
39
  const strip_function_1 = require("../utils/strip-function");
16
40
  /**
@@ -33,7 +57,7 @@ async function runAST(originalAST, schema, options) {
33
57
  // Retrieve the database columns to select in the current AST
34
58
  const { fieldNodes, primaryKeyField, nestedCollectionNodes } = await parseCurrentLevel(schema, collection, children, query);
35
59
  // The actual knex query builder instance. This is a promise that resolves with the raw items from the db
36
- const dbQuery = getDBQuery(schema, knex, collection, fieldNodes, query);
60
+ const dbQuery = await getDBQuery(schema, knex, collection, fieldNodes, query);
37
61
  const rawItems = await dbQuery;
38
62
  if (!rawItems)
39
63
  return null;
@@ -152,17 +176,119 @@ function getColumnPreprocessor(knex, schema, table) {
152
176
  return helpers.st.asText(table, field.field);
153
177
  }
154
178
  if (fieldNode.type === 'functionField') {
155
- return (0, get_column_1.getColumn)(knex, table, fieldNode.name, alias, schema, fieldNode.query);
179
+ return (0, get_column_1.getColumn)(knex, table, fieldNode.name, alias, schema, { query: fieldNode.query });
156
180
  }
157
181
  return (0, get_column_1.getColumn)(knex, table, fieldNode.name, alias, schema);
158
182
  };
159
183
  }
160
- function getDBQuery(schema, knex, table, fieldNodes, query) {
184
+ async function getDBQuery(schema, knex, table, fieldNodes, query) {
161
185
  const preProcess = getColumnPreprocessor(knex, schema, table);
162
- const dbQuery = knex.select(fieldNodes.map(preProcess)).from(table);
163
186
  const queryCopy = (0, lodash_1.clone)(query);
187
+ const helpers = (0, helpers_1.getHelpers)(knex);
164
188
  queryCopy.limit = typeof queryCopy.limit === 'number' ? queryCopy.limit : 100;
165
- return (0, apply_query_1.default)(knex, table, dbQuery, queryCopy, schema);
189
+ // Queries with aggregates and groupBy will not have duplicate result
190
+ if (queryCopy.aggregate || queryCopy.group) {
191
+ const flatQuery = knex.select(fieldNodes.map(preProcess)).from(table);
192
+ return await (0, apply_query_1.default)(knex, table, flatQuery, queryCopy, schema).query;
193
+ }
194
+ const primaryKey = schema.collections[table].primary;
195
+ const aliasMap = Object.create(null);
196
+ let dbQuery = knex.from(table);
197
+ let sortRecords;
198
+ const innerQuerySortRecords = [];
199
+ let hasMultiRelationalSort;
200
+ if (queryCopy.sort) {
201
+ const sortResult = (0, apply_query_1.applySort)(knex, schema, dbQuery, queryCopy.sort, table, aliasMap, true);
202
+ if (sortResult) {
203
+ sortRecords = sortResult.sortRecords;
204
+ hasMultiRelationalSort = sortResult.hasMultiRelationalSort;
205
+ }
206
+ }
207
+ const { hasMultiRelationalFilter } = await (0, apply_query_1.default)(knex, table, dbQuery, queryCopy, schema, {
208
+ aliasMap,
209
+ isInnerQuery: true,
210
+ hasMultiRelationalSort,
211
+ });
212
+ const needsInnerQuery = hasMultiRelationalSort || hasMultiRelationalFilter;
213
+ if (needsInnerQuery) {
214
+ dbQuery.select(`${table}.${primaryKey}`).distinct();
215
+ }
216
+ else {
217
+ dbQuery.select(fieldNodes.map(preProcess));
218
+ }
219
+ if (sortRecords) {
220
+ if (needsInnerQuery) {
221
+ sortRecords.map((sortRecord) => {
222
+ const sortAlias = `sort_${(0, apply_query_1.generateAlias)()}`;
223
+ if (sortRecord.column.includes('.')) {
224
+ const [alias, field] = sortRecord.column.split('.');
225
+ dbQuery.select((0, get_column_1.getColumn)(knex, alias, field, sortAlias, schema, {
226
+ originalCollectionName: (0, get_collection_from_alias_1.getCollectionFromAlias)(alias, aliasMap),
227
+ }));
228
+ }
229
+ else {
230
+ dbQuery.select((0, get_column_1.getColumn)(knex, table, sortRecord.column, sortAlias, schema));
231
+ }
232
+ innerQuerySortRecords.push({ alias: sortAlias, order: sortRecord.order });
233
+ });
234
+ if (hasMultiRelationalSort) {
235
+ let orderByString = '';
236
+ const orderByFields = [];
237
+ sortRecords.map((sortRecord) => {
238
+ if (orderByString.length === 0) {
239
+ orderByString += ' order by';
240
+ }
241
+ else {
242
+ orderByString += ',';
243
+ }
244
+ if (sortRecord.column.includes('.')) {
245
+ orderByString += ` ?? ${sortRecord.order}`;
246
+ const [alias, field] = sortRecord.column.split('.');
247
+ orderByFields.push((0, get_column_1.getColumn)(knex, alias, field, false, schema, {
248
+ originalCollectionName: (0, get_collection_from_alias_1.getCollectionFromAlias)(alias, aliasMap),
249
+ }));
250
+ }
251
+ else {
252
+ orderByString += ` ?? ${sortRecord.order}`;
253
+ orderByFields.push((0, get_column_1.getColumn)(knex, table, sortRecord.column, false, schema));
254
+ }
255
+ });
256
+ dbQuery = helpers.schema.applyMultiRelationalSort(knex, dbQuery, table, primaryKey, orderByString, orderByFields);
257
+ }
258
+ }
259
+ else {
260
+ // Clears the order if any, eg: from MSSQL offset
261
+ dbQuery.clear('order');
262
+ sortRecords.map((sortRecord) => {
263
+ if (sortRecord.column.includes('.')) {
264
+ const [alias, field] = sortRecord.column.split('.');
265
+ sortRecord.column = (0, get_column_1.getColumn)(knex, alias, field, false, schema, {
266
+ originalCollectionName: (0, get_collection_from_alias_1.getCollectionFromAlias)(alias, aliasMap),
267
+ });
268
+ }
269
+ else {
270
+ sortRecord.column = (0, get_column_1.getColumn)(knex, table, sortRecord.column, false, schema);
271
+ }
272
+ });
273
+ dbQuery.orderBy(sortRecords);
274
+ }
275
+ }
276
+ if (!needsInnerQuery)
277
+ return dbQuery;
278
+ const wrapperQuery = knex
279
+ .select(fieldNodes.map(preProcess))
280
+ .from(table)
281
+ .innerJoin(knex.raw('??', dbQuery.as('inner')), `${table}.${primaryKey}`, `inner.${primaryKey}`);
282
+ if (sortRecords && needsInnerQuery) {
283
+ innerQuerySortRecords.map((innerQuerySortRecord) => {
284
+ wrapperQuery.orderBy(`inner.${innerQuerySortRecord.alias}`, innerQuerySortRecord.order);
285
+ });
286
+ if (hasMultiRelationalSort) {
287
+ wrapperQuery.where('inner.directus_row_number', '=', 1);
288
+ (0, apply_query_1.applyLimit)(wrapperQuery, queryCopy.limit);
289
+ }
290
+ }
291
+ return wrapperQuery;
166
292
  }
167
293
  function applyParentFilters(schema, nestedCollectionNodes, parentItem) {
168
294
  var _a;
@@ -7,9 +7,10 @@ exports.systemFieldRows = void 0;
7
7
  const fs_extra_1 = __importDefault(require("fs-extra"));
8
8
  const lodash_1 = require("lodash");
9
9
  const path_1 = __importDefault(require("path"));
10
- const format_title_1 = __importDefault(require("@directus/format-title"));
11
10
  const get_auth_providers_1 = require("../../../utils/get-auth-providers");
12
11
  const require_yaml_1 = require("../../../utils/require-yaml");
12
+ // @ts-ignore
13
+ const format_title_1 = __importDefault(require("@directus/format-title"));
13
14
  const defaults = (0, require_yaml_1.requireYAML)(require.resolve('./_defaults.yaml'));
14
15
  const fieldData = fs_extra_1.default.readdirSync(path_1.default.resolve(__dirname));
15
16
  exports.systemFieldRows = [];
package/dist/env.js CHANGED
@@ -14,7 +14,6 @@ const lodash_1 = require("lodash");
14
14
  const path_1 = __importDefault(require("path"));
15
15
  const require_yaml_1 = require("./utils/require-yaml");
16
16
  const utils_1 = require("@directus/shared/utils");
17
- const utils_2 = require("@directus/shared/utils");
18
17
  // keeping this here for now to prevent a circular import to constants.ts
19
18
  const allowedEnvironmentVars = [
20
19
  // general
@@ -107,6 +106,7 @@ const allowedEnvironmentVars = [
107
106
  'ASSETS_TRANSFORM_IMAGE_MAX_DIMENSION',
108
107
  'ASSETS_TRANSFORM_MAX_OPERATIONS',
109
108
  'ASSETS_CONTENT_SECURITY_POLICY',
109
+ 'ASSETS_INVALID_IMAGE_SENSITIVITY_LEVEL',
110
110
  // auth
111
111
  'AUTH_PROVIDERS',
112
112
  'AUTH_DISABLE_DEFAULT',
@@ -237,6 +237,7 @@ const defaults = {
237
237
  ASSETS_TRANSFORM_MAX_CONCURRENT: 1,
238
238
  ASSETS_TRANSFORM_IMAGE_MAX_DIMENSION: 6000,
239
239
  ASSETS_TRANSFORM_MAX_OPERATIONS: 5,
240
+ ASSETS_INVALID_IMAGE_SENSITIVITY_LEVEL: 'warning',
240
241
  IP_TRUST_PROXY: true,
241
242
  IP_CUSTOM_HEADER: false,
242
243
  IMPORT_IP_DENY_LIST: '0.0.0.0',
@@ -437,7 +438,7 @@ function processValues(env) {
437
438
  }
438
439
  function tryJSON(value) {
439
440
  try {
440
- return (0, utils_2.parseJSON)(value);
441
+ return (0, utils_1.parseJSON)(value);
441
442
  }
442
443
  catch {
443
444
  return value;
@@ -1,5 +1,5 @@
1
1
  import { BaseException } from '@directus/shared/exceptions';
2
- import { Range } from '@directus/drive';
2
+ import type { Range } from '@directus/storage';
3
3
  export declare class RangeNotSatisfiableException extends BaseException {
4
4
  constructor(range?: Range);
5
5
  }
@@ -1,5 +1,5 @@
1
- import { Router } from 'express';
2
1
  import { ExtensionType } from '@directus/shared/types';
2
+ import { Router } from 'express';
3
3
  export declare function getExtensionManager(): ExtensionManager;
4
4
  type Options = {
5
5
  schedule: boolean;
@@ -14,6 +14,8 @@ declare class ExtensionManager {
14
14
  private apiEmitter;
15
15
  private hookEvents;
16
16
  private endpointRouter;
17
+ private hookEmbedsHead;
18
+ private hookEmbedsBody;
17
19
  private reloadQueue;
18
20
  private watcher;
19
21
  constructor();
@@ -22,6 +24,10 @@ declare class ExtensionManager {
22
24
  getExtensionsList(type?: ExtensionType): string[];
23
25
  getAppExtensions(): string | null;
24
26
  getEndpointRouter(): Router;
27
+ getEmbeds(): {
28
+ head: string;
29
+ body: string;
30
+ };
25
31
  private load;
26
32
  private unload;
27
33
  private initializeWatcher;
@@ -27,31 +27,32 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
27
27
  };
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
29
  exports.getExtensionManager = void 0;
30
+ const constants_1 = require("@directus/shared/constants");
31
+ const sharedExceptions = __importStar(require("@directus/shared/exceptions"));
32
+ const node_1 = require("@directus/shared/utils/node");
30
33
  const express_1 = __importStar(require("express"));
34
+ const fs_extra_1 = __importDefault(require("fs-extra"));
31
35
  const path_1 = __importDefault(require("path"));
32
- const node_1 = require("@directus/shared/utils/node");
33
- const constants_1 = require("@directus/shared/constants");
34
36
  const database_1 = __importDefault(require("./database"));
35
37
  const emitter_1 = __importStar(require("./emitter"));
36
38
  const env_1 = __importDefault(require("./env"));
37
39
  const exceptions = __importStar(require("./exceptions"));
38
- const sharedExceptions = __importStar(require("@directus/shared/exceptions"));
39
40
  const logger_1 = __importDefault(require("./logger"));
40
- const fs_extra_1 = __importDefault(require("fs-extra"));
41
+ const dynamic_import_1 = require("./utils/dynamic-import");
41
42
  const get_schema_1 = require("./utils/get-schema");
42
- const services = __importStar(require("./services"));
43
- const node_cron_1 = require("node-cron");
44
- const rollup_1 = require("rollup");
45
- const plugin_virtual_1 = __importDefault(require("@rollup/plugin-virtual"));
43
+ const utils_1 = require("@directus/shared/utils");
46
44
  const plugin_alias_1 = __importDefault(require("@rollup/plugin-alias"));
47
- const url_1 = require("./utils/url");
48
- const get_module_default_1 = __importDefault(require("./utils/get-module-default"));
49
- const lodash_1 = require("lodash");
45
+ const plugin_virtual_1 = __importDefault(require("@rollup/plugin-virtual"));
50
46
  const chokidar_1 = __importDefault(require("chokidar"));
51
- const utils_1 = require("@directus/shared/utils");
52
- const flows_1 = require("./flows");
53
47
  const globby_1 = __importDefault(require("globby"));
48
+ const lodash_1 = require("lodash");
49
+ const node_cron_1 = require("node-cron");
50
+ const rollup_1 = require("rollup");
51
+ const flows_1 = require("./flows");
52
+ const services = __importStar(require("./services"));
53
+ const get_module_default_1 = __importDefault(require("./utils/get-module-default"));
54
54
  const job_queue_1 = require("./utils/job-queue");
55
+ const url_1 = require("./utils/url");
55
56
  let extensionManager;
56
57
  function getExtensionManager() {
57
58
  if (extensionManager) {
@@ -72,6 +73,8 @@ class ExtensionManager {
72
73
  this.appExtensions = null;
73
74
  this.apiExtensions = [];
74
75
  this.hookEvents = [];
76
+ this.hookEmbedsHead = [];
77
+ this.hookEmbedsBody = [];
75
78
  this.watcher = null;
76
79
  this.options = defaultOptions;
77
80
  this.apiEmitter = new emitter_1.Emitter();
@@ -139,6 +142,17 @@ class ExtensionManager {
139
142
  getEndpointRouter() {
140
143
  return this.endpointRouter;
141
144
  }
145
+ getEmbeds() {
146
+ return {
147
+ head: wrapEmbeds('Custom Embed Head', this.hookEmbedsHead),
148
+ body: wrapEmbeds('Custom Embed Body', this.hookEmbedsBody),
149
+ };
150
+ function wrapEmbeds(label, content) {
151
+ if (content.length === 0)
152
+ return '';
153
+ return `<!-- Start ${label} -->\n${content.join('\n')}\n<!-- End ${label} -->`;
154
+ }
155
+ }
142
156
  async load() {
143
157
  try {
144
158
  await (0, node_1.ensureExtensionDirs)(env_1.default.EXTENSIONS_PATH, env_1.default.SERVE_APP ? constants_1.EXTENSION_TYPES : constants_1.API_OR_HYBRID_EXTENSION_TYPES);
@@ -252,12 +266,11 @@ class ExtensionManager {
252
266
  return depsMapping;
253
267
  }
254
268
  async registerHooks() {
255
- var _a;
256
269
  const hooks = this.extensions.filter((extension) => extension.type === 'hook');
257
270
  for (const hook of hooks) {
258
271
  try {
259
272
  const hookPath = path_1.default.resolve(hook.path, hook.entrypoint);
260
- const hookInstance = await (_a = hookPath, Promise.resolve().then(() => __importStar(require(_a))));
273
+ const hookInstance = await (0, dynamic_import_1.dynamicImport)(hookPath);
261
274
  const config = (0, get_module_default_1.default)(hookInstance);
262
275
  this.registerHook(config);
263
276
  this.apiExtensions.push({ path: hookPath });
@@ -380,6 +393,19 @@ class ExtensionManager {
380
393
  logger_1.default.warn(`Couldn't register cron hook. Provided cron is invalid: ${cron}`);
381
394
  }
382
395
  },
396
+ embed: (position, code) => {
397
+ const content = typeof code === 'function' ? code() : code;
398
+ if (content.trim().length === 0) {
399
+ logger_1.default.warn(`Couldn't register embed hook. Provided code is empty!`);
400
+ return;
401
+ }
402
+ if (position === 'head') {
403
+ this.hookEmbedsHead.push(content);
404
+ }
405
+ if (position === 'body') {
406
+ this.hookEmbedsBody.push(content);
407
+ }
408
+ },
383
409
  };
384
410
  register(registerFunctions, {
385
411
  services,
package/dist/logger.js CHANGED
@@ -41,13 +41,31 @@ const pinoOptions = {
41
41
  censor: '--redact--',
42
42
  },
43
43
  };
44
+ const httpLoggerOptions = {
45
+ level: env_1.default.LOG_LEVEL || 'info',
46
+ redact: {
47
+ paths: ['req.headers.authorization', `req.cookies.${env_1.default.REFRESH_TOKEN_COOKIE_NAME}`],
48
+ censor: '--redact--',
49
+ },
50
+ };
44
51
  if (env_1.default.LOG_STYLE !== 'raw') {
45
52
  pinoOptions.transport = {
53
+ target: 'pino-pretty',
54
+ options: {
55
+ ignore: 'hostname,pid',
56
+ sync: true,
57
+ },
58
+ };
59
+ httpLoggerOptions.transport = {
46
60
  target: 'pino-http-print',
47
61
  options: {
48
62
  all: true,
49
63
  translateTime: 'SYS:HH:MM:ss',
50
64
  relativeUrl: true,
65
+ prettyOptions: {
66
+ ignore: 'hostname,pid',
67
+ sync: true,
68
+ },
51
69
  },
52
70
  };
53
71
  }
@@ -67,12 +85,20 @@ if (loggerEnvConfig.levels) {
67
85
  };
68
86
  },
69
87
  };
88
+ httpLoggerOptions.formatters = {
89
+ level(label, number) {
90
+ return {
91
+ severity: customLogLevels[label] || 'info',
92
+ level: number,
93
+ };
94
+ },
95
+ };
70
96
  delete loggerEnvConfig.levels;
71
97
  }
72
98
  const logger = (0, pino_1.default)((0, lodash_1.merge)(pinoOptions, loggerEnvConfig));
73
99
  const httpLoggerEnvConfig = (0, get_config_from_env_1.getConfigFromEnv)('LOGGER_HTTP', ['LOGGER_HTTP_LOGGER']);
74
100
  exports.expressLogger = (0, pino_http_1.default)({
75
- logger,
101
+ logger: (0, pino_1.default)((0, lodash_1.merge)(httpLoggerOptions, loggerEnvConfig)),
76
102
  ...httpLoggerEnvConfig,
77
103
  serializers: {
78
104
  req(request) {
@@ -1,7 +1,6 @@
1
- import { Method } from 'axios';
2
1
  type Options = {
3
2
  url: string;
4
- method: Method;
3
+ method: string;
5
4
  body: Record<string, any> | string | null;
6
5
  headers?: {
7
6
  header: string;
@@ -4,12 +4,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const utils_1 = require("@directus/shared/utils");
7
- const axios_1 = __importDefault(require("axios"));
8
7
  const encodeurl_1 = __importDefault(require("encodeurl"));
9
8
  exports.default = (0, utils_1.defineOperationApi)({
10
9
  id: 'request',
11
10
  handler: async ({ url, method, body, headers }) => {
12
11
  var _a;
12
+ const axios = (await import('axios')).default;
13
13
  const customHeaders = (_a = headers === null || headers === void 0 ? void 0 : headers.reduce((acc, { header, value }) => {
14
14
  acc[header] = value;
15
15
  return acc;
@@ -17,7 +17,7 @@ exports.default = (0, utils_1.defineOperationApi)({
17
17
  if (!customHeaders['Content-Type'] && isValidJSON(body)) {
18
18
  customHeaders['Content-Type'] = 'application/json';
19
19
  }
20
- const result = await (0, axios_1.default)({
20
+ const result = await axios({
21
21
  url: (0, encodeurl_1.default)(url),
22
22
  method,
23
23
  data: body,
@@ -1,6 +1,7 @@
1
1
  /// <reference types="node" />
2
- import { Range, StatResponse } from '@directus/drive';
2
+ import type { Range, Stat } from '@directus/storage';
3
3
  import { Accountability } from '@directus/shared/types';
4
+ import type { Readable } from 'node:stream';
4
5
  import { Knex } from 'knex';
5
6
  import { AbstractServiceOptions, TransformationParams, TransformationPreset } from '../types';
6
7
  import { AuthorizationService } from './authorization';
@@ -10,8 +11,8 @@ export declare class AssetsService {
10
11
  authorizationService: AuthorizationService;
11
12
  constructor(options: AbstractServiceOptions);
12
13
  getAsset(id: string, transformation: TransformationParams | TransformationPreset, range?: Range): Promise<{
13
- stream: NodeJS.ReadableStream;
14
+ stream: Readable;
14
15
  file: any;
15
- stat: StatResponse;
16
+ stat: Stat;
16
17
  }>;
17
18
  }
@@ -37,7 +37,7 @@ const database_1 = __importDefault(require("../database"));
37
37
  const env_1 = __importDefault(require("../env"));
38
38
  const exceptions_1 = require("../exceptions");
39
39
  const logger_1 = __importDefault(require("../logger"));
40
- const storage_1 = __importDefault(require("../storage"));
40
+ const storage_1 = require("../storage");
41
41
  const TransformationUtils = __importStar(require("../utils/transformations"));
42
42
  const authorization_1 = require("./authorization");
43
43
  sharp_1.default.concurrency(1);
@@ -52,6 +52,7 @@ class AssetsService {
52
52
  }
53
53
  async getAsset(id, transformation, range) {
54
54
  var _a;
55
+ const storage = await (0, storage_1.getStorage)();
55
56
  const publicSettings = await this.knex
56
57
  .select('project_logo', 'public_background', 'public_foreground')
57
58
  .from('directus_settings')
@@ -71,7 +72,7 @@ class AssetsService {
71
72
  const file = (await this.knex.select('*').from('directus_files').where({ id }).first());
72
73
  if (!file)
73
74
  throw new exceptions_1.ForbiddenException();
74
- const { exists } = await storage_1.default.disk(file.storage).exists(file.filename_disk);
75
+ const exists = await storage.location(file.storage).exists(file.filename_disk);
75
76
  if (!exists)
76
77
  throw new exceptions_1.ForbiddenException();
77
78
  if (range) {
@@ -113,15 +114,15 @@ class AssetsService {
113
114
  const assetFilename = path_1.default.basename(file.filename_disk, path_1.default.extname(file.filename_disk)) +
114
115
  getAssetSuffix(transforms) +
115
116
  (maybeNewFormat ? `.${maybeNewFormat}` : path_1.default.extname(file.filename_disk));
116
- const { exists } = await storage_1.default.disk(file.storage).exists(assetFilename);
117
+ const exists = await storage.location(file.storage).exists(assetFilename);
117
118
  if (maybeNewFormat) {
118
119
  file.type = (0, mime_types_1.contentType)(assetFilename) || null;
119
120
  }
120
121
  if (exists) {
121
122
  return {
122
- stream: storage_1.default.disk(file.storage).getStream(assetFilename, range),
123
+ stream: await storage.location(file.storage).read(assetFilename, range),
123
124
  file,
124
- stat: await storage_1.default.disk(file.storage).getStat(assetFilename),
125
+ stat: await storage.location(file.storage).stat(assetFilename),
125
126
  };
126
127
  }
127
128
  // Check image size before transforming. Processing an image that's too large for the
@@ -135,10 +136,11 @@ class AssetsService {
135
136
  throw new exceptions_1.IllegalAssetTransformation(`Image is too large to be transformed, or image size couldn't be determined.`);
136
137
  }
137
138
  return await semaphore.runExclusive(async () => {
138
- const readStream = storage_1.default.disk(file.storage).getStream(file.filename_disk, range);
139
+ const readStream = await storage.location(file.storage).read(file.filename_disk, range);
139
140
  const transformer = (0, sharp_1.default)({
140
141
  limitInputPixels: Math.pow(env_1.default.ASSETS_TRANSFORM_IMAGE_MAX_DIMENSION, 2),
141
142
  sequentialRead: true,
143
+ failOn: env_1.default.ASSETS_INVALID_IMAGE_SENSITIVITY_LEVEL,
142
144
  });
143
145
  if (transforms.find((transform) => transform[0] === 'rotate') === undefined)
144
146
  transformer.rotate();
@@ -147,17 +149,17 @@ class AssetsService {
147
149
  logger_1.default.error(e, `Couldn't transform file ${file.id}`);
148
150
  readStream.unpipe(transformer);
149
151
  });
150
- await storage_1.default.disk(file.storage).put(assetFilename, readStream.pipe(transformer), type);
152
+ await storage.location(file.storage).write(assetFilename, readStream.pipe(transformer), type);
151
153
  return {
152
- stream: storage_1.default.disk(file.storage).getStream(assetFilename, range),
153
- stat: await storage_1.default.disk(file.storage).getStat(assetFilename),
154
+ stream: await storage.location(file.storage).read(assetFilename, range),
155
+ stat: await storage.location(file.storage).stat(assetFilename),
154
156
  file,
155
157
  };
156
158
  });
157
159
  }
158
160
  else {
159
- const readStream = storage_1.default.disk(file.storage).getStream(file.filename_disk, range);
160
- const stat = await storage_1.default.disk(file.storage).getStat(file.filename_disk);
161
+ const readStream = await storage.location(file.storage).read(file.filename_disk, range);
162
+ const stat = await storage.location(file.storage).stat(file.filename_disk);
161
163
  return { stream: readStream, file, stat };
162
164
  }
163
165
  }