directus 9.12.1 → 9.12.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.
package/dist/app.js CHANGED
@@ -72,6 +72,7 @@ const extract_token_1 = __importDefault(require("./middleware/extract-token"));
72
72
  const rate_limiter_1 = __importDefault(require("./middleware/rate-limiter"));
73
73
  const sanitize_query_1 = __importDefault(require("./middleware/sanitize-query"));
74
74
  const schema_1 = __importDefault(require("./middleware/schema"));
75
+ const constants_1 = require("./constants");
75
76
  const track_1 = require("./utils/track");
76
77
  const validate_env_1 = require("./utils/validate-env");
77
78
  const validate_storage_1 = require("./utils/validate-storage");
@@ -157,6 +158,11 @@ async function createApp() {
157
158
  next();
158
159
  }
159
160
  });
161
+ app.get('/robots.txt', (_, res) => {
162
+ res.set('Content-Type', 'text/plain');
163
+ res.status(200);
164
+ res.send(constants_1.ROBOTSTXT);
165
+ });
160
166
  if (env_1.default.SERVE_APP) {
161
167
  const adminPath = require.resolve('@directus/app');
162
168
  const adminUrl = new url_1.Url(env_1.default.PUBLIC_URL).addPath('admin');
@@ -13,3 +13,4 @@ export declare const COOKIE_OPTIONS: {
13
13
  secure: any;
14
14
  sameSite: "lax" | "strict" | "none";
15
15
  };
16
+ export declare const ROBOTSTXT: string;
package/dist/constants.js CHANGED
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  var _a;
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.COOKIE_OPTIONS = exports.UUID_REGEX = exports.COLUMN_TRANSFORMS = exports.DEFAULT_AUTH_PROVIDER = exports.ALIAS_TYPES = exports.FILTER_VARIABLES = exports.ASSET_TRANSFORM_QUERY_KEYS = exports.SYSTEM_ASSET_ALLOW_LIST = void 0;
7
+ exports.ROBOTSTXT = exports.COOKIE_OPTIONS = exports.UUID_REGEX = exports.COLUMN_TRANSFORMS = exports.DEFAULT_AUTH_PROVIDER = exports.ALIAS_TYPES = exports.FILTER_VARIABLES = exports.ASSET_TRANSFORM_QUERY_KEYS = exports.SYSTEM_ASSET_ALLOW_LIST = void 0;
8
8
  const env_1 = __importDefault(require("./env"));
9
9
  const ms_1 = __importDefault(require("ms"));
10
10
  exports.SYSTEM_ASSET_ALLOW_LIST = [
@@ -55,3 +55,7 @@ exports.COOKIE_OPTIONS = {
55
55
  secure: (_a = env_1.default.REFRESH_TOKEN_COOKIE_SECURE) !== null && _a !== void 0 ? _a : false,
56
56
  sameSite: env_1.default.REFRESH_TOKEN_COOKIE_SAME_SITE || 'strict',
57
57
  };
58
+ exports.ROBOTSTXT = `
59
+ User-agent: *
60
+ Disallow: /
61
+ `.trim();
@@ -1,13 +1,13 @@
1
- import { FnHelper } from '../types';
1
+ import { FnHelper, FnHelperOptions } from '../types';
2
2
  import { Knex } from 'knex';
3
3
  export declare class FnHelperOracle extends FnHelper {
4
- year(table: string, column: string): Knex.Raw;
5
- month(table: string, column: string): Knex.Raw;
6
- week(table: string, column: string): Knex.Raw;
7
- day(table: string, column: string): Knex.Raw;
8
- weekday(table: string, column: string): Knex.Raw;
9
- hour(table: string, column: string): Knex.Raw;
10
- minute(table: string, column: string): Knex.Raw;
11
- second(table: string, column: string): Knex.Raw;
4
+ year(table: string, column: string, options: FnHelperOptions): Knex.Raw;
5
+ month(table: string, column: string, options: FnHelperOptions): Knex.Raw;
6
+ week(table: string, column: string, options: FnHelperOptions): Knex.Raw;
7
+ day(table: string, column: string, options: FnHelperOptions): Knex.Raw;
8
+ weekday(table: string, column: string, options: FnHelperOptions): Knex.Raw;
9
+ hour(table: string, column: string, options: FnHelperOptions): Knex.Raw;
10
+ minute(table: string, column: string, options: FnHelperOptions): Knex.Raw;
11
+ second(table: string, column: string, options: FnHelperOptions): Knex.Raw;
12
12
  count(table: string, column: string): Knex.Raw<any>;
13
13
  }
@@ -2,30 +2,36 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.FnHelperOracle = void 0;
4
4
  const types_1 = require("../types");
5
+ const parseLocaltime = (columnType) => {
6
+ if (columnType === 'timestamp') {
7
+ return ` AT TIME ZONE 'UTC'`;
8
+ }
9
+ return '';
10
+ };
5
11
  class FnHelperOracle extends types_1.FnHelper {
6
- year(table, column) {
7
- return this.knex.raw("TO_CHAR(??.??, 'IYYY')", [table, column]);
12
+ year(table, column, options) {
13
+ return this.knex.raw(`TO_CHAR(??.??${parseLocaltime(options === null || options === void 0 ? void 0 : options.type)}, 'IYYY')`, [table, column]);
8
14
  }
9
- month(table, column) {
10
- return this.knex.raw("TO_CHAR(??.??, 'MM')", [table, column]);
15
+ month(table, column, options) {
16
+ return this.knex.raw(`TO_CHAR(??.??${parseLocaltime(options === null || options === void 0 ? void 0 : options.type)}, 'MM')`, [table, column]);
11
17
  }
12
- week(table, column) {
13
- return this.knex.raw("TO_CHAR(??.??, 'IW')", [table, column]);
18
+ week(table, column, options) {
19
+ return this.knex.raw(`TO_CHAR(??.??${parseLocaltime(options === null || options === void 0 ? void 0 : options.type)}, 'IW')`, [table, column]);
14
20
  }
15
- day(table, column) {
16
- return this.knex.raw("TO_CHAR(??.??, 'DD')", [table, column]);
21
+ day(table, column, options) {
22
+ return this.knex.raw(`TO_CHAR(??.??${parseLocaltime(options === null || options === void 0 ? void 0 : options.type)}, 'DD')`, [table, column]);
17
23
  }
18
- weekday(table, column) {
19
- return this.knex.raw("TO_CHAR(??.??, 'D')", [table, column]);
24
+ weekday(table, column, options) {
25
+ return this.knex.raw(`TO_CHAR(??.??${parseLocaltime(options === null || options === void 0 ? void 0 : options.type)}, 'D')`, [table, column]);
20
26
  }
21
- hour(table, column) {
22
- return this.knex.raw("TO_CHAR(??.??, 'HH24')", [table, column]);
27
+ hour(table, column, options) {
28
+ return this.knex.raw(`TO_CHAR(??.??${parseLocaltime(options === null || options === void 0 ? void 0 : options.type)}, 'HH24')`, [table, column]);
23
29
  }
24
- minute(table, column) {
25
- return this.knex.raw("TO_CHAR(??.??, 'MI')", [table, column]);
30
+ minute(table, column, options) {
31
+ return this.knex.raw(`TO_CHAR(??.??${parseLocaltime(options === null || options === void 0 ? void 0 : options.type)}, 'MI')`, [table, column]);
26
32
  }
27
- second(table, column) {
28
- return this.knex.raw("TO_CHAR(??.??, 'SS')", [table, column]);
33
+ second(table, column, options) {
34
+ return this.knex.raw(`TO_CHAR(??.??${parseLocaltime(options === null || options === void 0 ? void 0 : options.type)}, 'SS')`, [table, column]);
29
35
  }
30
36
  count(table, column) {
31
37
  var _a, _b, _c, _d, _e;
@@ -1,13 +1,13 @@
1
- import { FnHelper } from '../types';
1
+ import { FnHelper, FnHelperOptions } from '../types';
2
2
  import { Knex } from 'knex';
3
3
  export declare class FnHelperSQLite extends FnHelper {
4
- year(table: string, column: string): Knex.Raw;
5
- month(table: string, column: string): Knex.Raw;
6
- week(table: string, column: string): Knex.Raw;
7
- day(table: string, column: string): Knex.Raw;
8
- weekday(table: string, column: string): Knex.Raw;
9
- hour(table: string, column: string): Knex.Raw;
10
- minute(table: string, column: string): Knex.Raw;
11
- second(table: string, column: string): Knex.Raw;
4
+ year(table: string, column: string, options?: FnHelperOptions): Knex.Raw;
5
+ month(table: string, column: string, options?: FnHelperOptions): Knex.Raw;
6
+ week(table: string, column: string, options?: FnHelperOptions): Knex.Raw;
7
+ day(table: string, column: string, options?: FnHelperOptions): Knex.Raw;
8
+ weekday(table: string, column: string, options?: FnHelperOptions): Knex.Raw;
9
+ hour(table: string, column: string, options?: FnHelperOptions): Knex.Raw;
10
+ minute(table: string, column: string, options?: FnHelperOptions): Knex.Raw;
11
+ second(table: string, column: string, options?: FnHelperOptions): Knex.Raw;
12
12
  count(table: string, column: string): Knex.Raw<any>;
13
13
  }
@@ -2,30 +2,60 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.FnHelperSQLite = void 0;
4
4
  const types_1 = require("../types");
5
+ const parseLocaltime = (columnType) => {
6
+ if (columnType === 'timestamp') {
7
+ return '';
8
+ }
9
+ return `, 'localtime'`;
10
+ };
5
11
  class FnHelperSQLite extends types_1.FnHelper {
6
- year(table, column) {
7
- return this.knex.raw("CAST(strftime('%Y', ??.?? / 1000, 'unixepoch') AS INTEGER)", [table, column]);
12
+ year(table, column, options) {
13
+ return this.knex.raw(`CAST(strftime('%Y', ??.?? / 1000, 'unixepoch'${parseLocaltime(options === null || options === void 0 ? void 0 : options.type)}) AS INTEGER)`, [
14
+ table,
15
+ column,
16
+ ]);
8
17
  }
9
- month(table, column) {
10
- return this.knex.raw("CAST(strftime('%m', ??.?? / 1000, 'unixepoch') AS INTEGER)", [table, column]);
18
+ month(table, column, options) {
19
+ return this.knex.raw(`CAST(strftime('%m', ??.?? / 1000, 'unixepoch'${parseLocaltime(options === null || options === void 0 ? void 0 : options.type)}) AS INTEGER)`, [
20
+ table,
21
+ column,
22
+ ]);
11
23
  }
12
- week(table, column) {
13
- return this.knex.raw("CAST(strftime('%W', ??.?? / 1000, 'unixepoch') AS INTEGER)", [table, column]);
24
+ week(table, column, options) {
25
+ return this.knex.raw(`CAST(strftime('%W', ??.?? / 1000, 'unixepoch'${parseLocaltime(options === null || options === void 0 ? void 0 : options.type)}) AS INTEGER)`, [
26
+ table,
27
+ column,
28
+ ]);
14
29
  }
15
- day(table, column) {
16
- return this.knex.raw("CAST(strftime('%d', ??.?? / 1000, 'unixepoch') AS INTEGER)", [table, column]);
30
+ day(table, column, options) {
31
+ return this.knex.raw(`CAST(strftime('%d', ??.?? / 1000, 'unixepoch'${parseLocaltime(options === null || options === void 0 ? void 0 : options.type)}) AS INTEGER)`, [
32
+ table,
33
+ column,
34
+ ]);
17
35
  }
18
- weekday(table, column) {
19
- return this.knex.raw("CAST(strftime('%w', ??.?? / 1000, 'unixepoch') AS INTEGER)", [table, column]);
36
+ weekday(table, column, options) {
37
+ return this.knex.raw(`CAST(strftime('%w', ??.?? / 1000, 'unixepoch'${parseLocaltime(options === null || options === void 0 ? void 0 : options.type)}) AS INTEGER)`, [
38
+ table,
39
+ column,
40
+ ]);
20
41
  }
21
- hour(table, column) {
22
- return this.knex.raw("CAST(strftime('%H', ??.?? / 1000, 'unixepoch') AS INTEGER)", [table, column]);
42
+ hour(table, column, options) {
43
+ return this.knex.raw(`CAST(strftime('%H', ??.?? / 1000, 'unixepoch'${parseLocaltime(options === null || options === void 0 ? void 0 : options.type)}) AS INTEGER)`, [
44
+ table,
45
+ column,
46
+ ]);
23
47
  }
24
- minute(table, column) {
25
- return this.knex.raw("CAST(strftime('%M', ??.?? / 1000, 'unixepoch') AS INTEGER)", [table, column]);
48
+ minute(table, column, options) {
49
+ return this.knex.raw(`CAST(strftime('%M', ??.?? / 1000, 'unixepoch'${parseLocaltime(options === null || options === void 0 ? void 0 : options.type)}) AS INTEGER)`, [
50
+ table,
51
+ column,
52
+ ]);
26
53
  }
27
- second(table, column) {
28
- return this.knex.raw("CAST(strftime('%S', ??.?? / 1000, 'unixepoch') AS INTEGER)", [table, column]);
54
+ second(table, column, options) {
55
+ return this.knex.raw(`CAST(strftime('%S', ??.?? / 1000, 'unixepoch'${parseLocaltime(options === null || options === void 0 ? void 0 : options.type)}) AS INTEGER)`, [
56
+ table,
57
+ column,
58
+ ]);
29
59
  }
30
60
  count(table, column) {
31
61
  var _a, _b, _c, _d, _e;
@@ -1,18 +1,21 @@
1
1
  import { SchemaOverview } from '@directus/shared/types';
2
2
  import { Knex } from 'knex';
3
3
  import { DatabaseHelper } from '../types';
4
+ export declare type FnHelperOptions = {
5
+ type?: string;
6
+ };
4
7
  export declare abstract class FnHelper extends DatabaseHelper {
5
8
  protected knex: Knex;
6
9
  protected schema: SchemaOverview;
7
10
  constructor(knex: Knex, schema: SchemaOverview);
8
- abstract year(table: string, column: string): Knex.Raw;
9
- abstract month(table: string, column: string): Knex.Raw;
10
- abstract week(table: string, column: string): Knex.Raw;
11
- abstract day(table: string, column: string): Knex.Raw;
12
- abstract weekday(table: string, column: string): Knex.Raw;
13
- abstract hour(table: string, column: string): Knex.Raw;
14
- abstract minute(table: string, column: string): Knex.Raw;
15
- abstract second(table: string, column: string): Knex.Raw;
16
- abstract count(table: string, column: string): Knex.Raw;
11
+ abstract year(table: string, column: string, options?: FnHelperOptions): Knex.Raw;
12
+ abstract month(table: string, column: string, options?: FnHelperOptions): Knex.Raw;
13
+ abstract week(table: string, column: string, options?: FnHelperOptions): Knex.Raw;
14
+ abstract day(table: string, column: string, options?: FnHelperOptions): Knex.Raw;
15
+ abstract weekday(table: string, column: string, options?: FnHelperOptions): Knex.Raw;
16
+ abstract hour(table: string, column: string, options?: FnHelperOptions): Knex.Raw;
17
+ abstract minute(table: string, column: string, options?: FnHelperOptions): Knex.Raw;
18
+ abstract second(table: string, column: string, options?: FnHelperOptions): Knex.Raw;
19
+ abstract count(table: string, column: string, options?: FnHelperOptions): Knex.Raw;
17
20
  protected _relationalCount(table: string, column: string): Knex.Raw;
18
21
  }
@@ -6,7 +6,7 @@ fields:
6
6
  - field: name
7
7
  - field: icon
8
8
  - field: color
9
- - field: note
9
+ - field: description
10
10
  - field: status
11
11
  - field: trigger
12
12
  - field: accountability
@@ -13,10 +13,11 @@ declare class ExtensionManager {
13
13
  private apiExtensions;
14
14
  private apiEmitter;
15
15
  private endpointRouter;
16
+ private reloadQueue;
16
17
  private watcher;
17
18
  constructor();
18
19
  initialize(options?: Partial<Options>): Promise<void>;
19
- reload(): Promise<void>;
20
+ reload(): void;
20
21
  getExtensionsList(type?: ExtensionType): string[];
21
22
  getAppExtensions(type: AppExtensionType): string | undefined;
22
23
  getEndpointRouter(): Router;
@@ -47,6 +47,7 @@ const chokidar_1 = __importDefault(require("chokidar"));
47
47
  const utils_1 = require("@directus/shared/utils");
48
48
  const flows_1 = require("./flows");
49
49
  const globby_1 = __importDefault(require("globby"));
50
+ const job_queue_1 = require("./utils/job-queue");
50
51
  let extensionManager;
51
52
  function getExtensionManager() {
52
53
  if (extensionManager) {
@@ -70,6 +71,7 @@ class ExtensionManager {
70
71
  this.options = defaultOptions;
71
72
  this.apiEmitter = new emitter_1.Emitter();
72
73
  this.endpointRouter = (0, express_1.Router)();
74
+ this.reloadQueue = new job_queue_1.JobQueue();
73
75
  }
74
76
  async initialize(options = {}) {
75
77
  this.options = {
@@ -86,27 +88,29 @@ class ExtensionManager {
86
88
  }
87
89
  }
88
90
  }
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(', ')}`);
91
+ reload() {
92
+ this.reloadQueue.enqueue(async () => {
93
+ if (this.isLoaded) {
94
+ logger_1.default.info('Reloading extensions');
95
+ const prevExtensions = (0, lodash_1.clone)(this.extensions);
96
+ await this.unload();
97
+ await this.load();
98
+ const added = this.extensions.filter((extension) => !prevExtensions.some((prevExtension) => extension.path === prevExtension.path));
99
+ const removed = prevExtensions.filter((prevExtension) => !this.extensions.some((extension) => prevExtension.path === extension.path));
100
+ this.updateWatchedExtensions(added, removed);
101
+ const addedExtensions = added.map((extension) => extension.name);
102
+ const removedExtensions = removed.map((extension) => extension.name);
103
+ if (addedExtensions.length > 0) {
104
+ logger_1.default.info(`Added extensions: ${addedExtensions.join(', ')}`);
105
+ }
106
+ if (removedExtensions.length > 0) {
107
+ logger_1.default.info(`Removed extensions: ${removedExtensions.join(', ')}`);
108
+ }
102
109
  }
103
- if (removedExtensions.length > 0) {
104
- logger_1.default.info(`Removed extensions: ${removedExtensions.join(', ')}`);
110
+ else {
111
+ logger_1.default.warn('Extensions have to be loaded before they can be reloaded');
105
112
  }
106
- }
107
- else {
108
- logger_1.default.warn('Extensions have to be loaded before they can be reloaded');
109
- }
113
+ });
110
114
  }
111
115
  getExtensionsList(type) {
112
116
  if (type === undefined) {
package/dist/flows.d.ts CHANGED
@@ -1,16 +1,21 @@
1
1
  import { OperationHandler } from '@directus/shared/types';
2
2
  export declare function getFlowManager(): FlowManager;
3
3
  declare class FlowManager {
4
+ private isLoaded;
4
5
  private operations;
5
6
  private triggerHandlers;
6
7
  private operationFlowHandlers;
7
8
  private webhookFlowHandlers;
9
+ private reloadQueue;
10
+ constructor();
8
11
  initialize(): Promise<void>;
9
12
  reload(): Promise<void>;
10
13
  addOperation(id: string, operation: OperationHandler): void;
11
14
  clearOperations(): void;
12
15
  runOperationFlow(id: string, data: unknown, context: Record<string, unknown>): Promise<unknown>;
13
16
  runWebhookFlow(id: string, data: unknown, context: Record<string, unknown>): Promise<unknown>;
17
+ private load;
18
+ private unload;
14
19
  private executeFlow;
15
20
  private executeOperation;
16
21
  }
package/dist/flows.js CHANGED
@@ -42,6 +42,7 @@ const lodash_1 = require("lodash");
42
42
  const messenger_1 = require("./messenger");
43
43
  const fast_redact_1 = __importDefault(require("fast-redact"));
44
44
  const operation_options_1 = require("./utils/operation-options");
45
+ const job_queue_1 = require("./utils/job-queue");
45
46
  let flowManager;
46
47
  const redactLogs = (0, fast_redact_1.default)({
47
48
  censor: '--redacted--',
@@ -61,12 +62,59 @@ const ACCOUNTABILITY_KEY = '$accountability';
61
62
  const LAST_KEY = '$last';
62
63
  class FlowManager {
63
64
  constructor() {
65
+ this.isLoaded = false;
64
66
  this.operations = {};
65
67
  this.triggerHandlers = [];
66
68
  this.operationFlowHandlers = {};
67
69
  this.webhookFlowHandlers = {};
70
+ this.reloadQueue = new job_queue_1.JobQueue();
71
+ const messenger = (0, messenger_1.getMessenger)();
72
+ messenger.subscribe('flows', (event) => {
73
+ if (event.type === 'reload') {
74
+ this.reloadQueue.enqueue(async () => {
75
+ if (this.isLoaded) {
76
+ await this.unload();
77
+ await this.load();
78
+ }
79
+ else {
80
+ logger_1.default.warn('Flows have to be loaded before they can be reloaded');
81
+ }
82
+ });
83
+ }
84
+ });
68
85
  }
69
86
  async initialize() {
87
+ if (!this.isLoaded) {
88
+ await this.load();
89
+ }
90
+ }
91
+ async reload() {
92
+ const messenger = (0, messenger_1.getMessenger)();
93
+ messenger.publish('flows', { type: 'reload' });
94
+ }
95
+ addOperation(id, operation) {
96
+ this.operations[id] = operation;
97
+ }
98
+ clearOperations() {
99
+ this.operations = {};
100
+ }
101
+ async runOperationFlow(id, data, context) {
102
+ if (!(id in this.operationFlowHandlers)) {
103
+ logger_1.default.warn(`Couldn't find operation triggered flow with id "${id}"`);
104
+ return null;
105
+ }
106
+ const handler = this.operationFlowHandlers[id];
107
+ return handler(data, context);
108
+ }
109
+ async runWebhookFlow(id, data, context) {
110
+ if (!(id in this.webhookFlowHandlers)) {
111
+ logger_1.default.warn(`Couldn't find webhook or manual triggered flow with id "${id}"`);
112
+ throw new exceptions.ForbiddenException();
113
+ }
114
+ const handler = this.webhookFlowHandlers[id];
115
+ return handler(data, context);
116
+ }
117
+ async load() {
70
118
  var _a, _b, _c, _d, _e, _f, _g;
71
119
  const flowsService = new services_1.FlowsService({ knex: (0, database_1.default)(), schema: await (0, get_schema_1.getSchema)() });
72
120
  const flows = await flowsService.readByQuery({
@@ -177,13 +225,9 @@ class FlowManager {
177
225
  this.webhookFlowHandlers[`POST-${flow.id}`] = handler;
178
226
  }
179
227
  }
180
- (0, messenger_1.getMessenger)().subscribe('flows', (event) => {
181
- if (event.type === 'reload') {
182
- this.reload();
183
- }
184
- });
228
+ this.isLoaded = true;
185
229
  }
186
- async reload() {
230
+ async unload() {
187
231
  for (const trigger of this.triggerHandlers) {
188
232
  trigger.events.forEach((event) => {
189
233
  switch (event.type) {
@@ -202,29 +246,7 @@ class FlowManager {
202
246
  this.triggerHandlers = [];
203
247
  this.operationFlowHandlers = {};
204
248
  this.webhookFlowHandlers = {};
205
- await this.initialize();
206
- }
207
- addOperation(id, operation) {
208
- this.operations[id] = operation;
209
- }
210
- clearOperations() {
211
- this.operations = {};
212
- }
213
- async runOperationFlow(id, data, context) {
214
- if (!(id in this.operationFlowHandlers)) {
215
- logger_1.default.warn(`Couldn't find operation triggered flow with id "${id}"`);
216
- return null;
217
- }
218
- const handler = this.operationFlowHandlers[id];
219
- return handler(data, context);
220
- }
221
- async runWebhookFlow(id, data, context) {
222
- if (!(id in this.webhookFlowHandlers)) {
223
- logger_1.default.warn(`Couldn't find webhook or manual triggered flow with id "${id}"`);
224
- throw new exceptions.ForbiddenException();
225
- }
226
- const handler = this.webhookFlowHandlers[id];
227
- return handler(data, context);
249
+ this.isLoaded = false;
228
250
  }
229
251
  async executeFlow(flow, data = null, context = {}) {
230
252
  var _a, _b, _c, _d, _e, _f;
@@ -3,7 +3,10 @@ declare type Options = {
3
3
  url: string;
4
4
  method: Method;
5
5
  body: Record<string, any> | string | null;
6
- headers: Record<string, string>;
6
+ headers?: {
7
+ header: string;
8
+ value: string;
9
+ }[] | null;
7
10
  };
8
11
  declare const _default: import("@directus/shared/types").OperationApiConfig<Options>;
9
12
  export default _default;
@@ -8,7 +8,11 @@ const axios_1 = __importDefault(require("axios"));
8
8
  exports.default = (0, utils_1.defineOperationApi)({
9
9
  id: 'request',
10
10
  handler: async ({ url, method, body, headers }) => {
11
- const result = await (0, axios_1.default)({ url, method, data: body, headers });
11
+ const customHeaders = headers === null || headers === void 0 ? void 0 : headers.reduce((acc, { header, value }) => {
12
+ acc[header] = value;
13
+ return acc;
14
+ }, {});
15
+ const result = await (0, axios_1.default)({ url, method, data: body, headers: customHeaders });
12
16
  return { status: result.status, statusText: result.statusText, headers: result.headers, data: result.data };
13
17
  },
14
18
  });
@@ -1,9 +1,7 @@
1
1
  import { FlowRaw } from '@directus/shared/types';
2
- import { Messenger } from '../messenger';
3
2
  import { AbstractServiceOptions, Item, MutationOptions, PrimaryKey } from '../types';
4
3
  import { ItemsService } from './items';
5
4
  export declare class FlowsService extends ItemsService<FlowRaw> {
6
- messenger: Messenger;
7
5
  constructor(options: AbstractServiceOptions);
8
6
  createOne(data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
9
7
  createMany(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
@@ -1,41 +1,46 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.FlowsService = void 0;
4
- const messenger_1 = require("../messenger");
4
+ const flows_1 = require("../flows");
5
5
  const items_1 = require("./items");
6
6
  class FlowsService extends items_1.ItemsService {
7
7
  constructor(options) {
8
8
  super('directus_flows', options);
9
- this.messenger = (0, messenger_1.getMessenger)();
10
9
  }
11
10
  async createOne(data, opts) {
11
+ const flowManager = (0, flows_1.getFlowManager)();
12
12
  const result = await super.createOne(data, opts);
13
- this.messenger.publish('flows', { type: 'reload' });
13
+ await flowManager.reload();
14
14
  return result;
15
15
  }
16
16
  async createMany(data, opts) {
17
+ const flowManager = (0, flows_1.getFlowManager)();
17
18
  const result = await super.createMany(data, opts);
18
- this.messenger.publish('flows', { type: 'reload' });
19
+ await flowManager.reload();
19
20
  return result;
20
21
  }
21
22
  async updateOne(key, data, opts) {
23
+ const flowManager = (0, flows_1.getFlowManager)();
22
24
  const result = await super.updateOne(key, data, opts);
23
- this.messenger.publish('flows', { type: 'reload' });
25
+ await flowManager.reload();
24
26
  return result;
25
27
  }
26
28
  async updateMany(keys, data, opts) {
29
+ const flowManager = (0, flows_1.getFlowManager)();
27
30
  const result = await super.updateMany(keys, data, opts);
28
- this.messenger.publish('flows', { type: 'reload' });
31
+ await flowManager.reload();
29
32
  return result;
30
33
  }
31
34
  async deleteOne(key, opts) {
35
+ const flowManager = (0, flows_1.getFlowManager)();
32
36
  const result = await super.deleteOne(key, opts);
33
- this.messenger.publish('flows', { type: 'reload' });
37
+ await flowManager.reload();
34
38
  return result;
35
39
  }
36
40
  async deleteMany(keys, opts) {
41
+ const flowManager = (0, flows_1.getFlowManager)();
37
42
  const result = await super.deleteMany(keys, opts);
38
- this.messenger.publish('flows', { type: 'reload' });
43
+ await flowManager.reload();
39
44
  return result;
40
45
  }
41
46
  }
@@ -1,9 +1,7 @@
1
1
  import { OperationRaw } from '@directus/shared/types';
2
- import { Messenger } from '../messenger';
3
2
  import { AbstractServiceOptions, Item, MutationOptions, PrimaryKey } from '../types';
4
3
  import { ItemsService } from './items';
5
4
  export declare class OperationsService extends ItemsService<OperationRaw> {
6
- messenger: Messenger;
7
5
  constructor(options: AbstractServiceOptions);
8
6
  createOne(data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
9
7
  createMany(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
@@ -1,41 +1,46 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.OperationsService = void 0;
4
- const messenger_1 = require("../messenger");
4
+ const flows_1 = require("../flows");
5
5
  const items_1 = require("./items");
6
6
  class OperationsService extends items_1.ItemsService {
7
7
  constructor(options) {
8
8
  super('directus_operations', options);
9
- this.messenger = (0, messenger_1.getMessenger)();
10
9
  }
11
10
  async createOne(data, opts) {
11
+ const flowManager = (0, flows_1.getFlowManager)();
12
12
  const result = await super.createOne(data, opts);
13
- this.messenger.publish('flows', { type: 'reload' });
13
+ await flowManager.reload();
14
14
  return result;
15
15
  }
16
16
  async createMany(data, opts) {
17
+ const flowManager = (0, flows_1.getFlowManager)();
17
18
  const result = await super.createMany(data, opts);
18
- this.messenger.publish('flows', { type: 'reload' });
19
+ await flowManager.reload();
19
20
  return result;
20
21
  }
21
22
  async updateOne(key, data, opts) {
23
+ const flowManager = (0, flows_1.getFlowManager)();
22
24
  const result = await super.updateOne(key, data, opts);
23
- this.messenger.publish('flows', { type: 'reload' });
25
+ await flowManager.reload();
24
26
  return result;
25
27
  }
26
28
  async updateMany(keys, data, opts) {
29
+ const flowManager = (0, flows_1.getFlowManager)();
27
30
  const result = await super.updateMany(keys, data, opts);
28
- this.messenger.publish('flows', { type: 'reload' });
31
+ await flowManager.reload();
29
32
  return result;
30
33
  }
31
34
  async deleteOne(key, opts) {
35
+ const flowManager = (0, flows_1.getFlowManager)();
32
36
  const result = await super.deleteOne(key, opts);
33
- this.messenger.publish('flows', { type: 'reload' });
37
+ await flowManager.reload();
34
38
  return result;
35
39
  }
36
40
  async deleteMany(keys, opts) {
41
+ const flowManager = (0, flows_1.getFlowManager)();
37
42
  const result = await super.deleteMany(keys, opts);
38
- this.messenger.publish('flows', { type: 'reload' });
43
+ await flowManager.reload();
39
44
  return result;
40
45
  }
41
46
  }
@@ -305,6 +305,7 @@ class UsersService extends items_1.ItemsService {
305
305
  await (0, stall_1.stall)(STALL_TIME, timeStart);
306
306
  }
307
307
  async resetPassword(token, password) {
308
+ var _a;
308
309
  const { email, scope, hash } = jsonwebtoken_1.default.verify(token, env_1.default.SECRET, { issuer: 'directus' });
309
310
  if (scope !== 'password-reset' || !hash)
310
311
  throw new exceptions_2.ForbiddenException();
@@ -317,6 +318,10 @@ class UsersService extends items_1.ItemsService {
317
318
  const service = new UsersService({
318
319
  knex: this.knex,
319
320
  schema: this.schema,
321
+ accountability: {
322
+ ...((_a = this.accountability) !== null && _a !== void 0 ? _a : { role: null }),
323
+ admin: true, // We need to skip permissions checks for the update call below
324
+ },
320
325
  });
321
326
  await service.updateOne(user.id, { password, status: 'active' });
322
327
  }
@@ -11,10 +11,12 @@ const services_1 = require("../services");
11
11
  const get_schema_1 = require("./get-schema");
12
12
  const get_snapshot_1 = require("./get-snapshot");
13
13
  const get_snapshot_diff_1 = require("./get-snapshot-diff");
14
+ const cache_1 = require("../cache");
14
15
  async function applySnapshot(snapshot, options) {
15
16
  var _a, _b, _c, _d;
16
17
  const database = (_a = options === null || options === void 0 ? void 0 : options.database) !== null && _a !== void 0 ? _a : (0, database_1.default)();
17
18
  const schema = (_b = options === null || options === void 0 ? void 0 : options.schema) !== null && _b !== void 0 ? _b : (await (0, get_schema_1.getSchema)({ database }));
19
+ const { systemCache } = (0, cache_1.getCache)();
18
20
  const current = (_c = options === null || options === void 0 ? void 0 : options.current) !== null && _c !== void 0 ? _c : (await (0, get_snapshot_1.getSnapshot)({ database, schema }));
19
21
  const snapshotDiff = (_d = options === null || options === void 0 ? void 0 : options.diff) !== null && _d !== void 0 ? _d : (0, get_snapshot_diff_1.getSnapshotDiff)(current, snapshot);
20
22
  await database.transaction(async (trx) => {
@@ -178,6 +180,7 @@ async function applySnapshot(snapshot, options) {
178
180
  }
179
181
  }
180
182
  });
183
+ await (systemCache === null || systemCache === void 0 ? void 0 : systemCache.clear());
181
184
  }
182
185
  exports.applySnapshot = applySnapshot;
183
186
  function isNestedMetaUpdate(diff) {
@@ -28,7 +28,7 @@ function getColumn(knex, table, column, alias = (0, apply_function_to_column_nam
28
28
  if (allowedFunctions.includes(functionName) === false) {
29
29
  throw new exceptions_1.InvalidQueryException(`Invalid function specified "${functionName}"`);
30
30
  }
31
- const result = fn[functionName](table, columnName);
31
+ const result = fn[functionName](table, columnName, { type });
32
32
  if (alias) {
33
33
  return knex.raw(result + ' AS ??', [alias]);
34
34
  }
@@ -0,0 +1,9 @@
1
+ declare type Job = () => Promise<void> | void;
2
+ export declare class JobQueue {
3
+ private running;
4
+ private jobs;
5
+ constructor();
6
+ enqueue(job: Job): void;
7
+ private run;
8
+ }
9
+ export {};
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.JobQueue = void 0;
4
+ class JobQueue {
5
+ constructor() {
6
+ this.running = false;
7
+ this.jobs = [];
8
+ }
9
+ enqueue(job) {
10
+ this.jobs.push(job);
11
+ if (!this.running) {
12
+ this.run();
13
+ }
14
+ }
15
+ async run() {
16
+ this.running = true;
17
+ while (this.jobs.length > 0) {
18
+ const job = this.jobs.shift();
19
+ await job();
20
+ }
21
+ this.running = false;
22
+ }
23
+ }
24
+ exports.JobQueue = JobQueue;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "directus",
3
- "version": "9.12.1",
3
+ "version": "9.12.2",
4
4
  "license": "GPL-3.0-only",
5
5
  "homepage": "https://github.com/directus/directus#readme",
6
6
  "description": "Directus is a real-time API and App dashboard for managing SQL database content.",
@@ -77,16 +77,16 @@
77
77
  ],
78
78
  "dependencies": {
79
79
  "@aws-sdk/client-ses": "^3.40.0",
80
- "@directus/app": "9.12.1",
81
- "@directus/drive": "9.12.1",
82
- "@directus/drive-azure": "9.12.1",
83
- "@directus/drive-gcs": "9.12.1",
84
- "@directus/drive-s3": "9.12.1",
85
- "@directus/extensions-sdk": "9.12.1",
86
- "@directus/format-title": "9.12.1",
87
- "@directus/schema": "9.12.1",
88
- "@directus/shared": "9.12.1",
89
- "@directus/specs": "9.12.1",
80
+ "@directus/app": "9.12.2",
81
+ "@directus/drive": "9.12.2",
82
+ "@directus/drive-azure": "9.12.2",
83
+ "@directus/drive-gcs": "9.12.2",
84
+ "@directus/drive-s3": "9.12.2",
85
+ "@directus/extensions-sdk": "9.12.2",
86
+ "@directus/format-title": "9.12.2",
87
+ "@directus/schema": "9.12.2",
88
+ "@directus/shared": "9.12.2",
89
+ "@directus/specs": "9.12.2",
90
90
  "@godaddy/terminus": "^4.9.0",
91
91
  "@rollup/plugin-alias": "^3.1.9",
92
92
  "@rollup/plugin-virtual": "^2.0.3",
@@ -173,7 +173,7 @@
173
173
  "sqlite3": "^5.0.6",
174
174
  "tedious": "^13.0.0"
175
175
  },
176
- "gitHead": "cd58611aa7114dd7ef13f0819f77f9858f34ad1e",
176
+ "gitHead": "bce19abf6f52adc933ebc4f557e6959aa69d083e",
177
177
  "devDependencies": {
178
178
  "@types/async": "3.2.10",
179
179
  "@types/body-parser": "1.19.2",