directus 9.13.0 → 9.14.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 (61) hide show
  1. package/dist/app.js +9 -4
  2. package/dist/auth/drivers/openid.js +4 -1
  3. package/dist/cli/commands/security/key.d.ts +1 -0
  4. package/dist/cli/commands/security/key.js +8 -0
  5. package/dist/cli/commands/security/secret.d.ts +1 -0
  6. package/dist/cli/commands/security/secret.js +8 -0
  7. package/dist/cli/index.js +6 -0
  8. package/dist/controllers/extensions.js +9 -1
  9. package/dist/controllers/files.js +1 -1
  10. package/dist/database/helpers/fn/dialects/mssql.d.ts +2 -2
  11. package/dist/database/helpers/fn/dialects/mssql.js +2 -2
  12. package/dist/database/helpers/fn/dialects/mysql.d.ts +2 -2
  13. package/dist/database/helpers/fn/dialects/mysql.js +2 -2
  14. package/dist/database/helpers/fn/dialects/oracle.d.ts +1 -1
  15. package/dist/database/helpers/fn/dialects/oracle.js +2 -2
  16. package/dist/database/helpers/fn/dialects/postgres.d.ts +2 -2
  17. package/dist/database/helpers/fn/dialects/postgres.js +2 -2
  18. package/dist/database/helpers/fn/dialects/sqlite.d.ts +1 -1
  19. package/dist/database/helpers/fn/dialects/sqlite.js +2 -2
  20. package/dist/database/helpers/fn/types.d.ts +3 -2
  21. package/dist/database/helpers/fn/types.js +11 -8
  22. package/dist/database/helpers/index.d.ts +3 -3
  23. package/dist/database/helpers/schema/dialects/sqlite.d.ts +5 -0
  24. package/dist/database/helpers/schema/dialects/sqlite.js +17 -0
  25. package/dist/database/helpers/schema/index.d.ts +1 -1
  26. package/dist/database/helpers/schema/index.js +4 -4
  27. package/dist/database/helpers/schema/types.d.ts +2 -0
  28. package/dist/database/helpers/schema/types.js +6 -0
  29. package/dist/database/run-ast.js +8 -5
  30. package/dist/emitter.js +12 -7
  31. package/dist/logger.d.ts +0 -1
  32. package/dist/middleware/authenticate.d.ts +0 -1
  33. package/dist/middleware/cache.js +3 -3
  34. package/dist/middleware/graphql.js +9 -7
  35. package/dist/middleware/respond.js +5 -5
  36. package/dist/operations/item-read/index.d.ts +1 -0
  37. package/dist/operations/item-read/index.js +3 -3
  38. package/dist/operations/request/index.js +1 -1
  39. package/dist/services/authorization.js +15 -5
  40. package/dist/services/fields.js +4 -0
  41. package/dist/services/graphql/index.js +40 -15
  42. package/dist/services/import-export.js +6 -1
  43. package/dist/services/items.d.ts +1 -0
  44. package/dist/services/items.js +21 -17
  45. package/dist/services/notifications.js +22 -11
  46. package/dist/services/payload.js +11 -8
  47. package/dist/services/server.js +13 -2
  48. package/dist/types/ast.d.ts +11 -4
  49. package/dist/utils/apply-query.js +5 -13
  50. package/dist/utils/apply-snapshot.js +41 -17
  51. package/dist/utils/filter-items.js +3 -6
  52. package/dist/utils/get-ast-from-query.js +18 -0
  53. package/dist/utils/get-column-path.d.ts +5 -1
  54. package/dist/utils/get-column-path.js +3 -1
  55. package/dist/utils/get-column.d.ts +2 -2
  56. package/dist/utils/get-column.js +2 -2
  57. package/dist/utils/get-config-from-env.js +1 -1
  58. package/dist/utils/merge-permissions.d.ts +0 -1
  59. package/package.json +232 -226
  60. package/dist/utils/generate-joi.d.ts +0 -3
  61. package/dist/utils/generate-joi.js +0 -145
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() {
@@ -0,0 +1 @@
1
+ export default function generateKey(): Promise<void>;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const uuid_1 = require("uuid");
4
+ async function generateKey() {
5
+ process.stdout.write((0, uuid_1.v4)());
6
+ process.exit(0);
7
+ }
8
+ exports.default = generateKey;
@@ -0,0 +1 @@
1
+ export default function generateSecret(): Promise<void>;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const nanoid_1 = require("nanoid");
4
+ async function generateSecret() {
5
+ process.stdout.write((0, nanoid_1.nanoid)(32));
6
+ process.exit(0);
7
+ }
8
+ exports.default = generateSecret;
package/dist/cli/index.js CHANGED
@@ -13,6 +13,8 @@ const count_1 = __importDefault(require("./commands/count"));
13
13
  const install_1 = __importDefault(require("./commands/database/install"));
14
14
  const migrate_1 = __importDefault(require("./commands/database/migrate"));
15
15
  const init_1 = __importDefault(require("./commands/init"));
16
+ const key_1 = __importDefault(require("./commands/security/key"));
17
+ const secret_1 = __importDefault(require("./commands/security/secret"));
16
18
  const create_1 = __importDefault(require("./commands/roles/create"));
17
19
  const create_2 = __importDefault(require("./commands/users/create"));
18
20
  const passwd_1 = __importDefault(require("./commands/users/passwd"));
@@ -28,6 +30,10 @@ async function createCli() {
28
30
  program.version(pkg.version, '-v, --version');
29
31
  program.command('start').description('Start the Directus API').action(server_1.startServer);
30
32
  program.command('init').description('Create a new Directus Project').action(init_1.default);
33
+ // Security
34
+ const securityCommand = program.command('security');
35
+ securityCommand.command('key:generate').description('Generate the app key').action(key_1.default);
36
+ securityCommand.command('secret:generate').description('Generate the app secret').action(secret_1.default);
31
37
  const dbCommand = program.command('database');
32
38
  dbCommand.command('install').description('Install the database').action(install_1.default);
33
39
  dbCommand
@@ -9,6 +9,9 @@ const exceptions_1 = require("../exceptions");
9
9
  const extensions_1 = require("../extensions");
10
10
  const respond_1 = require("../middleware/respond");
11
11
  const utils_1 = require("@directus/shared/utils");
12
+ const ms_1 = __importDefault(require("ms"));
13
+ const env_1 = __importDefault(require("../env"));
14
+ const get_cache_headers_1 = require("../utils/get-cache-headers");
12
15
  const router = (0, express_1.Router)();
13
16
  router.get('/:type', (0, async_handler_1.default)(async (req, res, next) => {
14
17
  const type = (0, utils_1.depluralize)(req.params.type);
@@ -33,7 +36,12 @@ router.get('/:type/index.js', (0, async_handler_1.default)(async (req, res) => {
33
36
  throw new exceptions_1.RouteNotFoundException(req.path);
34
37
  }
35
38
  res.setHeader('Content-Type', 'application/javascript; charset=UTF-8');
36
- res.setHeader('Cache-Control', 'no-store');
39
+ if (env_1.default.EXTENSIONS_CACHE_TTL) {
40
+ res.setHeader('Cache-Control', (0, get_cache_headers_1.getCacheControlHeader)(req, (0, ms_1.default)(env_1.default.EXTENSIONS_CACHE_TTL)));
41
+ }
42
+ else {
43
+ res.setHeader('Cache-Control', 'no-store');
44
+ }
37
45
  res.setHeader('Vary', 'Origin, Cache-Control');
38
46
  res.end(extensionSource);
39
47
  }));
@@ -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;
@@ -1,4 +1,4 @@
1
- import { FnHelper } from '../types';
1
+ import { FnHelper, FnHelperOptions } from '../types';
2
2
  import { Knex } from 'knex';
3
3
  export declare class FnHelperMSSQL extends FnHelper {
4
4
  year(table: string, column: string): Knex.Raw;
@@ -9,5 +9,5 @@ export declare class FnHelperMSSQL extends FnHelper {
9
9
  hour(table: string, column: string): Knex.Raw;
10
10
  minute(table: string, column: string): Knex.Raw;
11
11
  second(table: string, column: string): Knex.Raw;
12
- count(table: string, column: string): Knex.Raw<any>;
12
+ count(table: string, column: string, options?: FnHelperOptions): Knex.Raw<any>;
13
13
  }
@@ -27,14 +27,14 @@ class FnHelperMSSQL extends types_1.FnHelper {
27
27
  second(table, column) {
28
28
  return this.knex.raw('DATEPART(second, ??.??)', [table, column]);
29
29
  }
30
- count(table, column) {
30
+ count(table, column, options) {
31
31
  var _a, _b, _c, _d, _e;
32
32
  const type = (_e = (_d = (_c = (_b = (_a = this.schema.collections) === null || _a === void 0 ? void 0 : _a[table]) === null || _b === void 0 ? void 0 : _b.fields) === null || _c === void 0 ? void 0 : _c[column]) === null || _d === void 0 ? void 0 : _d.type) !== null && _e !== void 0 ? _e : 'unknown';
33
33
  if (type === 'json') {
34
34
  return this.knex.raw(`(SELECT COUNT(*) FROM OPENJSON(??.??, '$'))`, [table, column]);
35
35
  }
36
36
  if (type === 'alias') {
37
- return this._relationalCount(table, column);
37
+ return this._relationalCount(table, column, options);
38
38
  }
39
39
  throw new Error(`Couldn't extract type from ${table}.${column}`);
40
40
  }
@@ -1,4 +1,4 @@
1
- import { FnHelper } from '../types';
1
+ import { FnHelper, FnHelperOptions } from '../types';
2
2
  import { Knex } from 'knex';
3
3
  export declare class FnHelperMySQL extends FnHelper {
4
4
  year(table: string, column: string): Knex.Raw;
@@ -9,5 +9,5 @@ export declare class FnHelperMySQL extends FnHelper {
9
9
  hour(table: string, column: string): Knex.Raw;
10
10
  minute(table: string, column: string): Knex.Raw;
11
11
  second(table: string, column: string): Knex.Raw;
12
- count(table: string, column: string): Knex.Raw;
12
+ count(table: string, column: string, options?: FnHelperOptions): Knex.Raw;
13
13
  }
@@ -27,14 +27,14 @@ class FnHelperMySQL extends types_1.FnHelper {
27
27
  second(table, column) {
28
28
  return this.knex.raw('SECOND(??.??)', [table, column]);
29
29
  }
30
- count(table, column) {
30
+ count(table, column, options) {
31
31
  var _a, _b, _c, _d, _e;
32
32
  const type = (_e = (_d = (_c = (_b = (_a = this.schema.collections) === null || _a === void 0 ? void 0 : _a[table]) === null || _b === void 0 ? void 0 : _b.fields) === null || _c === void 0 ? void 0 : _c[column]) === null || _d === void 0 ? void 0 : _d.type) !== null && _e !== void 0 ? _e : 'unknown';
33
33
  if (type === 'json') {
34
34
  return this.knex.raw('JSON_LENGTH(??.??)', [table, column]);
35
35
  }
36
36
  if (type === 'alias') {
37
- return this._relationalCount(table, column);
37
+ return this._relationalCount(table, column, options);
38
38
  }
39
39
  throw new Error(`Couldn't extract type from ${table}.${column}`);
40
40
  }
@@ -9,5 +9,5 @@ export declare class FnHelperOracle extends FnHelper {
9
9
  hour(table: string, column: string, options: FnHelperOptions): Knex.Raw;
10
10
  minute(table: string, column: string, options: FnHelperOptions): Knex.Raw;
11
11
  second(table: string, column: string, options: FnHelperOptions): Knex.Raw;
12
- count(table: string, column: string): Knex.Raw<any>;
12
+ count(table: string, column: string, options?: FnHelperOptions): Knex.Raw<any>;
13
13
  }
@@ -33,14 +33,14 @@ class FnHelperOracle extends types_1.FnHelper {
33
33
  second(table, column, options) {
34
34
  return this.knex.raw(`TO_CHAR(??.??${parseLocaltime(options === null || options === void 0 ? void 0 : options.type)}, 'SS')`, [table, column]);
35
35
  }
36
- count(table, column) {
36
+ count(table, column, options) {
37
37
  var _a, _b, _c, _d, _e;
38
38
  const type = (_e = (_d = (_c = (_b = (_a = this.schema.collections) === null || _a === void 0 ? void 0 : _a[table]) === null || _b === void 0 ? void 0 : _b.fields) === null || _c === void 0 ? void 0 : _c[column]) === null || _d === void 0 ? void 0 : _d.type) !== null && _e !== void 0 ? _e : 'unknown';
39
39
  if (type === 'json') {
40
40
  return this.knex.raw("json_value(??.??, '$.size()')", [table, column]);
41
41
  }
42
42
  if (type === 'alias') {
43
- return this._relationalCount(table, column);
43
+ return this._relationalCount(table, column, options);
44
44
  }
45
45
  throw new Error(`Couldn't extract type from ${table}.${column}`);
46
46
  }
@@ -1,4 +1,4 @@
1
- import { FnHelper } from '../types';
1
+ import { FnHelper, FnHelperOptions } from '../types';
2
2
  import { Knex } from 'knex';
3
3
  export declare class FnHelperPostgres extends FnHelper {
4
4
  year(table: string, column: string): Knex.Raw;
@@ -9,5 +9,5 @@ export declare class FnHelperPostgres extends FnHelper {
9
9
  hour(table: string, column: string): Knex.Raw;
10
10
  minute(table: string, column: string): Knex.Raw;
11
11
  second(table: string, column: string): Knex.Raw;
12
- count(table: string, column: string): Knex.Raw;
12
+ count(table: string, column: string, options?: FnHelperOptions): Knex.Raw;
13
13
  }
@@ -27,7 +27,7 @@ class FnHelperPostgres extends types_1.FnHelper {
27
27
  second(table, column) {
28
28
  return this.knex.raw('EXTRACT(SECOND FROM ??.??)', [table, column]);
29
29
  }
30
- count(table, column) {
30
+ count(table, column, options) {
31
31
  var _a, _b, _c, _d, _e;
32
32
  const type = (_e = (_d = (_c = (_b = (_a = this.schema.collections) === null || _a === void 0 ? void 0 : _a[table]) === null || _b === void 0 ? void 0 : _b.fields) === null || _c === void 0 ? void 0 : _c[column]) === null || _d === void 0 ? void 0 : _d.type) !== null && _e !== void 0 ? _e : 'unknown';
33
33
  if (type === 'json') {
@@ -38,7 +38,7 @@ class FnHelperPostgres extends types_1.FnHelper {
38
38
  ]);
39
39
  }
40
40
  if (type === 'alias') {
41
- return this._relationalCount(table, column);
41
+ return this._relationalCount(table, column, options);
42
42
  }
43
43
  throw new Error(`Couldn't extract type from ${table}.${column}`);
44
44
  }
@@ -9,5 +9,5 @@ export declare class FnHelperSQLite extends FnHelper {
9
9
  hour(table: string, column: string, options?: FnHelperOptions): Knex.Raw;
10
10
  minute(table: string, column: string, options?: FnHelperOptions): Knex.Raw;
11
11
  second(table: string, column: string, options?: FnHelperOptions): Knex.Raw;
12
- count(table: string, column: string): Knex.Raw<any>;
12
+ count(table: string, column: string, options?: FnHelperOptions): Knex.Raw<any>;
13
13
  }
@@ -57,14 +57,14 @@ class FnHelperSQLite extends types_1.FnHelper {
57
57
  column,
58
58
  ]);
59
59
  }
60
- count(table, column) {
60
+ count(table, column, options) {
61
61
  var _a, _b, _c, _d, _e;
62
62
  const type = (_e = (_d = (_c = (_b = (_a = this.schema.collections) === null || _a === void 0 ? void 0 : _a[table]) === null || _b === void 0 ? void 0 : _b.fields) === null || _c === void 0 ? void 0 : _c[column]) === null || _d === void 0 ? void 0 : _d.type) !== null && _e !== void 0 ? _e : 'unknown';
63
63
  if (type === 'json') {
64
64
  return this.knex.raw(`json_array_length(??.??, '$')`, [table, column]);
65
65
  }
66
66
  if (type === 'alias') {
67
- return this._relationalCount(table, column);
67
+ return this._relationalCount(table, column, options);
68
68
  }
69
69
  throw new Error(`Couldn't extract type from ${table}.${column}`);
70
70
  }
@@ -1,8 +1,9 @@
1
- import { SchemaOverview } from '@directus/shared/types';
1
+ import { Query, SchemaOverview } from '@directus/shared/types';
2
2
  import { Knex } from 'knex';
3
3
  import { DatabaseHelper } from '../types';
4
4
  export declare type FnHelperOptions = {
5
5
  type?: string;
6
+ query?: Query;
6
7
  };
7
8
  export declare abstract class FnHelper extends DatabaseHelper {
8
9
  protected knex: Knex;
@@ -17,5 +18,5 @@ export declare abstract class FnHelper extends DatabaseHelper {
17
18
  abstract minute(table: string, column: string, options?: FnHelperOptions): Knex.Raw;
18
19
  abstract second(table: string, column: string, options?: FnHelperOptions): Knex.Raw;
19
20
  abstract count(table: string, column: string, options?: FnHelperOptions): Knex.Raw;
20
- protected _relationalCount(table: string, column: string): Knex.Raw;
21
+ protected _relationalCount(table: string, column: string, options?: FnHelperOptions): Knex.Raw;
21
22
  }
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.FnHelper = void 0;
4
+ const apply_query_1 = require("../../../utils/apply-query");
4
5
  const types_1 = require("../types");
5
6
  class FnHelper extends types_1.DatabaseHelper {
6
7
  constructor(knex, schema) {
@@ -9,19 +10,21 @@ class FnHelper extends types_1.DatabaseHelper {
9
10
  this.schema = schema;
10
11
  this.schema = schema;
11
12
  }
12
- _relationalCount(table, column) {
13
+ _relationalCount(table, column, options) {
14
+ var _a;
13
15
  const relation = this.schema.relations.find((relation) => { var _a; return relation.related_collection === table && ((_a = relation === null || relation === void 0 ? void 0 : relation.meta) === null || _a === void 0 ? void 0 : _a.one_field) === column; });
14
16
  const currentPrimary = this.schema.collections[table].primary;
15
17
  if (!relation) {
16
18
  throw new Error(`Field ${table}.${column} isn't a nested relational collection`);
17
19
  }
18
- return this.knex.raw('(' +
19
- this.knex
20
- .count('*')
21
- .from(relation.collection)
22
- .where(relation.field, '=', this.knex.raw(`??.??`, [table, currentPrimary]))
23
- .toQuery() +
24
- ')');
20
+ let countQuery = this.knex
21
+ .count('*')
22
+ .from(relation.collection)
23
+ .where(relation.field, '=', this.knex.raw(`??.??`, [table, currentPrimary]));
24
+ if ((_a = options === null || options === void 0 ? void 0 : options.query) === null || _a === void 0 ? void 0 : _a.filter) {
25
+ countQuery = (0, apply_query_1.applyFilter)(this.knex, this.schema, countQuery, options.query.filter, relation.collection, false);
26
+ }
27
+ return this.knex.raw('(' + countQuery.toQuery() + ')');
25
28
  }
26
29
  }
27
30
  exports.FnHelper = FnHelper;
@@ -6,8 +6,8 @@ import * as geometryHelpers from './geometry';
6
6
  import * as schemaHelpers from './schema';
7
7
  export declare function getHelpers(database: Knex): {
8
8
  date: dateHelpers.postgres | dateHelpers.oracle | dateHelpers.mysql | dateHelpers.mssql | dateHelpers.sqlite;
9
- st: geometryHelpers.postgres | geometryHelpers.redshift | geometryHelpers.oracle | geometryHelpers.sqlite | geometryHelpers.mysql | geometryHelpers.mssql;
10
- schema: schemaHelpers.postgres | schemaHelpers.cockroachdb | schemaHelpers.oracle;
9
+ st: geometryHelpers.sqlite | geometryHelpers.postgres | geometryHelpers.mysql | geometryHelpers.oracle | geometryHelpers.mssql | geometryHelpers.redshift;
10
+ schema: schemaHelpers.sqlite | schemaHelpers.postgres | schemaHelpers.cockroachdb | schemaHelpers.oracle;
11
11
  };
12
- export declare function getFunctions(database: Knex, schema: SchemaOverview): fnHelpers.postgres | fnHelpers.oracle | fnHelpers.sqlite | fnHelpers.mysql | fnHelpers.mssql;
12
+ export declare function getFunctions(database: Knex, schema: SchemaOverview): fnHelpers.sqlite | fnHelpers.postgres | fnHelpers.mysql | fnHelpers.oracle | fnHelpers.mssql;
13
13
  export declare type Helpers = ReturnType<typeof getHelpers>;
@@ -0,0 +1,5 @@
1
+ import { SchemaHelper } from '../types';
2
+ export declare class SchemaHelperSQLite extends SchemaHelper {
3
+ preColumnDelete(): Promise<boolean>;
4
+ postColumnDelete(): Promise<void>;
5
+ }
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SchemaHelperSQLite = void 0;
4
+ const types_1 = require("../types");
5
+ class SchemaHelperSQLite extends types_1.SchemaHelper {
6
+ async preColumnDelete() {
7
+ const foreignCheckStatus = (await this.knex.raw('PRAGMA foreign_keys'))[0].foreign_keys === 1;
8
+ if (foreignCheckStatus) {
9
+ await this.knex.raw('PRAGMA foreign_keys = OFF');
10
+ }
11
+ return foreignCheckStatus;
12
+ }
13
+ async postColumnDelete() {
14
+ await this.knex.raw('PRAGMA foreign_keys = ON');
15
+ }
16
+ }
17
+ exports.SchemaHelperSQLite = SchemaHelperSQLite;
@@ -2,6 +2,6 @@ export { SchemaHelperDefault as postgres } from './dialects/default';
2
2
  export { SchemaHelperCockroachDb as cockroachdb } from './dialects/cockroachdb';
3
3
  export { SchemaHelperDefault as redshift } from './dialects/default';
4
4
  export { SchemaHelperOracle as oracle } from './dialects/oracle';
5
- export { SchemaHelperDefault as sqlite } from './dialects/default';
5
+ export { SchemaHelperSQLite as sqlite } from './dialects/sqlite';
6
6
  export { SchemaHelperDefault as mysql } from './dialects/default';
7
7
  export { SchemaHelperDefault as mssql } from './dialects/default';
@@ -9,9 +9,9 @@ var default_2 = require("./dialects/default");
9
9
  Object.defineProperty(exports, "redshift", { enumerable: true, get: function () { return default_2.SchemaHelperDefault; } });
10
10
  var oracle_1 = require("./dialects/oracle");
11
11
  Object.defineProperty(exports, "oracle", { enumerable: true, get: function () { return oracle_1.SchemaHelperOracle; } });
12
+ var sqlite_1 = require("./dialects/sqlite");
13
+ Object.defineProperty(exports, "sqlite", { enumerable: true, get: function () { return sqlite_1.SchemaHelperSQLite; } });
12
14
  var default_3 = require("./dialects/default");
13
- Object.defineProperty(exports, "sqlite", { enumerable: true, get: function () { return default_3.SchemaHelperDefault; } });
15
+ Object.defineProperty(exports, "mysql", { enumerable: true, get: function () { return default_3.SchemaHelperDefault; } });
14
16
  var default_4 = require("./dialects/default");
15
- Object.defineProperty(exports, "mysql", { enumerable: true, get: function () { return default_4.SchemaHelperDefault; } });
16
- var default_5 = require("./dialects/default");
17
- Object.defineProperty(exports, "mssql", { enumerable: true, get: function () { return default_5.SchemaHelperDefault; } });
17
+ Object.defineProperty(exports, "mssql", { enumerable: true, get: function () { return default_4.SchemaHelperDefault; } });
@@ -21,5 +21,7 @@ export declare abstract class SchemaHelper extends DatabaseHelper {
21
21
  nullable?: boolean;
22
22
  default?: any;
23
23
  }>(table: string, column: string, options: Options, cb: (builder: Knex.CreateTableBuilder, column: string, options: Options) => Knex.ColumnBuilder): Promise<void>;
24
+ preColumnDelete(): Promise<boolean>;
25
+ postColumnDelete(): Promise<void>;
24
26
  }
25
27
  export {};
@@ -85,5 +85,11 @@ class SchemaHelper extends types_1.DatabaseHelper {
85
85
  await this.changeNullable(table, column, options.nullable);
86
86
  }
87
87
  }
88
+ async preColumnDelete() {
89
+ return false;
90
+ }
91
+ async postColumnDelete() {
92
+ return;
93
+ }
88
94
  }
89
95
  exports.SchemaHelper = SchemaHelper;
@@ -92,7 +92,7 @@ async function parseCurrentLevel(schema, collection, children, query) {
92
92
  const columnsToSelectInternal = [];
93
93
  const nestedCollectionNodes = [];
94
94
  for (const child of children) {
95
- if (child.type === 'field') {
95
+ if (child.type === 'field' || child.type === 'functionField') {
96
96
  const fieldName = (0, strip_function_1.stripFunction)(child.name);
97
97
  if (columnsInCollection.includes(fieldName)) {
98
98
  columnsToSelectInternal.push(child.fieldKey);
@@ -121,7 +121,7 @@ async function parseCurrentLevel(schema, collection, children, query) {
121
121
  const columnsToSelect = [...new Set(columnsToSelectInternal)];
122
122
  const fieldNodes = columnsToSelect.map((column) => {
123
123
  var _a;
124
- return (_a = children.find((childNode) => childNode.type === 'field' && childNode.fieldKey === column)) !== null && _a !== void 0 ? _a : {
124
+ return (_a = children.find((childNode) => (childNode.type === 'field' || childNode.type === 'functionField') && childNode.fieldKey === column)) !== null && _a !== void 0 ? _a : {
125
125
  type: 'field',
126
126
  name: column,
127
127
  fieldKey: column,
@@ -138,7 +138,7 @@ function getColumnPreprocessor(knex, schema, table) {
138
138
  alias = fieldNode.fieldKey;
139
139
  }
140
140
  let field;
141
- if (fieldNode.type === 'field') {
141
+ if (fieldNode.type === 'field' || fieldNode.type === 'functionField') {
142
142
  field = schema.collections[table].fields[(0, strip_function_1.stripFunction)(fieldNode.name)];
143
143
  }
144
144
  else {
@@ -147,6 +147,9 @@ function getColumnPreprocessor(knex, schema, table) {
147
147
  if ((_a = field === null || field === void 0 ? void 0 : field.type) === null || _a === void 0 ? void 0 : _a.startsWith('geometry')) {
148
148
  return helpers.st.asText(table, field.field);
149
149
  }
150
+ if (fieldNode.type === 'functionField') {
151
+ return (0, get_column_1.getColumn)(knex, table, fieldNode.name, alias, schema, fieldNode.query);
152
+ }
150
153
  return (0, get_column_1.getColumn)(knex, table, fieldNode.name, alias, schema);
151
154
  };
152
155
  }
@@ -299,7 +302,7 @@ function removeTemporaryFields(schema, rawItem, ast, primaryKeyField, parentItem
299
302
  if (!nestedCollectionNodes[relatedCollection])
300
303
  nestedCollectionNodes[relatedCollection] = [];
301
304
  for (const child of ast.children[relatedCollection]) {
302
- if (child.type === 'field') {
305
+ if (child.type === 'field' || child.type === 'functionField') {
303
306
  fields[relatedCollection].push(child.name);
304
307
  }
305
308
  else {
@@ -325,7 +328,7 @@ function removeTemporaryFields(schema, rawItem, ast, primaryKeyField, parentItem
325
328
  const nestedCollectionNodes = [];
326
329
  for (const child of ast.children) {
327
330
  fields.push(child.fieldKey);
328
- if (child.type !== 'field') {
331
+ if (child.type !== 'field' && child.type !== 'functionField') {
329
332
  nestedCollectionNodes.push(child);
330
333
  }
331
334
  }
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}"`);
package/dist/logger.d.ts CHANGED
@@ -1,4 +1,3 @@
1
- /// <reference types="qs" />
2
1
  import { RequestHandler } from 'express';
3
2
  import pino from 'pino';
4
3
  declare const logger: pino.Logger;
@@ -1,4 +1,3 @@
1
- /// <reference types="qs" />
2
1
  import { NextFunction, Request, Response } from 'express';
3
2
  /**
4
3
  * Verify the passed JWT and assign the user ID and role to `req`
@@ -10,15 +10,15 @@ const get_cache_headers_1 = require("../utils/get-cache-headers");
10
10
  const get_cache_key_1 = require("../utils/get-cache-key");
11
11
  const logger_1 = __importDefault(require("../logger"));
12
12
  const checkCacheMiddleware = (0, async_handler_1.default)(async (req, res, next) => {
13
- var _a, _b;
13
+ var _a, _b, _c;
14
14
  const { cache } = (0, cache_1.getCache)();
15
- if (req.method.toLowerCase() !== 'get')
15
+ if (req.method.toLowerCase() !== 'get' && ((_a = req.path) === null || _a === void 0 ? void 0 : _a.startsWith('/graphql')) === false)
16
16
  return next();
17
17
  if (env_1.default.CACHE_ENABLED !== true)
18
18
  return next();
19
19
  if (!cache)
20
20
  return next();
21
- if (((_a = req.headers['cache-control']) === null || _a === void 0 ? void 0 : _a.includes('no-store')) || ((_b = req.headers['Cache-Control']) === null || _b === void 0 ? void 0 : _b.includes('no-store'))) {
21
+ if (((_b = req.headers['cache-control']) === null || _b === void 0 ? void 0 : _b.includes('no-store')) || ((_c = req.headers['Cache-Control']) === null || _c === void 0 ? void 0 : _c.includes('no-store'))) {
22
22
  if (env_1.default.CACHE_STATUS_HEADER)
23
23
  res.setHeader(`${env_1.default.CACHE_STATUS_HEADER}`, 'MISS');
24
24
  return next();
@@ -47,14 +47,16 @@ exports.parseGraphQL = (0, async_handler_1.default)(async (req, res, next) => {
47
47
  graphqlErrors: [err],
48
48
  });
49
49
  }
50
+ const operationAST = (0, graphql_1.getOperationAST)(document, operationName);
50
51
  // You can only do `query` through GET
51
- if (req.method === 'GET') {
52
- const operationAST = (0, graphql_1.getOperationAST)(document, operationName);
53
- if ((operationAST === null || operationAST === void 0 ? void 0 : operationAST.operation) !== 'query') {
54
- throw new exceptions_1.MethodNotAllowedException(`Can only perform a ${operationAST === null || operationAST === void 0 ? void 0 : operationAST.operation} from a POST request.`, {
55
- allow: ['POST'],
56
- });
57
- }
52
+ if (req.method === 'GET' && (operationAST === null || operationAST === void 0 ? void 0 : operationAST.operation) !== 'query') {
53
+ throw new exceptions_1.MethodNotAllowedException(`Can only perform a ${operationAST === null || operationAST === void 0 ? void 0 : operationAST.operation} from a POST request.`, {
54
+ allow: ['POST'],
55
+ });
56
+ }
57
+ // Prevent caching responses when mutations are made
58
+ if ((operationAST === null || operationAST === void 0 ? void 0 : operationAST.operation) === 'mutation') {
59
+ res.locals.cache = false;
58
60
  }
59
61
  res.locals.graphqlParams = { document, query, variables, operationName, contextValue: { req, res } };
60
62
  return next();
@@ -16,7 +16,7 @@ const get_date_formatted_1 = require("../utils/get-date-formatted");
16
16
  const get_string_byte_size_1 = require("../utils/get-string-byte-size");
17
17
  const bytes_1 = require("bytes");
18
18
  exports.respond = (0, async_handler_1.default)(async (req, res) => {
19
- var _a, _b, _c;
19
+ var _a, _b, _c, _d;
20
20
  const { cache } = (0, cache_1.getCache)();
21
21
  let exceedsMaxSize = false;
22
22
  if (env_1.default.CACHE_VALUE_MAX_SIZE !== false) {
@@ -24,7 +24,7 @@ exports.respond = (0, async_handler_1.default)(async (req, res) => {
24
24
  const maxSize = (0, bytes_1.parse)(env_1.default.CACHE_VALUE_MAX_SIZE);
25
25
  exceedsMaxSize = valueSize > maxSize;
26
26
  }
27
- if (req.method.toLowerCase() === 'get' &&
27
+ if ((req.method.toLowerCase() === 'get' || ((_a = req.path) === null || _a === void 0 ? void 0 : _a.startsWith('/graphql'))) &&
28
28
  env_1.default.CACHE_ENABLED === true &&
29
29
  cache &&
30
30
  !req.sanitizedQuery.export &&
@@ -59,17 +59,17 @@ exports.respond = (0, async_handler_1.default)(async (req, res) => {
59
59
  if (req.sanitizedQuery.export === 'json') {
60
60
  res.attachment(`${filename}.json`);
61
61
  res.set('Content-Type', 'application/json');
62
- return res.status(200).send(exportService.transform((_a = res.locals.payload) === null || _a === void 0 ? void 0 : _a.data, 'json'));
62
+ return res.status(200).send(exportService.transform((_b = res.locals.payload) === null || _b === void 0 ? void 0 : _b.data, 'json'));
63
63
  }
64
64
  if (req.sanitizedQuery.export === 'xml') {
65
65
  res.attachment(`${filename}.xml`);
66
66
  res.set('Content-Type', 'text/xml');
67
- return res.status(200).send(exportService.transform((_b = res.locals.payload) === null || _b === void 0 ? void 0 : _b.data, 'xml'));
67
+ return res.status(200).send(exportService.transform((_c = res.locals.payload) === null || _c === void 0 ? void 0 : _c.data, 'xml'));
68
68
  }
69
69
  if (req.sanitizedQuery.export === 'csv') {
70
70
  res.attachment(`${filename}.csv`);
71
71
  res.set('Content-Type', 'text/csv');
72
- return res.status(200).send(exportService.transform((_c = res.locals.payload) === null || _c === void 0 ? void 0 : _c.data, 'csv'));
72
+ return res.status(200).send(exportService.transform((_d = res.locals.payload) === null || _d === void 0 ? void 0 : _d.data, 'csv'));
73
73
  }
74
74
  }
75
75
  if (Buffer.isBuffer(res.locals.payload)) {
@@ -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>;