directus 9.2.2 → 9.3.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.
@@ -30,6 +30,7 @@ const logger_1 = __importDefault(require("../../../logger"));
30
30
  const get_schema_1 = require("../../../utils/get-schema");
31
31
  const services_1 = require("../../../services");
32
32
  const database_1 = __importStar(require("../../../database"));
33
+ const defaults_1 = require("../../utils/defaults");
33
34
  async function bootstrap({ skipAdminInit }) {
34
35
  logger_1.default.info('Initializing bootstrap...');
35
36
  const database = (0, database_1.default)();
@@ -75,7 +76,7 @@ async function waitForDatabase(database) {
75
76
  async function createDefaultAdmin(schema) {
76
77
  logger_1.default.info('Setting up first admin role...');
77
78
  const rolesService = new services_1.RolesService({ schema });
78
- const role = await rolesService.createOne({ name: 'Admin', admin_access: true });
79
+ const role = await rolesService.createOne(defaults_1.defaultAdminRole);
79
80
  logger_1.default.info('Adding first admin user...');
80
81
  const usersService = new services_1.UsersService({ schema });
81
82
  let adminEmail = env_1.default.ADMIN_EMAIL;
@@ -88,5 +89,5 @@ async function createDefaultAdmin(schema) {
88
89
  adminPassword = (0, nanoid_1.nanoid)(12);
89
90
  logger_1.default.info(`No admin password provided. Defaulting to "${adminPassword}"`);
90
91
  }
91
- await usersService.createOne({ email: adminEmail, password: adminPassword, role });
92
+ await usersService.createOne({ email: adminEmail, password: adminPassword, role, ...defaults_1.defaultAdminUser });
92
93
  }
@@ -15,6 +15,7 @@ const create_env_1 = __importDefault(require("../../utils/create-env"));
15
15
  const drivers_1 = require("../../utils/drivers");
16
16
  const questions_1 = require("./questions");
17
17
  const generate_hash_1 = require("../../../utils/generate-hash");
18
+ const defaults_1 = require("../../utils/defaults");
18
19
  async function init() {
19
20
  const rootPath = process.cwd();
20
21
  const { client } = await inquirer_1.default.prompt([
@@ -79,19 +80,14 @@ async function init() {
79
80
  const roleID = (0, uuid_1.v4)();
80
81
  await db('directus_roles').insert({
81
82
  id: roleID,
82
- name: 'Administrator',
83
- icon: 'verified',
84
- admin_access: true,
85
- description: 'Initial administrative role with unrestricted App/API access',
83
+ ...defaults_1.defaultAdminRole,
86
84
  });
87
85
  await db('directus_users').insert({
88
86
  id: userID,
89
- status: 'active',
90
87
  email: firstUser.email,
91
88
  password: firstUser.password,
92
- first_name: 'Admin',
93
- last_name: 'User',
94
89
  role: roleID,
90
+ ...defaults_1.defaultAdminUser,
95
91
  });
96
92
  await db.destroy();
97
93
  process.stdout.write(`\nYour project has been created at ${chalk_1.default.green(rootPath)}.\n`);
@@ -0,0 +1,11 @@
1
+ export declare const defaultAdminRole: {
2
+ name: string;
3
+ icon: string;
4
+ admin_access: boolean;
5
+ description: string;
6
+ };
7
+ export declare const defaultAdminUser: {
8
+ status: string;
9
+ first_name: string;
10
+ last_name: string;
11
+ };
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.defaultAdminUser = exports.defaultAdminRole = void 0;
4
+ exports.defaultAdminRole = {
5
+ name: 'Administrator',
6
+ icon: 'verified',
7
+ admin_access: true,
8
+ description: '$t:admin_description',
9
+ };
10
+ exports.defaultAdminUser = {
11
+ status: 'active',
12
+ first_name: 'Admin',
13
+ last_name: 'User',
14
+ };
package/dist/emitter.d.ts CHANGED
@@ -1,10 +1,9 @@
1
1
  import { ActionHandler, FilterHandler, HookContext, InitHandler } from './types';
2
- declare class Emitter {
2
+ export declare class Emitter {
3
3
  private filterEmitter;
4
4
  private actionEmitter;
5
5
  private initEmitter;
6
6
  constructor();
7
- eventsToEmit(event: string, meta: Record<string, any>): string[];
8
7
  emitFilter<T>(event: string, payload: T, meta: Record<string, any>, context: HookContext): Promise<T>;
9
8
  emitAction(event: string, meta: Record<string, any>, context: HookContext): void;
10
9
  emitInit(event: string, meta: Record<string, any>): Promise<void>;
@@ -14,6 +13,8 @@ declare class Emitter {
14
13
  offFilter(event: string, handler: FilterHandler): void;
15
14
  offAction(event: string, handler: ActionHandler): void;
16
15
  offInit(event: string, handler: InitHandler): void;
16
+ offAll(): void;
17
+ private eventsToEmit;
17
18
  }
18
19
  declare const emitter: Emitter;
19
20
  export default emitter;
package/dist/emitter.js CHANGED
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.Emitter = void 0;
6
7
  const eventemitter2_1 = require("eventemitter2");
7
8
  const logger_1 = __importDefault(require("./logger"));
8
9
  class Emitter {
@@ -18,12 +19,6 @@ class Emitter {
18
19
  this.actionEmitter = new eventemitter2_1.EventEmitter2(emitterOptions);
19
20
  this.initEmitter = new eventemitter2_1.EventEmitter2(emitterOptions);
20
21
  }
21
- eventsToEmit(event, meta) {
22
- if (event.startsWith('items')) {
23
- return [event, `${meta.collection}.${event}`];
24
- }
25
- return [event];
26
- }
27
22
  async emitFilter(event, payload, meta, context) {
28
23
  const events = this.eventsToEmit(event, meta);
29
24
  const listeners = events.flatMap((event) => this.filterEmitter.listeners(event));
@@ -72,6 +67,18 @@ class Emitter {
72
67
  offInit(event, handler) {
73
68
  this.initEmitter.off(event, handler);
74
69
  }
70
+ offAll() {
71
+ this.filterEmitter.removeAllListeners();
72
+ this.actionEmitter.removeAllListeners();
73
+ this.initEmitter.removeAllListeners();
74
+ }
75
+ eventsToEmit(event, meta) {
76
+ if (event.startsWith('items')) {
77
+ return [event, `${meta.collection}.${event}`];
78
+ }
79
+ return [event];
80
+ }
75
81
  }
82
+ exports.Emitter = Emitter;
76
83
  const emitter = new Emitter();
77
84
  exports.default = emitter;
@@ -7,6 +7,7 @@ declare class ExtensionManager {
7
7
  private appExtensions;
8
8
  private apiHooks;
9
9
  private apiEndpoints;
10
+ private apiEmitter;
10
11
  private endpointRouter;
11
12
  private isScheduleHookEnabled;
12
13
  constructor();
@@ -28,7 +28,7 @@ const path_1 = __importDefault(require("path"));
28
28
  const node_1 = require("@directus/shared/utils/node");
29
29
  const constants_1 = require("@directus/shared/constants");
30
30
  const database_1 = __importDefault(require("./database"));
31
- const emitter_1 = __importDefault(require("./emitter"));
31
+ const emitter_1 = __importStar(require("./emitter"));
32
32
  const env_1 = __importDefault(require("./env"));
33
33
  const exceptions = __importStar(require("./exceptions"));
34
34
  const sharedExceptions = __importStar(require("@directus/shared/exceptions"));
@@ -61,6 +61,7 @@ class ExtensionManager {
61
61
  this.apiHooks = [];
62
62
  this.apiEndpoints = [];
63
63
  this.isScheduleHookEnabled = true;
64
+ this.apiEmitter = new emitter_1.Emitter();
64
65
  this.endpointRouter = (0, express_1.Router)();
65
66
  }
66
67
  async initialize({ schedule } = { schedule: true }) {
@@ -92,6 +93,7 @@ class ExtensionManager {
92
93
  logger_1.default.info('Reloading extensions');
93
94
  this.unregisterHooks();
94
95
  this.unregisterEndpoints();
96
+ this.apiEmitter.offAll();
95
97
  if (env_1.default.SERVE_APP) {
96
98
  this.appExtensions = {};
97
99
  }
@@ -237,6 +239,7 @@ class ExtensionManager {
237
239
  exceptions: { ...exceptions, ...sharedExceptions },
238
240
  env: env_1.default,
239
241
  database: (0, database_1.default)(),
242
+ emitter: this.apiEmitter,
240
243
  logger: logger_1.default,
241
244
  getSchema: get_schema_1.getSchema,
242
245
  });
@@ -254,6 +257,7 @@ class ExtensionManager {
254
257
  exceptions: { ...exceptions, ...sharedExceptions },
255
258
  env: env_1.default,
256
259
  database: (0, database_1.default)(),
260
+ emitter: this.apiEmitter,
257
261
  logger: logger_1.default,
258
262
  getSchema: get_schema_1.getSchema,
259
263
  });
@@ -64,14 +64,20 @@ class FilesService extends items_1.ItemsService {
64
64
  payload.filesize = size;
65
65
  if (['image/jpeg', 'image/png', 'image/webp', 'image/gif', 'image/tiff'].includes(payload.type)) {
66
66
  const buffer = await storage_1.default.disk(data.storage).getBuffer(payload.filename_disk);
67
- const meta = await (0, sharp_1.default)(buffer.content, {}).metadata();
68
- if (meta.orientation && meta.orientation >= 5) {
69
- payload.height = meta.width;
70
- payload.width = meta.height;
67
+ try {
68
+ const meta = await (0, sharp_1.default)(buffer.content, {}).metadata();
69
+ if (meta.orientation && meta.orientation >= 5) {
70
+ payload.height = meta.width;
71
+ payload.width = meta.height;
72
+ }
73
+ else {
74
+ payload.width = meta.width;
75
+ payload.height = meta.height;
76
+ }
71
77
  }
72
- else {
73
- payload.width = meta.width;
74
- payload.height = meta.height;
78
+ catch (err) {
79
+ logger_1.default.warn(`Couldn't extract sharp metadata from file`);
80
+ logger_1.default.warn(err);
75
81
  }
76
82
  payload.metadata = {};
77
83
  try {
@@ -95,7 +101,7 @@ class FilesService extends items_1.ItemsService {
95
101
  }
96
102
  }
97
103
  catch (err) {
98
- logger_1.default.warn(`Couldn't extract metadata from file`);
104
+ logger_1.default.warn(`Couldn't extract EXIF metadata from file`);
99
105
  logger_1.default.warn(err);
100
106
  }
101
107
  }
@@ -5,6 +5,7 @@ import { Logger } from 'pino';
5
5
  import env from '../env';
6
6
  import * as exceptions from '../exceptions';
7
7
  import * as services from '../services';
8
+ import { Emitter } from '../emitter';
8
9
  import { getSchema } from '../utils/get-schema';
9
10
  import { SchemaOverview } from './schema';
10
11
  export declare type ExtensionContext = {
@@ -12,6 +13,7 @@ export declare type ExtensionContext = {
12
13
  exceptions: typeof exceptions;
13
14
  database: Knex;
14
15
  env: typeof env;
16
+ emitter: Emitter;
15
17
  logger: Logger;
16
18
  getSchema: typeof getSchema;
17
19
  };
@@ -10,6 +10,7 @@ const database_1 = __importDefault(require("../database"));
10
10
  const get_schema_1 = require("./get-schema");
11
11
  const services_1 = require("../services");
12
12
  const lodash_1 = require("lodash");
13
+ const logger_1 = __importDefault(require("../logger"));
13
14
  async function applySnapshot(snapshot, options) {
14
15
  var _a, _b, _c, _d;
15
16
  const database = (_a = options === null || options === void 0 ? void 0 : options.database) !== null && _a !== void 0 ? _a : (0, database_1.default)();
@@ -20,7 +21,13 @@ async function applySnapshot(snapshot, options) {
20
21
  const collectionsService = new services_1.CollectionsService({ knex: trx, schema });
21
22
  for (const { collection, diff } of snapshotDiff.collections) {
22
23
  if ((diff === null || diff === void 0 ? void 0 : diff[0].kind) === 'D') {
23
- await collectionsService.deleteOne(collection);
24
+ try {
25
+ await collectionsService.deleteOne(collection);
26
+ }
27
+ catch (err) {
28
+ logger_1.default.error(`Failed to delete collection "${collection}"`);
29
+ throw err;
30
+ }
24
31
  }
25
32
  if ((diff === null || diff === void 0 ? void 0 : diff[0].kind) === 'N' && diff[0].rhs) {
26
33
  // We'll nest the to-be-created fields in the same collection creation, to prevent
@@ -28,10 +35,16 @@ async function applySnapshot(snapshot, options) {
28
35
  const fields = snapshotDiff.fields
29
36
  .filter((fieldDiff) => fieldDiff.collection === collection)
30
37
  .map((fieldDiff) => fieldDiff.diff[0].rhs);
31
- await collectionsService.createOne({
32
- ...diff[0].rhs,
33
- fields,
34
- });
38
+ try {
39
+ await collectionsService.createOne({
40
+ ...diff[0].rhs,
41
+ fields,
42
+ });
43
+ }
44
+ catch (err) {
45
+ logger_1.default.error(`Failed to create collection "${collection}"`);
46
+ throw err;
47
+ }
35
48
  // Now that the fields are in for this collection, we can strip them from the field
36
49
  // edits
37
50
  snapshotDiff.fields = snapshotDiff.fields.filter((fieldDiff) => fieldDiff.collection !== collection);
@@ -41,27 +54,51 @@ async function applySnapshot(snapshot, options) {
41
54
  return field.collection === collection;
42
55
  });
43
56
  if (newValues) {
44
- await collectionsService.updateOne(collection, newValues);
57
+ try {
58
+ await collectionsService.updateOne(collection, newValues);
59
+ }
60
+ catch (err) {
61
+ logger_1.default.error(`Failed to update collection "${collection}"`);
62
+ throw err;
63
+ }
45
64
  }
46
65
  }
47
66
  }
48
67
  const fieldsService = new services_1.FieldsService({ knex: trx, schema: await (0, get_schema_1.getSchema)({ database: trx }) });
49
68
  for (const { collection, field, diff } of snapshotDiff.fields) {
50
69
  if ((diff === null || diff === void 0 ? void 0 : diff[0].kind) === 'N') {
51
- await fieldsService.createField(collection, diff[0].rhs);
70
+ try {
71
+ await fieldsService.createField(collection, diff[0].rhs);
72
+ }
73
+ catch (err) {
74
+ logger_1.default.error(`Failed to create field "${collection}.${field}"`);
75
+ throw err;
76
+ }
52
77
  }
53
78
  if ((diff === null || diff === void 0 ? void 0 : diff[0].kind) === 'E' || (diff === null || diff === void 0 ? void 0 : diff[0].kind) === 'A') {
54
79
  const newValues = snapshot.fields.find((snapshotField) => {
55
80
  return snapshotField.collection === collection && snapshotField.field === field;
56
81
  });
57
82
  if (newValues) {
58
- await fieldsService.updateField(collection, {
59
- ...newValues,
60
- });
83
+ try {
84
+ await fieldsService.updateField(collection, {
85
+ ...newValues,
86
+ });
87
+ }
88
+ catch (err) {
89
+ logger_1.default.error(`Failed to update field "${collection}.${field}"`);
90
+ throw err;
91
+ }
61
92
  }
62
93
  }
63
94
  if ((diff === null || diff === void 0 ? void 0 : diff[0].kind) === 'D') {
64
- await fieldsService.deleteField(collection, field);
95
+ try {
96
+ await fieldsService.deleteField(collection, field);
97
+ }
98
+ catch (err) {
99
+ logger_1.default.error(`Failed to delete field "${collection}.${field}"`);
100
+ throw err;
101
+ }
65
102
  // Field deletion also cleans up the relationship. We should ignore any relationship
66
103
  // changes attached to this now non-existing field
67
104
  snapshotDiff.relations = snapshotDiff.relations.filter((relation) => (relation.collection === collection && relation.field === field) === false);
@@ -74,18 +111,36 @@ async function applySnapshot(snapshot, options) {
74
111
  (0, lodash_1.set)(structure, diffEdit.path, undefined);
75
112
  }
76
113
  if ((diff === null || diff === void 0 ? void 0 : diff[0].kind) === 'N') {
77
- await relationsService.createOne(diff[0].rhs);
114
+ try {
115
+ await relationsService.createOne(diff[0].rhs);
116
+ }
117
+ catch (err) {
118
+ logger_1.default.error(`Failed to create relation "${collection}.${field}"`);
119
+ throw err;
120
+ }
78
121
  }
79
122
  if ((diff === null || diff === void 0 ? void 0 : diff[0].kind) === 'E' || (diff === null || diff === void 0 ? void 0 : diff[0].kind) === 'A') {
80
123
  const newValues = snapshot.relations.find((relation) => {
81
124
  return relation.collection === collection && relation.field === field;
82
125
  });
83
126
  if (newValues) {
84
- await relationsService.updateOne(collection, field, newValues);
127
+ try {
128
+ await relationsService.updateOne(collection, field, newValues);
129
+ }
130
+ catch (err) {
131
+ logger_1.default.error(`Failed to update relation "${collection}.${field}"`);
132
+ throw err;
133
+ }
85
134
  }
86
135
  }
87
136
  if ((diff === null || diff === void 0 ? void 0 : diff[0].kind) === 'D') {
88
- await relationsService.deleteOne(collection, field);
137
+ try {
138
+ await relationsService.deleteOne(collection, field);
139
+ }
140
+ catch (err) {
141
+ logger_1.default.error(`Failed to delete relation "${collection}.${field}"`);
142
+ throw err;
143
+ }
89
144
  }
90
145
  }
91
146
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "directus",
3
- "version": "9.2.2",
3
+ "version": "9.3.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.",
@@ -76,16 +76,16 @@
76
76
  ],
77
77
  "dependencies": {
78
78
  "@aws-sdk/client-ses": "^3.40.0",
79
- "@directus/app": "9.2.2",
80
- "@directus/drive": "9.2.2",
81
- "@directus/drive-azure": "9.2.2",
82
- "@directus/drive-gcs": "9.2.2",
83
- "@directus/drive-s3": "9.2.2",
84
- "@directus/extensions-sdk": "9.2.2",
85
- "@directus/format-title": "9.2.2",
86
- "@directus/schema": "9.2.2",
87
- "@directus/shared": "9.2.2",
88
- "@directus/specs": "9.2.2",
79
+ "@directus/app": "9.3.0",
80
+ "@directus/drive": "9.3.0",
81
+ "@directus/drive-azure": "9.3.0",
82
+ "@directus/drive-gcs": "9.3.0",
83
+ "@directus/drive-s3": "9.3.0",
84
+ "@directus/extensions-sdk": "9.3.0",
85
+ "@directus/format-title": "9.3.0",
86
+ "@directus/schema": "9.3.0",
87
+ "@directus/shared": "9.3.0",
88
+ "@directus/specs": "9.3.0",
89
89
  "@godaddy/terminus": "^4.9.0",
90
90
  "@rollup/plugin-alias": "^3.1.2",
91
91
  "@rollup/plugin-virtual": "^2.0.3",
@@ -169,7 +169,7 @@
169
169
  "sqlite3": "^5.0.2",
170
170
  "tedious": "^13.0.0"
171
171
  },
172
- "gitHead": "546d525175c7ecc6ac9c235b4ebf77a1ae209e10",
172
+ "gitHead": "4b444baf5b73467a74f6d57c33277146bc86285c",
173
173
  "devDependencies": {
174
174
  "@types/async": "3.2.10",
175
175
  "@types/atob": "2.1.2",
@@ -209,7 +209,7 @@
209
209
  "copyfiles": "2.4.1",
210
210
  "cross-env": "7.0.3",
211
211
  "jest": "27.3.1",
212
- "knex-mock-client": "^1.6.1",
212
+ "knex-mock-client": "1.6.1",
213
213
  "ts-jest": "27.0.7",
214
214
  "ts-node-dev": "1.1.8",
215
215
  "typescript": "4.5.2"