directus 9.13.0 → 9.14.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.
package/dist/app.js CHANGED
@@ -173,13 +173,18 @@ async function createApp() {
173
173
  // Set the App's base path according to the APIs public URL
174
174
  const html = await fs_extra_1.default.readFile(adminPath, 'utf8');
175
175
  const htmlWithBase = html.replace(/<base \/>/, `<base href="${adminUrl.toString({ rootRelative: true })}/" />`);
176
- const noCacheIndexHtmlHandler = (_req, res) => {
176
+ const sendHtml = (_req, res) => {
177
177
  res.setHeader('Cache-Control', 'no-cache');
178
+ res.setHeader('Vary', 'Origin, Cache-Control');
178
179
  res.send(htmlWithBase);
179
180
  };
180
- app.get('/admin', noCacheIndexHtmlHandler);
181
- app.use('/admin', express_1.default.static(path_1.default.join(adminPath, '..')));
182
- app.use('/admin/*', noCacheIndexHtmlHandler);
181
+ const setStaticHeaders = (res) => {
182
+ res.setHeader('Cache-Control', 'max-age=31536000, immutable');
183
+ res.setHeader('Vary', 'Origin, Cache-Control');
184
+ };
185
+ app.get('/admin', sendHtml);
186
+ app.use('/admin', express_1.default.static(path_1.default.join(adminPath, '..'), { setHeaders: setStaticHeaders }));
187
+ app.use('/admin/*', sendHtml);
183
188
  }
184
189
  // use the rate limiter - all routes for now
185
190
  if (env_1.default.RATE_LIMITER_ENABLED === true) {
@@ -51,7 +51,10 @@ class OpenIDAuthDriver extends local_1.LocalAuthDriver {
51
51
  ...clientOptionsOverrides,
52
52
  }));
53
53
  })
54
- .catch(reject);
54
+ .catch((e) => {
55
+ logger_1.default.error(e, '[OpenID] Failed to fetch provider config');
56
+ process.exit(1);
57
+ });
55
58
  });
56
59
  }
57
60
  generateCodeVerifier() {
@@ -32,7 +32,7 @@ const multipartHandler = (req, res, next) => {
32
32
  'content-type': 'application/octet-stream',
33
33
  };
34
34
  }
35
- const busboy = (0, busboy_1.default)({ headers });
35
+ const busboy = (0, busboy_1.default)({ headers, defParamCharset: 'utf8' });
36
36
  const savedFiles = [];
37
37
  const service = new services_1.FilesService({ accountability: req.accountability, schema: req.schema });
38
38
  const existingPrimaryKey = req.params.pk || undefined;
package/dist/emitter.js CHANGED
@@ -21,12 +21,17 @@ class Emitter {
21
21
  }
22
22
  async emitFilter(event, payload, meta, context) {
23
23
  const events = Array.isArray(event) ? event : [event];
24
- const listeners = events.flatMap((event) => this.filterEmitter.listeners(event));
24
+ const eventListeners = events.map((event) => ({
25
+ event,
26
+ listeners: this.filterEmitter.listeners(event),
27
+ }));
25
28
  let updatedPayload = payload;
26
- for (const listener of listeners) {
27
- const result = await listener(updatedPayload, meta, context);
28
- if (result !== undefined) {
29
- updatedPayload = result;
29
+ for (const { event, listeners } of eventListeners) {
30
+ for (const listener of listeners) {
31
+ const result = await listener(updatedPayload, { event, ...meta }, context);
32
+ if (result !== undefined) {
33
+ updatedPayload = result;
34
+ }
30
35
  }
31
36
  }
32
37
  return updatedPayload;
@@ -34,7 +39,7 @@ class Emitter {
34
39
  emitAction(event, meta, context) {
35
40
  const events = Array.isArray(event) ? event : [event];
36
41
  for (const event of events) {
37
- this.actionEmitter.emitAsync(event, meta, context).catch((err) => {
42
+ this.actionEmitter.emitAsync(event, { event, ...meta }, context).catch((err) => {
38
43
  logger_1.default.warn(`An error was thrown while executing action "${event}"`);
39
44
  logger_1.default.warn(err);
40
45
  });
@@ -42,7 +47,7 @@ class Emitter {
42
47
  }
43
48
  async emitInit(event, meta) {
44
49
  try {
45
- await this.initEmitter.emitAsync(event, meta);
50
+ await this.initEmitter.emitAsync(event, { event, ...meta });
46
51
  }
47
52
  catch (err) {
48
53
  logger_1.default.warn(`An error was thrown while executing init "${event}"`);
@@ -3,6 +3,7 @@ declare type Options = {
3
3
  collection: string;
4
4
  key?: PrimaryKey | PrimaryKey[] | null;
5
5
  query?: Record<string, any> | string | null;
6
+ emitEvents: boolean;
6
7
  permissions: string;
7
8
  };
8
9
  declare const _default: import("@directus/shared/types").OperationApiConfig<Options>;
@@ -6,7 +6,7 @@ const get_accountability_for_role_1 = require("../../utils/get-accountability-fo
6
6
  const sanitize_query_1 = require("../../utils/sanitize-query");
7
7
  exports.default = (0, utils_1.defineOperationApi)({
8
8
  id: 'item-read',
9
- handler: async ({ collection, key, query, permissions }, { accountability, database, getSchema }) => {
9
+ handler: async ({ collection, key, query, emitEvents, permissions }, { accountability, database, getSchema }) => {
10
10
  const schema = await getSchema({ database });
11
11
  let customAccountability;
12
12
  if (!permissions || permissions === '$trigger') {
@@ -35,10 +35,10 @@ exports.default = (0, utils_1.defineOperationApi)({
35
35
  else {
36
36
  const keys = (0, utils_1.toArray)(key);
37
37
  if (keys.length === 1) {
38
- result = await itemsService.readOne(keys[0], sanitizedQueryObject);
38
+ result = await itemsService.readOne(keys[0], sanitizedQueryObject, { emitEvents });
39
39
  }
40
40
  else {
41
- result = await itemsService.readMany(keys, sanitizedQueryObject);
41
+ result = await itemsService.readMany(keys, sanitizedQueryObject, { emitEvents });
42
42
  }
43
43
  }
44
44
  return result;
@@ -102,7 +102,12 @@ class ImportService {
102
102
  else {
103
103
  try {
104
104
  const parsedJson = (0, utils_1.parseJSON)(value);
105
- (0, lodash_1.set)(result, key, parsedJson);
105
+ if (typeof parsedJson === 'number') {
106
+ (0, lodash_1.set)(result, key, value);
107
+ }
108
+ else {
109
+ (0, lodash_1.set)(result, key, parsedJson);
110
+ }
106
111
  }
107
112
  catch {
108
113
  (0, lodash_1.set)(result, key, value);
@@ -5,6 +5,7 @@ import { AbstractService, AbstractServiceOptions, Item as AnyItem, MutationOptio
5
5
  export declare type QueryOptions = {
6
6
  stripNonRequested?: boolean;
7
7
  permissionsAction?: PermissionsAction;
8
+ emitEvents?: boolean;
8
9
  };
9
10
  export declare class ItemsService<Item extends AnyItem = AnyItem> implements AbstractService {
10
11
  collection: string;
@@ -221,23 +221,27 @@ class ItemsService {
221
221
  if (records === null) {
222
222
  throw new exceptions_1.ForbiddenException();
223
223
  }
224
- const filteredRecords = await emitter_1.default.emitFilter(this.eventScope === 'items' ? ['items.read', `${this.collection}.items.read`] : `${this.eventScope}.read`, records, {
225
- query,
226
- collection: this.collection,
227
- }, {
228
- database: this.knex,
229
- schema: this.schema,
230
- accountability: this.accountability,
231
- });
232
- emitter_1.default.emitAction(this.eventScope === 'items' ? ['items.read', `${this.collection}.items.read`] : `${this.eventScope}.read`, {
233
- payload: filteredRecords,
234
- query,
235
- collection: this.collection,
236
- }, {
237
- database: this.knex || (0, database_1.default)(),
238
- schema: this.schema,
239
- accountability: this.accountability,
240
- });
224
+ const filteredRecords = (opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false
225
+ ? await emitter_1.default.emitFilter(this.eventScope === 'items' ? ['items.read', `${this.collection}.items.read`] : `${this.eventScope}.read`, records, {
226
+ query,
227
+ collection: this.collection,
228
+ }, {
229
+ database: this.knex,
230
+ schema: this.schema,
231
+ accountability: this.accountability,
232
+ })
233
+ : records;
234
+ if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false) {
235
+ emitter_1.default.emitAction(this.eventScope === 'items' ? ['items.read', `${this.collection}.items.read`] : `${this.eventScope}.read`, {
236
+ payload: filteredRecords,
237
+ query,
238
+ collection: this.collection,
239
+ }, {
240
+ database: this.knex || (0, database_1.default)(),
241
+ schema: this.schema,
242
+ accountability: this.accountability,
243
+ });
244
+ }
241
245
  return filteredRecords;
242
246
  }
243
247
  /**
@@ -1,10 +1,14 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.NotificationsService = void 0;
4
7
  const items_1 = require("./items");
5
8
  const md_1 = require("../utils/md");
6
9
  const users_1 = require("./users");
7
10
  const mail_1 = require("./mail");
11
+ const logger_1 = __importDefault(require("../logger"));
8
12
  class NotificationsService extends items_1.ItemsService {
9
13
  constructor(options) {
10
14
  super('directus_notifications', options);
@@ -12,29 +16,36 @@ class NotificationsService extends items_1.ItemsService {
12
16
  this.mailService = new mail_1.MailService({ schema: this.schema, accountability: this.accountability });
13
17
  }
14
18
  async createOne(data, opts) {
19
+ const response = await super.createOne(data, opts);
15
20
  await this.sendEmail(data);
16
- return super.createOne(data, opts);
21
+ return response;
17
22
  }
18
23
  async createMany(data, opts) {
24
+ const response = await super.createMany(data, opts);
19
25
  for (const notification of data) {
20
26
  await this.sendEmail(notification);
21
27
  }
22
- return super.createMany(data, opts);
28
+ return response;
23
29
  }
24
30
  async sendEmail(data) {
25
31
  if (data.recipient) {
26
32
  const user = await this.usersService.readOne(data.recipient, { fields: ['email', 'email_notifications'] });
27
33
  if (user.email && user.email_notifications === true) {
28
- await this.mailService.send({
29
- template: {
30
- name: 'base',
31
- data: {
32
- html: data.message ? (0, md_1.md)(data.message) : '',
34
+ try {
35
+ await this.mailService.send({
36
+ template: {
37
+ name: 'base',
38
+ data: {
39
+ html: data.message ? (0, md_1.md)(data.message) : '',
40
+ },
33
41
  },
34
- },
35
- to: user.email,
36
- subject: data.subject,
37
- });
42
+ to: user.email,
43
+ subject: data.subject,
44
+ });
45
+ }
46
+ catch (error) {
47
+ logger_1.default.error(error.message);
48
+ }
38
49
  }
39
50
  }
40
51
  }
@@ -4,8 +4,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.PayloadService = void 0;
7
- const utils_1 = require("@directus/shared/utils");
8
7
  const date_fns_1 = require("date-fns");
8
+ const utils_1 = require("@directus/shared/utils");
9
9
  const flat_1 = require("flat");
10
10
  const joi_1 = __importDefault(require("joi"));
11
11
  const lodash_1 = require("lodash");
@@ -259,15 +259,18 @@ class PayloadService {
259
259
  else {
260
260
  if (value instanceof Date === false && typeof value === 'string') {
261
261
  if (dateColumn.type === 'date') {
262
- const [date] = value.split('T');
263
- const [year, month, day] = date.split('-');
264
- payload[name] = new Date(Number(year), Number(month) - 1, Number(day));
262
+ const parsedDate = (0, date_fns_1.parseISO)(value);
263
+ if (!(0, date_fns_1.isValid)(parsedDate)) {
264
+ throw new exceptions_1.InvalidPayloadException(`Invalid Date format in field "${dateColumn.field}"`);
265
+ }
266
+ payload[name] = parsedDate;
265
267
  }
266
268
  if (dateColumn.type === 'dateTime') {
267
- const [date, time] = value.split('T');
268
- const [year, month, day] = date.split('-');
269
- const [hours, minutes, seconds] = time.substring(0, 8).split(':');
270
- payload[name] = new Date(Number(year), Number(month) - 1, Number(day), Number(hours), Number(minutes), Number(seconds));
269
+ const parsedDate = (0, date_fns_1.parseISO)(value);
270
+ if (!(0, date_fns_1.isValid)(parsedDate)) {
271
+ throw new exceptions_1.InvalidPayloadException(`Invalid DateTime format in field "${dateColumn.field}"`);
272
+ }
273
+ payload[name] = parsedDate;
271
274
  }
272
275
  if (dateColumn.type === 'timestamp') {
273
276
  const newValue = this.helpers.date.writeTimestamp(value);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "directus",
3
- "version": "9.13.0",
3
+ "version": "9.14.0",
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.107.0",
80
- "@directus/app": "9.13.0",
81
- "@directus/drive": "9.13.0",
82
- "@directus/drive-azure": "9.13.0",
83
- "@directus/drive-gcs": "9.13.0",
84
- "@directus/drive-s3": "9.13.0",
85
- "@directus/extensions-sdk": "9.13.0",
86
- "@directus/format-title": "9.13.0",
87
- "@directus/schema": "9.13.0",
88
- "@directus/shared": "9.13.0",
89
- "@directus/specs": "9.13.0",
80
+ "@directus/app": "9.14.0",
81
+ "@directus/drive": "9.14.0",
82
+ "@directus/drive-azure": "9.14.0",
83
+ "@directus/drive-gcs": "9.14.0",
84
+ "@directus/drive-s3": "9.14.0",
85
+ "@directus/extensions-sdk": "9.14.0",
86
+ "@directus/format-title": "9.14.0",
87
+ "@directus/schema": "9.14.0",
88
+ "@directus/shared": "9.14.0",
89
+ "@directus/specs": "9.14.0",
90
90
  "@godaddy/terminus": "^4.10.2",
91
91
  "@rollup/plugin-alias": "^3.1.9",
92
92
  "@rollup/plugin-virtual": "^2.1.0",
@@ -174,7 +174,7 @@
174
174
  "sqlite3": "^5.0.8",
175
175
  "tedious": "^13.0.0"
176
176
  },
177
- "gitHead": "fd55233b1124a2887e04e4418f04c7a7b94398b7",
177
+ "gitHead": "d952222058dca96cf823b3e91dc146266ff89bd0",
178
178
  "devDependencies": {
179
179
  "@types/async": "3.2.13",
180
180
  "@types/body-parser": "1.19.2",