directus 9.4.2 → 9.5.2

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 (124) hide show
  1. package/README.md +1 -1
  2. package/dist/app.js +9 -0
  3. package/dist/auth/auth.d.ts +2 -1
  4. package/dist/auth/drivers/oauth2.js +10 -20
  5. package/dist/auth/drivers/openid.js +18 -8
  6. package/dist/cli/commands/init/index.js +8 -0
  7. package/dist/cli/commands/init/questions.d.ts +3 -0
  8. package/dist/cli/commands/init/questions.js +2 -0
  9. package/dist/cli/index.js +1 -1
  10. package/dist/cli/utils/create-db-connection.d.ts +1 -1
  11. package/dist/cli/utils/create-db-connection.js +11 -1
  12. package/dist/cli/utils/drivers.d.ts +1 -0
  13. package/dist/cli/utils/drivers.js +2 -1
  14. package/dist/controllers/extensions.js +1 -1
  15. package/dist/controllers/files.js +3 -0
  16. package/dist/controllers/utils.js +2 -0
  17. package/dist/database/helpers/date/index.d.ts +1 -0
  18. package/dist/database/helpers/date/index.js +3 -1
  19. package/dist/database/helpers/geometry/index.d.ts +1 -0
  20. package/dist/database/helpers/geometry/index.js +3 -1
  21. package/dist/database/helpers/index.d.ts +2 -0
  22. package/dist/database/helpers/index.js +2 -0
  23. package/dist/database/helpers/schema/dialects/cockroachdb.d.ts +16 -0
  24. package/dist/database/helpers/schema/dialects/cockroachdb.js +16 -0
  25. package/dist/database/helpers/schema/dialects/default.d.ts +3 -0
  26. package/dist/database/helpers/schema/dialects/default.js +7 -0
  27. package/dist/database/helpers/schema/dialects/oracle.d.ts +12 -0
  28. package/dist/database/helpers/schema/dialects/oracle.js +13 -0
  29. package/dist/database/helpers/schema/index.d.ts +7 -0
  30. package/dist/database/helpers/schema/index.js +17 -0
  31. package/dist/database/helpers/schema/types.d.ts +25 -0
  32. package/dist/database/helpers/schema/types.js +89 -0
  33. package/dist/database/index.d.ts +1 -1
  34. package/dist/database/index.js +66 -20
  35. package/dist/database/migrations/20201105B-change-webhook-url-type.js +6 -25
  36. package/dist/database/migrations/20210312A-webhooks-collections-text.js +6 -25
  37. package/dist/database/migrations/20210415A-make-filesize-nullable.js +9 -4
  38. package/dist/database/migrations/20210506A-rename-interfaces.js +1 -1
  39. package/dist/database/migrations/20210510A-restructure-relations.js +12 -4
  40. package/dist/database/migrations/20210525A-add-insights.js +2 -2
  41. package/dist/database/migrations/20210626A-change-filesize-bigint.js +5 -7
  42. package/dist/database/migrations/20210903A-add-auth-provider.js +11 -2
  43. package/dist/database/migrations/20210907A-webhooks-collections-not-null.js +6 -20
  44. package/dist/database/migrations/20210920A-webhooks-url-not-null.js +10 -14
  45. package/dist/database/migrations/20211211A-add-shares.js +2 -2
  46. package/dist/database/migrations/run.js +1 -1
  47. package/dist/database/run-ast.d.ts +1 -1
  48. package/dist/database/seeds/01-collections.yaml +1 -0
  49. package/dist/database/seeds/02-roles.yaml +1 -0
  50. package/dist/database/seeds/03-users.yaml +1 -0
  51. package/dist/database/system-data/app-access-permissions/app-access-permissions.yaml +8 -2
  52. package/dist/database/system-data/fields/collections.yaml +2 -0
  53. package/dist/database/system-data/relations/index.d.ts +1 -1
  54. package/dist/emitter.d.ts +3 -4
  55. package/dist/emitter.js +2 -8
  56. package/dist/env.js +1 -0
  57. package/dist/exceptions/database/translate.js +1 -0
  58. package/dist/exceptions/index.d.ts +1 -0
  59. package/dist/exceptions/index.js +1 -0
  60. package/dist/exceptions/unsupported-media-type.d.ts +4 -0
  61. package/dist/exceptions/unsupported-media-type.js +10 -0
  62. package/dist/extensions.d.ts +14 -8
  63. package/dist/extensions.js +136 -69
  64. package/dist/logger.js +22 -1
  65. package/dist/middleware/extract-token.js +1 -1
  66. package/dist/services/assets.js +3 -3
  67. package/dist/services/authentication.d.ts +2 -2
  68. package/dist/services/authentication.js +7 -2
  69. package/dist/services/authorization.d.ts +2 -3
  70. package/dist/services/collections.d.ts +2 -2
  71. package/dist/services/collections.js +20 -18
  72. package/dist/services/fields.d.ts +2 -3
  73. package/dist/services/fields.js +14 -10
  74. package/dist/services/graphql.d.ts +2 -1
  75. package/dist/services/graphql.js +4 -4
  76. package/dist/services/import.d.ts +2 -2
  77. package/dist/services/import.js +2 -1
  78. package/dist/services/items.d.ts +2 -2
  79. package/dist/services/items.js +22 -18
  80. package/dist/services/mail/index.d.ts +2 -2
  81. package/dist/services/meta.d.ts +2 -2
  82. package/dist/services/payload.d.ts +2 -2
  83. package/dist/services/payload.js +7 -3
  84. package/dist/services/relations.d.ts +2 -2
  85. package/dist/services/relations.js +4 -0
  86. package/dist/services/server.d.ts +2 -2
  87. package/dist/services/specifications.d.ts +2 -2
  88. package/dist/services/users.d.ts +2 -3
  89. package/dist/services/utils.d.ts +2 -2
  90. package/dist/types/ast.d.ts +1 -2
  91. package/dist/types/auth.d.ts +1 -3
  92. package/dist/types/index.d.ts +0 -3
  93. package/dist/types/index.js +0 -3
  94. package/dist/types/services.d.ts +1 -3
  95. package/dist/types/snapshot.d.ts +1 -2
  96. package/dist/utils/apply-query.d.ts +1 -2
  97. package/dist/utils/apply-query.js +1 -1
  98. package/dist/utils/apply-snapshot.d.ts +2 -1
  99. package/dist/utils/get-ast-from-query.d.ts +2 -3
  100. package/dist/utils/get-local-type.js +1 -1
  101. package/dist/utils/get-permissions.d.ts +1 -2
  102. package/dist/utils/get-permissions.js +1 -1
  103. package/dist/utils/get-relation-type.d.ts +1 -1
  104. package/dist/utils/get-schema.d.ts +1 -2
  105. package/dist/utils/get-snapshot.d.ts +2 -1
  106. package/dist/utils/md.js +1 -1
  107. package/dist/utils/merge-permissions-for-share.d.ts +1 -2
  108. package/dist/utils/reduce-schema.d.ts +1 -2
  109. package/example.env +8 -0
  110. package/package.json +23 -17
  111. package/dist/cli/index.test.d.ts +0 -1
  112. package/dist/cli/index.test.js +0 -58
  113. package/dist/middleware/cache.test.d.ts +0 -1
  114. package/dist/middleware/cache.test.js +0 -62
  115. package/dist/tests/database/migrations/run.test.d.ts +0 -1
  116. package/dist/tests/database/migrations/run.test.js +0 -29
  117. package/dist/types/extensions.d.ts +0 -43
  118. package/dist/types/extensions.js +0 -2
  119. package/dist/types/relation.d.ts +0 -21
  120. package/dist/types/relation.js +0 -2
  121. package/dist/types/schema.d.ts +0 -32
  122. package/dist/types/schema.js +0 -2
  123. package/dist/utils/get-cache-key.test.d.ts +0 -1
  124. package/dist/utils/get-cache-key.test.js +0 -53
@@ -156,6 +156,8 @@ fields:
156
156
  - decimal
157
157
  - integer
158
158
  allowNone: true
159
+ allowPrimaryKey: false
160
+ allowForeignKeys: false
159
161
  width: half
160
162
 
161
163
  - field: accountability_divider
@@ -1,2 +1,2 @@
1
- import { RelationMeta } from '../../../types';
1
+ import { RelationMeta } from '@directus/shared/types';
2
2
  export declare const systemRelationRows: RelationMeta[];
package/dist/emitter.d.ts CHANGED
@@ -1,11 +1,11 @@
1
- import { ActionHandler, FilterHandler, HookContext, InitHandler } from './types';
1
+ import { ActionHandler, EventContext, FilterHandler, InitHandler } from '@directus/shared/types';
2
2
  export declare class Emitter {
3
3
  private filterEmitter;
4
4
  private actionEmitter;
5
5
  private initEmitter;
6
6
  constructor();
7
- emitFilter<T>(event: string, payload: T, meta: Record<string, any>, context: HookContext): Promise<T>;
8
- emitAction(event: string, meta: Record<string, any>, context: HookContext): void;
7
+ emitFilter<T>(event: string | string[], payload: T, meta: Record<string, any>, context: EventContext): Promise<T>;
8
+ emitAction(event: string | string[], meta: Record<string, any>, context: EventContext): void;
9
9
  emitInit(event: string, meta: Record<string, any>): Promise<void>;
10
10
  onFilter(event: string, handler: FilterHandler): void;
11
11
  onAction(event: string, handler: ActionHandler): void;
@@ -14,7 +14,6 @@ export declare class Emitter {
14
14
  offAction(event: string, handler: ActionHandler): void;
15
15
  offInit(event: string, handler: InitHandler): void;
16
16
  offAll(): void;
17
- private eventsToEmit;
18
17
  }
19
18
  declare const emitter: Emitter;
20
19
  export default emitter;
package/dist/emitter.js CHANGED
@@ -20,7 +20,7 @@ class Emitter {
20
20
  this.initEmitter = new eventemitter2_1.EventEmitter2(emitterOptions);
21
21
  }
22
22
  async emitFilter(event, payload, meta, context) {
23
- const events = this.eventsToEmit(event, meta);
23
+ const events = Array.isArray(event) ? event : [event];
24
24
  const listeners = events.flatMap((event) => this.filterEmitter.listeners(event));
25
25
  let updatedPayload = payload;
26
26
  for (const listener of listeners) {
@@ -32,7 +32,7 @@ class Emitter {
32
32
  return updatedPayload;
33
33
  }
34
34
  emitAction(event, meta, context) {
35
- const events = this.eventsToEmit(event, meta);
35
+ const events = Array.isArray(event) ? event : [event];
36
36
  for (const event of events) {
37
37
  this.actionEmitter.emitAsync(event, meta, context).catch((err) => {
38
38
  logger_1.default.warn(`An error was thrown while executing action "${event}"`);
@@ -72,12 +72,6 @@ class Emitter {
72
72
  this.actionEmitter.removeAllListeners();
73
73
  this.initEmitter.removeAllListeners();
74
74
  }
75
- eventsToEmit(event, meta) {
76
- if (event.startsWith('items')) {
77
- return [event, `${meta.collection}.${event}`];
78
- }
79
- return [event];
80
- }
81
75
  }
82
76
  exports.Emitter = Emitter;
83
77
  const emitter = new Emitter();
package/dist/env.js CHANGED
@@ -52,6 +52,7 @@ const defaults = {
52
52
  AUTH_PROVIDERS: '',
53
53
  AUTH_DISABLE_DEFAULT: false,
54
54
  EXTENSIONS_PATH: './extensions',
55
+ EXTENSIONS_AUTO_RELOAD: false,
55
56
  EMAIL_FROM: 'no-reply@directus.io',
56
57
  EMAIL_TRANSPORT: 'sendmail',
57
58
  EMAIL_SENDMAIL_NEW_LINE: 'unix',
@@ -46,6 +46,7 @@ async function translateDatabaseError(error) {
46
46
  case 'mysql':
47
47
  defaultError = (0, mysql_1.extractError)(error);
48
48
  break;
49
+ case 'cockroachdb':
49
50
  case 'postgres':
50
51
  defaultError = (0, postgres_1.extractError)(error);
51
52
  break;
@@ -14,5 +14,6 @@ export * from './range-not-satisfiable';
14
14
  export * from './route-not-found';
15
15
  export * from './service-unavailable';
16
16
  export * from './unprocessable-entity';
17
+ export * from './unsupported-media-type';
17
18
  export * from './user-suspended';
18
19
  export * from './unexpected-response';
@@ -26,5 +26,6 @@ __exportStar(require("./range-not-satisfiable"), exports);
26
26
  __exportStar(require("./route-not-found"), exports);
27
27
  __exportStar(require("./service-unavailable"), exports);
28
28
  __exportStar(require("./unprocessable-entity"), exports);
29
+ __exportStar(require("./unsupported-media-type"), exports);
29
30
  __exportStar(require("./user-suspended"), exports);
30
31
  __exportStar(require("./unexpected-response"), exports);
@@ -0,0 +1,4 @@
1
+ import { BaseException } from '@directus/shared/exceptions';
2
+ export declare class UnsupportedMediaTypeException extends BaseException {
3
+ constructor(message: string, extensions?: Record<string, unknown>);
4
+ }
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.UnsupportedMediaTypeException = void 0;
4
+ const exceptions_1 = require("@directus/shared/exceptions");
5
+ class UnsupportedMediaTypeException extends exceptions_1.BaseException {
6
+ constructor(message, extensions) {
7
+ super(message, 415, 'UNSUPPORTED_MEDIA_TYPE', extensions);
8
+ }
9
+ }
10
+ exports.UnsupportedMediaTypeException = UnsupportedMediaTypeException;
@@ -1,23 +1,29 @@
1
1
  import { Router } from 'express';
2
2
  import { AppExtensionType, ExtensionType } from '@directus/shared/types';
3
3
  export declare function getExtensionManager(): ExtensionManager;
4
+ declare type Options = {
5
+ schedule: boolean;
6
+ watch: boolean;
7
+ };
4
8
  declare class ExtensionManager {
5
- private isInitialized;
9
+ private isLoaded;
10
+ private options;
6
11
  private extensions;
7
12
  private appExtensions;
8
- private apiHooks;
9
- private apiEndpoints;
13
+ private apiExtensions;
10
14
  private apiEmitter;
11
15
  private endpointRouter;
12
- private isScheduleHookEnabled;
16
+ private watcher;
13
17
  constructor();
14
- initialize({ schedule }?: {
15
- schedule: boolean;
16
- }): Promise<void>;
18
+ initialize(options?: Partial<Options>): Promise<void>;
17
19
  reload(): Promise<void>;
18
- listExtensions(type?: ExtensionType): string[];
20
+ getExtensionsList(type?: ExtensionType): string[];
19
21
  getAppExtensions(type: AppExtensionType): string | undefined;
20
22
  getEndpointRouter(): Router;
23
+ private load;
24
+ private unload;
25
+ private initializeWatcher;
26
+ private updateWatchedExtensions;
21
27
  private getExtensions;
22
28
  private generateExtensionBundles;
23
29
  private getSharedDepsMapping;
@@ -45,6 +45,8 @@ const plugin_alias_1 = __importDefault(require("@rollup/plugin-alias"));
45
45
  const url_1 = require("./utils/url");
46
46
  const get_module_default_1 = __importDefault(require("./utils/get-module-default"));
47
47
  const lodash_1 = require("lodash");
48
+ const chokidar_1 = __importDefault(require("chokidar"));
49
+ const utils_1 = require("@directus/shared/utils");
48
50
  let extensionManager;
49
51
  function getExtensionManager() {
50
52
  if (extensionManager) {
@@ -54,21 +56,73 @@ function getExtensionManager() {
54
56
  return extensionManager;
55
57
  }
56
58
  exports.getExtensionManager = getExtensionManager;
59
+ const defaultOptions = {
60
+ schedule: true,
61
+ watch: env_1.default.EXTENSIONS_AUTO_RELOAD && env_1.default.NODE_ENV !== 'development',
62
+ };
57
63
  class ExtensionManager {
58
64
  constructor() {
59
- this.isInitialized = false;
65
+ this.isLoaded = false;
60
66
  this.extensions = [];
61
67
  this.appExtensions = {};
62
- this.apiHooks = [];
63
- this.apiEndpoints = [];
64
- this.isScheduleHookEnabled = true;
68
+ this.apiExtensions = { hooks: [], endpoints: [] };
69
+ this.watcher = null;
70
+ this.options = defaultOptions;
65
71
  this.apiEmitter = new emitter_1.Emitter();
66
72
  this.endpointRouter = (0, express_1.Router)();
67
73
  }
68
- async initialize({ schedule } = { schedule: true }) {
69
- this.isScheduleHookEnabled = schedule;
70
- if (this.isInitialized)
71
- return;
74
+ async initialize(options = {}) {
75
+ this.options = {
76
+ ...defaultOptions,
77
+ ...options,
78
+ };
79
+ this.initializeWatcher();
80
+ if (!this.isLoaded) {
81
+ await this.load();
82
+ this.updateWatchedExtensions(this.extensions);
83
+ const loadedExtensions = this.getExtensionsList();
84
+ if (loadedExtensions.length > 0) {
85
+ logger_1.default.info(`Loaded extensions: ${loadedExtensions.join(', ')}`);
86
+ }
87
+ }
88
+ }
89
+ async reload() {
90
+ if (this.isLoaded) {
91
+ logger_1.default.info('Reloading extensions');
92
+ const prevExtensions = (0, lodash_1.clone)(this.extensions);
93
+ await this.unload();
94
+ await this.load();
95
+ const added = this.extensions.filter((extension) => !prevExtensions.some((prevExtension) => extension.path === prevExtension.path));
96
+ const removed = prevExtensions.filter((prevExtension) => !this.extensions.some((extension) => prevExtension.path === extension.path));
97
+ this.updateWatchedExtensions(added, removed);
98
+ const addedExtensions = added.map((extension) => extension.name);
99
+ const removedExtensions = removed.map((extension) => extension.name);
100
+ if (addedExtensions.length > 0) {
101
+ logger_1.default.info(`Added extensions: ${addedExtensions.join(', ')}`);
102
+ }
103
+ if (removedExtensions.length > 0) {
104
+ logger_1.default.info(`Removed extensions: ${removedExtensions.join(', ')}`);
105
+ }
106
+ }
107
+ else {
108
+ logger_1.default.warn('Extensions have to be loaded before they can be reloaded');
109
+ }
110
+ }
111
+ getExtensionsList(type) {
112
+ if (type === undefined) {
113
+ return this.extensions.map((extension) => extension.name);
114
+ }
115
+ else {
116
+ return this.extensions.filter((extension) => extension.type === type).map((extension) => extension.name);
117
+ }
118
+ }
119
+ getAppExtensions(type) {
120
+ return this.appExtensions[type];
121
+ }
122
+ getEndpointRouter() {
123
+ return this.endpointRouter;
124
+ }
125
+ async load() {
72
126
  try {
73
127
  await (0, node_1.ensureExtensionDirs)(env_1.default.EXTENSIONS_PATH, env_1.default.SERVE_APP ? constants_1.EXTENSION_TYPES : constants_1.API_EXTENSION_TYPES);
74
128
  this.extensions = await this.getExtensions();
@@ -82,38 +136,42 @@ class ExtensionManager {
82
136
  if (env_1.default.SERVE_APP) {
83
137
  this.appExtensions = await this.generateExtensionBundles();
84
138
  }
85
- const loadedExtensions = this.listExtensions();
86
- if (loadedExtensions.length > 0) {
87
- logger_1.default.info(`Loaded extensions: ${loadedExtensions.join(', ')}`);
88
- }
89
- this.isInitialized = true;
139
+ this.isLoaded = true;
90
140
  }
91
- async reload() {
92
- if (!this.isInitialized)
93
- return;
94
- logger_1.default.info('Reloading extensions');
141
+ async unload() {
95
142
  this.unregisterHooks();
96
143
  this.unregisterEndpoints();
97
144
  this.apiEmitter.offAll();
98
145
  if (env_1.default.SERVE_APP) {
99
146
  this.appExtensions = {};
100
147
  }
101
- this.isInitialized = false;
102
- await this.initialize();
148
+ this.isLoaded = false;
103
149
  }
104
- listExtensions(type) {
105
- if (type === undefined) {
106
- return this.extensions.map((extension) => extension.name);
107
- }
108
- else {
109
- return this.extensions.filter((extension) => extension.type === type).map((extension) => extension.name);
150
+ initializeWatcher() {
151
+ if (this.options.watch && !this.watcher) {
152
+ logger_1.default.info('Watching extensions for changes...');
153
+ const localExtensionPaths = (env_1.default.SERVE_APP ? constants_1.EXTENSION_TYPES : constants_1.API_EXTENSION_TYPES).map((type) => path_1.default.posix.join(path_1.default.relative('.', env_1.default.EXTENSIONS_PATH).split(path_1.default.sep).join(path_1.default.posix.sep), (0, utils_1.pluralize)(type), '*', 'index.js'));
154
+ this.watcher = chokidar_1.default.watch([path_1.default.resolve('.', 'package.json'), ...localExtensionPaths], {
155
+ ignoreInitial: true,
156
+ });
157
+ this.watcher
158
+ .on('add', () => this.reload())
159
+ .on('change', () => this.reload())
160
+ .on('unlink', () => this.reload());
110
161
  }
111
162
  }
112
- getAppExtensions(type) {
113
- return this.appExtensions[type];
114
- }
115
- getEndpointRouter() {
116
- return this.endpointRouter;
163
+ updateWatchedExtensions(added, removed = []) {
164
+ if (this.watcher) {
165
+ const toPackageExtensionPaths = (extensions) => extensions
166
+ .filter((extension) => !extension.local)
167
+ .map((extension) => extension.type !== 'pack'
168
+ ? path_1.default.resolve(extension.path, extension.entrypoint || '')
169
+ : path_1.default.resolve(extension.path, 'package.json'));
170
+ const addedPackageExtensionPaths = toPackageExtensionPaths(added);
171
+ const removedPackageExtensionPaths = toPackageExtensionPaths(removed);
172
+ this.watcher.add(addedPackageExtensionPaths);
173
+ this.watcher.unwatch(removedPackageExtensionPaths);
174
+ }
117
175
  }
118
176
  async getExtensions() {
119
177
  const packageExtensions = await (0, node_1.getPackageExtensions)('.', env_1.default.SERVE_APP ? constants_1.EXTENSION_PACKAGE_TYPES : constants_1.API_EXTENSION_PACKAGE_TYPES);
@@ -129,15 +187,21 @@ class ExtensionManager {
129
187
  const bundles = {};
130
188
  for (const extensionType of constants_1.APP_EXTENSION_TYPES) {
131
189
  const entry = (0, node_1.generateExtensionsEntry)(extensionType, this.extensions);
132
- const bundle = await (0, rollup_1.rollup)({
133
- input: 'entry',
134
- external: Object.values(sharedDepsMapping),
135
- makeAbsoluteExternalsRelative: false,
136
- plugins: [(0, plugin_virtual_1.default)({ entry }), (0, plugin_alias_1.default)({ entries: internalImports })],
137
- });
138
- const { output } = await bundle.generate({ format: 'es', compact: true });
139
- bundles[extensionType] = output[0].code;
140
- await bundle.close();
190
+ try {
191
+ const bundle = await (0, rollup_1.rollup)({
192
+ input: 'entry',
193
+ external: Object.values(sharedDepsMapping),
194
+ makeAbsoluteExternalsRelative: false,
195
+ plugins: [(0, plugin_virtual_1.default)({ entry }), (0, plugin_alias_1.default)({ entries: internalImports })],
196
+ });
197
+ const { output } = await bundle.generate({ format: 'es', compact: true });
198
+ bundles[extensionType] = output[0].code;
199
+ await bundle.close();
200
+ }
201
+ catch (error) {
202
+ logger_1.default.warn(`Couldn't bundle App extensions`);
203
+ logger_1.default.warn(error);
204
+ }
141
205
  }
142
206
  return bundles;
143
207
  }
@@ -185,38 +249,39 @@ class ExtensionManager {
185
249
  const hookPath = path_1.default.resolve(hook.path, hook.entrypoint || '');
186
250
  const hookInstance = require(hookPath);
187
251
  const register = (0, get_module_default_1.default)(hookInstance);
252
+ const hookHandler = {
253
+ path: hookPath,
254
+ events: [],
255
+ };
188
256
  const registerFunctions = {
189
257
  filter: (event, handler) => {
190
258
  emitter_1.default.onFilter(event, handler);
191
- this.apiHooks.push({
259
+ hookHandler.events.push({
192
260
  type: 'filter',
193
- path: hookPath,
194
- event,
261
+ name: event,
195
262
  handler,
196
263
  });
197
264
  },
198
265
  action: (event, handler) => {
199
266
  emitter_1.default.onAction(event, handler);
200
- this.apiHooks.push({
267
+ hookHandler.events.push({
201
268
  type: 'action',
202
- path: hookPath,
203
- event,
269
+ name: event,
204
270
  handler,
205
271
  });
206
272
  },
207
273
  init: (event, handler) => {
208
274
  emitter_1.default.onInit(event, handler);
209
- this.apiHooks.push({
275
+ hookHandler.events.push({
210
276
  type: 'init',
211
- path: hookPath,
212
- event,
277
+ name: event,
213
278
  handler,
214
279
  });
215
280
  },
216
281
  schedule: (cron, handler) => {
217
282
  if ((0, node_cron_1.validate)(cron)) {
218
283
  const task = (0, node_cron_1.schedule)(cron, async () => {
219
- if (this.isScheduleHookEnabled) {
284
+ if (this.options.schedule) {
220
285
  try {
221
286
  await handler();
222
287
  }
@@ -225,9 +290,8 @@ class ExtensionManager {
225
290
  }
226
291
  }
227
292
  });
228
- this.apiHooks.push({
293
+ hookHandler.events.push({
229
294
  type: 'schedule',
230
- path: hookPath,
231
295
  task,
232
296
  });
233
297
  }
@@ -245,6 +309,7 @@ class ExtensionManager {
245
309
  logger: logger_1.default,
246
310
  getSchema: get_schema_1.getSchema,
247
311
  });
312
+ this.apiExtensions.hooks.push(hookHandler);
248
313
  }
249
314
  registerEndpoint(endpoint, router) {
250
315
  const endpointPath = path_1.default.resolve(endpoint.path, endpoint.entrypoint || '');
@@ -263,35 +328,37 @@ class ExtensionManager {
263
328
  logger: logger_1.default,
264
329
  getSchema: get_schema_1.getSchema,
265
330
  });
266
- this.apiEndpoints.push({
331
+ this.apiExtensions.endpoints.push({
267
332
  path: endpointPath,
268
333
  });
269
334
  }
270
335
  unregisterHooks() {
271
- for (const hook of this.apiHooks) {
272
- switch (hook.type) {
273
- case 'filter':
274
- emitter_1.default.offFilter(hook.event, hook.handler);
275
- break;
276
- case 'action':
277
- emitter_1.default.offAction(hook.event, hook.handler);
278
- break;
279
- case 'init':
280
- emitter_1.default.offInit(hook.event, hook.handler);
281
- break;
282
- case 'schedule':
283
- hook.task.destroy();
284
- break;
336
+ for (const hook of this.apiExtensions.hooks) {
337
+ for (const event of hook.events) {
338
+ switch (event.type) {
339
+ case 'filter':
340
+ emitter_1.default.offFilter(event.name, event.handler);
341
+ break;
342
+ case 'action':
343
+ emitter_1.default.offAction(event.name, event.handler);
344
+ break;
345
+ case 'init':
346
+ emitter_1.default.offInit(event.name, event.handler);
347
+ break;
348
+ case 'schedule':
349
+ event.task.stop();
350
+ break;
351
+ }
285
352
  }
286
353
  delete require.cache[require.resolve(hook.path)];
287
354
  }
288
- this.apiHooks = [];
355
+ this.apiExtensions.hooks = [];
289
356
  }
290
357
  unregisterEndpoints() {
291
- for (const endpoint of this.apiEndpoints) {
358
+ for (const endpoint of this.apiExtensions.endpoints) {
292
359
  delete require.cache[require.resolve(endpoint.path)];
293
360
  }
294
361
  this.endpointRouter.stack = [];
295
- this.apiEndpoints = [];
362
+ this.apiExtensions.endpoints = [];
296
363
  }
297
364
  }
package/dist/logger.js CHANGED
@@ -25,6 +25,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
25
25
  exports.expressLogger = void 0;
26
26
  const pino_1 = __importDefault(require("pino"));
27
27
  const pino_http_1 = __importStar(require("pino-http"));
28
+ const get_config_from_env_1 = require("./utils/get-config-from-env");
28
29
  const url_1 = require("url");
29
30
  const env_1 = __importDefault(require("./env"));
30
31
  const pinoOptions = {
@@ -38,9 +39,29 @@ if (env_1.default.LOG_STYLE !== 'raw') {
38
39
  pinoOptions.prettyPrint = true;
39
40
  pinoOptions.prettifier = require('pino-colada');
40
41
  }
41
- const logger = (0, pino_1.default)(pinoOptions);
42
+ const loggerEnvConfig = (0, get_config_from_env_1.getConfigFromEnv)('LOGGER_', 'LOGGER_HTTP');
43
+ // Expose custom log levels into formatter function
44
+ if (loggerEnvConfig.levels) {
45
+ const customLogLevels = {};
46
+ for (const el of loggerEnvConfig.levels.split(',')) {
47
+ const key_val = el.split(':');
48
+ customLogLevels[key_val[0].trim()] = key_val[1].trim();
49
+ }
50
+ pinoOptions.formatters = {
51
+ level(label, number) {
52
+ return {
53
+ severity: customLogLevels[label] || 'info',
54
+ level: number,
55
+ };
56
+ },
57
+ };
58
+ delete loggerEnvConfig.levels;
59
+ }
60
+ const logger = (0, pino_1.default)(Object.assign(pinoOptions, loggerEnvConfig));
61
+ const httpLoggerEnvConfig = (0, get_config_from_env_1.getConfigFromEnv)('LOGGER_HTTP', ['LOGGER_HTTP_LOGGER']);
42
62
  exports.expressLogger = (0, pino_http_1.default)({
43
63
  logger,
64
+ ...httpLoggerEnvConfig,
44
65
  }, {
45
66
  serializers: {
46
67
  req(request) {
@@ -15,7 +15,7 @@ const extractToken = (req, res, next) => {
15
15
  }
16
16
  if (req.headers && req.headers.authorization) {
17
17
  const parts = req.headers.authorization.split(' ');
18
- if (parts.length === 2 && parts[0] === 'Bearer') {
18
+ if (parts.length === 2 && parts[0].toLowerCase() === 'bearer') {
19
19
  token = parts[1];
20
20
  }
21
21
  }
@@ -52,9 +52,6 @@ class AssetsService {
52
52
  .from('directus_settings')
53
53
  .first();
54
54
  const systemPublicKeys = Object.values(publicSettings || {});
55
- if (systemPublicKeys.includes(id) === false && ((_a = this.accountability) === null || _a === void 0 ? void 0 : _a.admin) !== true) {
56
- await this.authorizationService.checkAccess('read', 'directus_files', id);
57
- }
58
55
  /**
59
56
  * This is a little annoying. Postgres will error out if you're trying to search in `where`
60
57
  * with a wrong type. In case of directus_files where id is a uuid, we'll have to verify the
@@ -63,6 +60,9 @@ class AssetsService {
63
60
  const isValidUUID = (0, uuid_validate_1.default)(id, 4);
64
61
  if (isValidUUID === false)
65
62
  throw new exceptions_1.ForbiddenException();
63
+ if (systemPublicKeys.includes(id) === false && ((_a = this.accountability) === null || _a === void 0 ? void 0 : _a.admin) !== true) {
64
+ await this.authorizationService.checkAccess('read', 'directus_files', id);
65
+ }
66
66
  const file = (await this.knex.select('*').from('directus_files').where({ id }).first());
67
67
  if (!file)
68
68
  throw new exceptions_1.ForbiddenException();
@@ -1,7 +1,7 @@
1
1
  import { Knex } from 'knex';
2
2
  import { ActivityService } from './activity';
3
- import { AbstractServiceOptions, SchemaOverview, LoginResult } from '../types';
4
- import { Accountability } from '@directus/shared/types';
3
+ import { AbstractServiceOptions, LoginResult } from '../types';
4
+ import { Accountability, SchemaOverview } from '@directus/shared/types';
5
5
  export declare class AuthenticationService {
6
6
  knex: Knex;
7
7
  accountability: Accountability | null;
@@ -43,7 +43,7 @@ class AuthenticationService {
43
43
  const user = await this.knex
44
44
  .select('u.id', 'u.first_name', 'u.last_name', 'u.email', 'u.password', 'u.status', 'u.role', 'r.admin_access', 'r.app_access', 'u.tfa_secret', 'u.provider', 'u.external_identifier', 'u.auth_data')
45
45
  .from('directus_users as u')
46
- .innerJoin('directus_roles as r', 'u.role', 'r.id')
46
+ .leftJoin('directus_roles as r', 'u.role', 'r.id')
47
47
  .where('u.id', await provider.getUserID((0, lodash_1.cloneDeep)(payload)))
48
48
  .andWhere('u.provider', providerName)
49
49
  .first();
@@ -204,7 +204,9 @@ class AuthenticationService {
204
204
  .from('directus_sessions AS s')
205
205
  .leftJoin('directus_users AS u', 's.user', 'u.id')
206
206
  .leftJoin('directus_shares AS d', 's.share', 'd.id')
207
- .joinRaw('LEFT JOIN directus_roles AS r ON r.id IN (u.role, d.role)')
207
+ .leftJoin('directus_roles AS r', (join) => {
208
+ join.onIn('r.id', [this.knex.ref('u.role'), this.knex.ref('d.role')]);
209
+ })
208
210
  .where('s.token', refreshToken)
209
211
  .andWhere('s.expires', '>=', new Date())
210
212
  .andWhere((subQuery) => {
@@ -247,6 +249,9 @@ class AuthenticationService {
247
249
  collection: record.share_collection,
248
250
  item: record.share_item,
249
251
  };
252
+ tokenPayload.app_access = false;
253
+ tokenPayload.admin_access = false;
254
+ delete tokenPayload.id;
250
255
  }
251
256
  const customClaims = await emitter_1.default.emitFilter('auth.jwt', tokenPayload, {
252
257
  status: 'pending',
@@ -1,7 +1,6 @@
1
1
  import { Knex } from 'knex';
2
- import { Accountability } from '@directus/shared/types';
3
- import { AbstractServiceOptions, AST, Item, PrimaryKey, SchemaOverview } from '../types';
4
- import { PermissionsAction } from '@directus/shared/types';
2
+ import { AbstractServiceOptions, AST, Item, PrimaryKey } from '../types';
3
+ import { PermissionsAction, Accountability, SchemaOverview } from '@directus/shared/types';
5
4
  import { PayloadService } from './payload';
6
5
  export declare class AuthorizationService {
7
6
  knex: Knex;
@@ -1,8 +1,8 @@
1
1
  import SchemaInspector from '@directus/schema';
2
2
  import { Knex } from 'knex';
3
3
  import Keyv from 'keyv';
4
- import { AbstractServiceOptions, Collection, CollectionMeta, SchemaOverview, MutationOptions } from '../types';
5
- import { Accountability, RawField } from '@directus/shared/types';
4
+ import { AbstractServiceOptions, Collection, CollectionMeta, MutationOptions } from '../types';
5
+ import { Accountability, RawField, SchemaOverview } from '@directus/shared/types';
6
6
  import { Table } from 'knex-schema-inspector/dist/types/table';
7
7
  export declare type RawCollection = {
8
8
  collection: string;