directus 9.9.1 → 9.11.1

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 (67) hide show
  1. package/README.md +1 -1
  2. package/dist/app.js +3 -0
  3. package/dist/auth/drivers/oauth2.d.ts +1 -1
  4. package/dist/auth/drivers/oauth2.js +14 -11
  5. package/dist/auth/drivers/openid.d.ts +1 -1
  6. package/dist/auth/drivers/openid.js +14 -11
  7. package/dist/cli/commands/schema/apply.js +4 -3
  8. package/dist/controllers/assets.js +8 -9
  9. package/dist/database/helpers/date/dialects/sqlite.js +6 -2
  10. package/dist/database/index.js +5 -0
  11. package/dist/database/migrations/20210225A-add-relations-sort-field.js +2 -1
  12. package/dist/database/migrations/20210506A-rename-interfaces.js +2 -1
  13. package/dist/database/migrations/20210802A-replace-groups.js +2 -1
  14. package/dist/database/migrations/20210805A-update-groups.js +2 -1
  15. package/dist/database/migrations/20210805B-change-image-metadata-structure.js +3 -2
  16. package/dist/database/migrations/20211007A-update-presets.js +5 -4
  17. package/dist/database/run-ast.js +10 -14
  18. package/dist/database/system-data/fields/activity.yaml +3 -0
  19. package/dist/database/system-data/fields/dashboards.yaml +3 -1
  20. package/dist/database/system-data/fields/notifications.yaml +3 -1
  21. package/dist/database/system-data/fields/panels.yaml +3 -1
  22. package/dist/database/system-data/fields/shares.yaml +3 -1
  23. package/dist/env.js +193 -10
  24. package/dist/exceptions/index.d.ts +1 -0
  25. package/dist/exceptions/index.js +1 -0
  26. package/dist/exceptions/invalid-provider.d.ts +4 -0
  27. package/dist/exceptions/invalid-provider.js +10 -0
  28. package/dist/exceptions/range-not-satisfiable.d.ts +2 -2
  29. package/dist/exceptions/range-not-satisfiable.js +5 -1
  30. package/dist/middleware/graphql.js +2 -1
  31. package/dist/services/assets.js +27 -1
  32. package/dist/services/authentication.js +4 -1
  33. package/dist/services/fields.js +15 -8
  34. package/dist/services/graphql.js +61 -33
  35. package/dist/services/import-export.d.ts +1 -1
  36. package/dist/services/import-export.js +14 -11
  37. package/dist/services/items.d.ts +3 -3
  38. package/dist/services/items.js +31 -3
  39. package/dist/services/payload.d.ts +2 -2
  40. package/dist/services/payload.js +9 -8
  41. package/dist/services/users.d.ts +4 -0
  42. package/dist/services/users.js +20 -0
  43. package/dist/utils/apply-query.d.ts +2 -1
  44. package/dist/utils/apply-query.js +144 -149
  45. package/dist/utils/apply-snapshot.d.ts +3 -3
  46. package/dist/utils/apply-snapshot.js +64 -49
  47. package/dist/utils/get-ast-from-query.js +1 -7
  48. package/dist/utils/get-column-path.d.ts +16 -0
  49. package/dist/utils/get-column-path.js +46 -0
  50. package/dist/utils/get-default-value.js +4 -3
  51. package/dist/utils/get-permissions.d.ts +1 -1
  52. package/dist/utils/get-permissions.js +9 -8
  53. package/dist/utils/get-relation-info.d.ts +7 -0
  54. package/dist/utils/get-relation-info.js +45 -0
  55. package/dist/utils/get-relation-type.d.ts +1 -1
  56. package/dist/utils/get-schema.js +2 -1
  57. package/dist/utils/get-snapshot.js +22 -4
  58. package/dist/utils/merge-permissions-for-share.js +1 -1
  59. package/dist/utils/parse-json.d.ts +5 -0
  60. package/dist/utils/parse-json.js +19 -0
  61. package/dist/utils/reduce-schema.js +4 -5
  62. package/dist/utils/sanitize-query.d.ts +1 -2
  63. package/dist/utils/sanitize-query.js +6 -5
  64. package/dist/utils/validate-keys.d.ts +6 -0
  65. package/dist/utils/validate-keys.js +28 -0
  66. package/dist/utils/validate-query.js +1 -1
  67. package/package.json +16 -18
package/README.md CHANGED
@@ -16,7 +16,7 @@ view, author, and manage your raw database content. Our performant and flexible
16
16
  schema, and includes rule-based permissions, event/web hooks, custom endpoints, numerous auth options, configurable
17
17
  storage adapters, and much more.
18
18
 
19
- Current database support includes: PostgreSQL, MySQL, SQLite, MS-SQL Server, OracleDB, MariaDB, and varients such as AWS
19
+ Current database support includes: PostgreSQL, MySQL, SQLite, MS-SQL Server, OracleDB, MariaDB, and variants such as AWS
20
20
  Aurora/Redshift or Google Cloud Platform SQL.
21
21
 
22
22
  Learn more at...
package/dist/app.js CHANGED
@@ -119,6 +119,9 @@ async function createApp() {
119
119
  connectSrc: ["'self'", 'https://*'],
120
120
  },
121
121
  }, (0, get_config_from_env_1.getConfigFromEnv)('CONTENT_SECURITY_POLICY_'))));
122
+ if (env_1.default.HSTS_ENABLED) {
123
+ app.use(helmet_1.default.hsts((0, get_config_from_env_1.getConfigFromEnv)('HSTS_', ['HSTS_ENABLED'])));
124
+ }
122
125
  await emitter_1.default.emitInit('app.before', { app });
123
126
  await emitter_1.default.emitInit('middlewares.before', { app });
124
127
  app.use(logger_1.expressLogger);
@@ -1,8 +1,8 @@
1
1
  import { Router } from 'express';
2
2
  import { Client } from 'openid-client';
3
- import { LocalAuthDriver } from './local';
4
3
  import { UsersService } from '../../services';
5
4
  import { AuthDriverOptions, User } from '../../types';
5
+ import { LocalAuthDriver } from './local';
6
6
  export declare class OAuth2AuthDriver extends LocalAuthDriver {
7
7
  client: Client;
8
8
  redirectUrl: string;
@@ -5,21 +5,22 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.createOAuth2AuthRouter = exports.OAuth2AuthDriver = void 0;
7
7
  const express_1 = require("express");
8
- const openid_client_1 = require("openid-client");
8
+ const flat_1 = __importDefault(require("flat"));
9
9
  const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
10
10
  const ms_1 = __importDefault(require("ms"));
11
- const flat_1 = __importDefault(require("flat"));
12
- const local_1 = require("./local");
11
+ const openid_client_1 = require("openid-client");
13
12
  const auth_1 = require("../../auth");
14
13
  const env_1 = __importDefault(require("../../env"));
15
- const services_1 = require("../../services");
16
14
  const exceptions_1 = require("../../exceptions");
15
+ const logger_1 = __importDefault(require("../../logger"));
17
16
  const respond_1 = require("../../middleware/respond");
17
+ const services_1 = require("../../services");
18
18
  const async_handler_1 = __importDefault(require("../../utils/async-handler"));
19
- const url_1 = require("../../utils/url");
20
- const logger_1 = __importDefault(require("../../logger"));
21
- const get_ip_from_req_1 = require("../../utils/get-ip-from-req");
22
19
  const get_config_from_env_1 = require("../../utils/get-config-from-env");
20
+ const get_ip_from_req_1 = require("../../utils/get-ip-from-req");
21
+ const parse_json_1 = require("../../utils/parse-json");
22
+ const url_1 = require("../../utils/url");
23
+ const local_1 = require("./local");
23
24
  class OAuth2AuthDriver extends local_1.LocalAuthDriver {
24
25
  constructor(options, config) {
25
26
  super(options, config);
@@ -78,7 +79,6 @@ class OAuth2AuthDriver extends local_1.LocalAuthDriver {
78
79
  return user === null || user === void 0 ? void 0 : user.id;
79
80
  }
80
81
  async getUserID(payload) {
81
- var _a;
82
82
  if (!payload.code || !payload.codeVerifier) {
83
83
  logger_1.default.trace('[OAuth2] No code or codeVerifier in payload');
84
84
  throw new exceptions_1.InvalidCredentialsException();
@@ -95,9 +95,9 @@ class OAuth2AuthDriver extends local_1.LocalAuthDriver {
95
95
  // Flatten response to support dot indexes
96
96
  userInfo = (0, flat_1.default)(userInfo);
97
97
  const { provider, emailKey, identifierKey, allowPublicRegistration } = this.config;
98
- const email = userInfo[emailKey !== null && emailKey !== void 0 ? emailKey : 'email'];
98
+ const email = userInfo[emailKey !== null && emailKey !== void 0 ? emailKey : 'email'] ? String(userInfo[emailKey !== null && emailKey !== void 0 ? emailKey : 'email']) : undefined;
99
99
  // Fallback to email if explicit identifier not found
100
- const identifier = (_a = userInfo[identifierKey]) !== null && _a !== void 0 ? _a : email;
100
+ const identifier = userInfo[identifierKey] ? String(userInfo[identifierKey]) : email;
101
101
  if (!identifier) {
102
102
  logger_1.default.warn(`[OAuth2] Failed to find user identifier for provider "${provider}"`);
103
103
  throw new exceptions_1.InvalidCredentialsException();
@@ -135,7 +135,7 @@ class OAuth2AuthDriver extends local_1.LocalAuthDriver {
135
135
  let authData = user.auth_data;
136
136
  if (typeof authData === 'string') {
137
137
  try {
138
- authData = JSON.parse(authData);
138
+ authData = (0, parse_json_1.parseJSON)(authData);
139
139
  }
140
140
  catch {
141
141
  logger_1.default.warn(`[OAuth2] Session data isn't valid JSON: ${authData}`);
@@ -243,6 +243,9 @@ function createOAuth2AuthRouter(providerName) {
243
243
  else if (error instanceof exceptions_1.InvalidTokenException) {
244
244
  reason = 'INVALID_TOKEN';
245
245
  }
246
+ else if (error instanceof exceptions_1.InvalidProviderException) {
247
+ reason = 'INVALID_PROVIDER';
248
+ }
246
249
  else {
247
250
  logger_1.default.warn(error, `[OAuth2] Unexpected error during OAuth2 login`);
248
251
  }
@@ -1,8 +1,8 @@
1
1
  import { Router } from 'express';
2
2
  import { Client } from 'openid-client';
3
- import { LocalAuthDriver } from './local';
4
3
  import { UsersService } from '../../services';
5
4
  import { AuthDriverOptions, User } from '../../types';
5
+ import { LocalAuthDriver } from './local';
6
6
  export declare class OpenIDAuthDriver extends LocalAuthDriver {
7
7
  client: Promise<Client>;
8
8
  redirectUrl: string;
@@ -5,21 +5,22 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.createOpenIDAuthRouter = exports.OpenIDAuthDriver = void 0;
7
7
  const express_1 = require("express");
8
- const openid_client_1 = require("openid-client");
8
+ const flat_1 = __importDefault(require("flat"));
9
9
  const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
10
10
  const ms_1 = __importDefault(require("ms"));
11
- const flat_1 = __importDefault(require("flat"));
12
- const local_1 = require("./local");
11
+ const openid_client_1 = require("openid-client");
13
12
  const auth_1 = require("../../auth");
14
13
  const env_1 = __importDefault(require("../../env"));
15
- const services_1 = require("../../services");
16
14
  const exceptions_1 = require("../../exceptions");
15
+ const logger_1 = __importDefault(require("../../logger"));
17
16
  const respond_1 = require("../../middleware/respond");
17
+ const services_1 = require("../../services");
18
18
  const async_handler_1 = __importDefault(require("../../utils/async-handler"));
19
- const url_1 = require("../../utils/url");
20
- const logger_1 = __importDefault(require("../../logger"));
21
- const get_ip_from_req_1 = require("../../utils/get-ip-from-req");
22
19
  const get_config_from_env_1 = require("../../utils/get-config-from-env");
20
+ const get_ip_from_req_1 = require("../../utils/get-ip-from-req");
21
+ const parse_json_1 = require("../../utils/parse-json");
22
+ const url_1 = require("../../utils/url");
23
+ const local_1 = require("./local");
23
24
  class OpenIDAuthDriver extends local_1.LocalAuthDriver {
24
25
  constructor(options, config) {
25
26
  super(options, config);
@@ -85,7 +86,6 @@ class OpenIDAuthDriver extends local_1.LocalAuthDriver {
85
86
  return user === null || user === void 0 ? void 0 : user.id;
86
87
  }
87
88
  async getUserID(payload) {
88
- var _a;
89
89
  if (!payload.code || !payload.codeVerifier) {
90
90
  logger_1.default.trace('[OpenID] No code or codeVerifier in payload');
91
91
  throw new exceptions_1.InvalidCredentialsException();
@@ -109,9 +109,9 @@ class OpenIDAuthDriver extends local_1.LocalAuthDriver {
109
109
  // Flatten response to support dot indexes
110
110
  userInfo = (0, flat_1.default)(userInfo);
111
111
  const { provider, identifierKey, allowPublicRegistration, requireVerifiedEmail } = this.config;
112
- const email = userInfo.email;
112
+ const email = userInfo.email ? String(userInfo.email) : undefined;
113
113
  // Fallback to email if explicit identifier not found
114
- const identifier = (_a = userInfo[identifierKey !== null && identifierKey !== void 0 ? identifierKey : 'sub']) !== null && _a !== void 0 ? _a : email;
114
+ const identifier = userInfo[identifierKey !== null && identifierKey !== void 0 ? identifierKey : 'sub'] ? String(userInfo[identifierKey !== null && identifierKey !== void 0 ? identifierKey : 'sub']) : email;
115
115
  if (!identifier) {
116
116
  logger_1.default.warn(`[OpenID] Failed to find user identifier for provider "${provider}"`);
117
117
  throw new exceptions_1.InvalidCredentialsException();
@@ -150,7 +150,7 @@ class OpenIDAuthDriver extends local_1.LocalAuthDriver {
150
150
  let authData = user.auth_data;
151
151
  if (typeof authData === 'string') {
152
152
  try {
153
- authData = JSON.parse(authData);
153
+ authData = (0, parse_json_1.parseJSON)(authData);
154
154
  }
155
155
  catch {
156
156
  logger_1.default.warn(`[OpenID] Session data isn't valid JSON: ${authData}`);
@@ -260,6 +260,9 @@ function createOpenIDAuthRouter(providerName) {
260
260
  else if (error instanceof exceptions_1.InvalidTokenException) {
261
261
  reason = 'INVALID_TOKEN';
262
262
  }
263
+ else if (error instanceof exceptions_1.InvalidProviderException) {
264
+ reason = 'INVALID_PROVIDER';
265
+ }
263
266
  else {
264
267
  logger_1.default.warn(error, `[OpenID] Unexpected error during OpenID login`);
265
268
  }
@@ -28,12 +28,13 @@ const fs_1 = require("fs");
28
28
  const inquirer_1 = __importDefault(require("inquirer"));
29
29
  const js_yaml_1 = require("js-yaml");
30
30
  const path_1 = __importDefault(require("path"));
31
+ const cache_1 = require("../../../cache");
31
32
  const database_1 = __importStar(require("../../../database"));
32
33
  const logger_1 = __importDefault(require("../../../logger"));
34
+ const apply_snapshot_1 = require("../../../utils/apply-snapshot");
33
35
  const get_snapshot_1 = require("../../../utils/get-snapshot");
34
36
  const get_snapshot_diff_1 = require("../../../utils/get-snapshot-diff");
35
- const apply_snapshot_1 = require("../../../utils/apply-snapshot");
36
- const cache_1 = require("../../../cache");
37
+ const parse_json_1 = require("../../../utils/parse-json");
37
38
  async function apply(snapshotPath, options) {
38
39
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
39
40
  const filename = path_1.default.resolve(process.cwd(), snapshotPath);
@@ -52,7 +53,7 @@ async function apply(snapshotPath, options) {
52
53
  snapshot = (await (0, js_yaml_1.load)(fileContents));
53
54
  }
54
55
  else {
55
- snapshot = JSON.parse(fileContents);
56
+ snapshot = (0, parse_json_1.parseJSON)(fileContents);
56
57
  }
57
58
  const currentSnapshot = await (0, get_snapshot_1.getSnapshot)({ database });
58
59
  const snapshotDiff = (0, get_snapshot_diff_1.getSnapshotDiff)(currentSnapshot, snapshot);
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const express_1 = require("express");
7
+ const helmet_1 = __importDefault(require("helmet"));
7
8
  const lodash_1 = require("lodash");
8
9
  const ms_1 = __importDefault(require("ms"));
9
10
  const constants_1 = require("../constants");
@@ -14,9 +15,8 @@ const use_collection_1 = __importDefault(require("../middleware/use-collection")
14
15
  const services_1 = require("../services");
15
16
  const assets_1 = require("../types/assets");
16
17
  const async_handler_1 = __importDefault(require("../utils/async-handler"));
17
- const helmet_1 = __importDefault(require("helmet"));
18
- const lodash_2 = require("lodash");
19
18
  const get_config_from_env_1 = require("../utils/get-config-from-env");
19
+ const parse_json_1 = require("../utils/parse-json");
20
20
  const router = (0, express_1.Router)();
21
21
  router.use((0, use_collection_1.default)('directus_files'));
22
22
  router.get('/:pk',
@@ -41,7 +41,7 @@ router.get('/:pk',
41
41
  let transforms;
42
42
  // Try parse the JSON array
43
43
  try {
44
- transforms = JSON.parse(transformation['transforms']);
44
+ transforms = (0, parse_json_1.parseJSON)(transformation['transforms']);
45
45
  }
46
46
  catch {
47
47
  throw new exceptions_1.InvalidQueryException(`"transforms" Parameter needs to be a JSON array of allowed transformations.`);
@@ -91,7 +91,7 @@ router.get('/:pk',
91
91
  return next();
92
92
  throw new exceptions_1.InvalidQueryException(`Dynamic asset generation has been disabled for this project.`);
93
93
  }
94
- }), helmet_1.default.contentSecurityPolicy((0, lodash_2.merge)({
94
+ }), helmet_1.default.contentSecurityPolicy((0, lodash_1.merge)({
95
95
  useDefaults: false,
96
96
  directives: {
97
97
  defaultSrc: ['none'],
@@ -110,11 +110,10 @@ router.get('/:pk',
110
110
  : res.locals.transformation;
111
111
  let range = undefined;
112
112
  if (req.headers.range) {
113
- // substring 6 = "bytes="
114
- const rangeParts = req.headers.range.substring(6).split('-');
113
+ const rangeParts = /bytes=([0-9]*)-([0-9]*)/.exec(req.headers.range);
115
114
  range = {
116
- start: rangeParts[0] ? Number(rangeParts[0]) : 0,
117
- end: rangeParts[1] ? Number(rangeParts[1]) : undefined,
115
+ start: (rangeParts === null || rangeParts === void 0 ? void 0 : rangeParts[1]) ? Number(rangeParts[1]) : undefined,
116
+ end: (rangeParts === null || rangeParts === void 0 ? void 0 : rangeParts[2]) ? Number(rangeParts[2]) : undefined,
118
117
  };
119
118
  if (Number.isNaN(range.start) || Number.isNaN(range.end)) {
120
119
  throw new exceptions_1.RangeNotSatisfiableException(range);
@@ -134,7 +133,7 @@ router.get('/:pk',
134
133
  if (range) {
135
134
  res.setHeader('Content-Range', `bytes ${range.start}-${range.end || stat.size - 1}/${stat.size}`);
136
135
  res.status(206);
137
- res.setHeader('Content-Length', (range.end ? range.end + 1 : stat.size) - range.start);
136
+ res.setHeader('Content-Length', (range.end ? range.end + 1 : stat.size) - (range.start || 0));
138
137
  }
139
138
  else {
140
139
  res.setHeader('Content-Length', stat.size);
@@ -4,8 +4,12 @@ exports.DateHelperSQLite = void 0;
4
4
  const types_1 = require("../types");
5
5
  class DateHelperSQLite extends types_1.DateHelper {
6
6
  parse(date) {
7
- const newDate = new Date(date);
8
- return (newDate.getTime() - newDate.getTimezoneOffset() * 60 * 1000).toString();
7
+ // Return the time as string
8
+ if (date.length <= 8 && date.includes(':')) {
9
+ return date;
10
+ }
11
+ // Return dates in epoch milliseconds
12
+ return String(new Date(date).getTime());
9
13
  }
10
14
  fieldFlagForField(fieldType) {
11
15
  switch (fieldType) {
@@ -54,6 +54,11 @@ function getDatabase() {
54
54
  requiredEnvVars.push('DB_CONNECTION_STRING');
55
55
  }
56
56
  break;
57
+ case 'mssql':
58
+ if (!env_1.default.DB_TYPE || env_1.default.DB_TYPE === 'default') {
59
+ requiredEnvVars.push('DB_HOST', 'DB_PORT', 'DB_DATABASE', 'DB_USER', 'DB_PASSWORD');
60
+ }
61
+ break;
57
62
  default:
58
63
  requiredEnvVars.push('DB_HOST', 'DB_PORT', 'DB_DATABASE', 'DB_USER', 'DB_PASSWORD');
59
64
  }
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.down = exports.up = void 0;
4
+ const parse_json_1 = require("../../utils/parse-json");
4
5
  async function up(knex) {
5
6
  var _a;
6
7
  await knex.schema.alterTable('directus_relations', (table) => {
@@ -11,7 +12,7 @@ async function up(knex) {
11
12
  .from('directus_fields')
12
13
  .whereIn('interface', ['one-to-many', 'm2a-builder', 'many-to-many']);
13
14
  for (const field of fieldsWithSort) {
14
- const options = typeof field.options === 'string' ? JSON.parse(field.options) : (_a = field.options) !== null && _a !== void 0 ? _a : {};
15
+ const options = typeof field.options === 'string' ? (0, parse_json_1.parseJSON)(field.options) : (_a = field.options) !== null && _a !== void 0 ? _a : {};
15
16
  if ('sortField' in options) {
16
17
  await knex('directus_relations')
17
18
  .update({
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.down = exports.up = void 0;
4
+ const parse_json_1 = require("../../utils/parse-json");
4
5
  // [before, after, after-option additions]
5
6
  const changes = [
6
7
  ['button-links', 'presentation-links'],
@@ -54,7 +55,7 @@ async function up(knex) {
54
55
  .from('directus_fields')
55
56
  .where({ interface: before });
56
57
  for (const { id, options: existingOptionsRaw } of fields) {
57
- const existingOptions = typeof existingOptionsRaw === 'string' ? JSON.parse(existingOptionsRaw) : existingOptionsRaw;
58
+ const existingOptions = typeof existingOptionsRaw === 'string' ? (0, parse_json_1.parseJSON)(existingOptionsRaw) : existingOptionsRaw;
58
59
  const newOptions = {
59
60
  ...(existingOptions || {}),
60
61
  ...options,
@@ -5,13 +5,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.down = exports.up = void 0;
7
7
  const logger_1 = __importDefault(require("../../logger"));
8
+ const parse_json_1 = require("../../utils/parse-json");
8
9
  async function up(knex) {
9
10
  const dividerGroups = await knex.select('*').from('directus_fields').where('interface', '=', 'group-divider');
10
11
  for (const dividerGroup of dividerGroups) {
11
12
  const newOptions = { showHeader: true };
12
13
  if (dividerGroup.options) {
13
14
  try {
14
- const options = typeof dividerGroup.options === 'string' ? JSON.parse(dividerGroup.options) : dividerGroup.options;
15
+ const options = typeof dividerGroup.options === 'string' ? (0, parse_json_1.parseJSON)(dividerGroup.options) : dividerGroup.options;
15
16
  if (options.icon)
16
17
  newOptions.headerIcon = options.icon;
17
18
  if (options.color)
@@ -1,12 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.down = exports.up = void 0;
4
+ const parse_json_1 = require("../../utils/parse-json");
4
5
  async function up(knex) {
5
6
  const groups = await knex.select('*').from('directus_fields').where({ interface: 'group-standard' });
6
7
  const raw = [];
7
8
  const detail = [];
8
9
  for (const group of groups) {
9
- const options = typeof group.options === 'string' ? JSON.parse(group.options) : group.options || {};
10
+ const options = typeof group.options === 'string' ? (0, parse_json_1.parseJSON)(group.options) : group.options || {};
10
11
  if (options.showHeader === true) {
11
12
  detail.push(group);
12
13
  }
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.down = exports.up = void 0;
4
+ const parse_json_1 = require("../../utils/parse-json");
4
5
  // Change image metadata structure to match the output from 'exifr'
5
6
  async function up(knex) {
6
7
  const files = await knex
@@ -10,7 +11,7 @@ async function up(knex) {
10
11
  for (const { id, metadata } of files) {
11
12
  let prevMetadata;
12
13
  try {
13
- prevMetadata = JSON.parse(metadata);
14
+ prevMetadata = (0, parse_json_1.parseJSON)(metadata);
14
15
  }
15
16
  catch {
16
17
  continue;
@@ -54,7 +55,7 @@ async function down(knex) {
54
55
  .whereNotNull('metadata')
55
56
  .whereNot('metadata', '{}');
56
57
  for (const { id, metadata } of files) {
57
- const prevMetadata = JSON.parse(metadata);
58
+ const prevMetadata = (0, parse_json_1.parseJSON)(metadata);
58
59
  // Update only required if metadata has keys other than 'icc' and 'iptc'
59
60
  if (Object.keys(prevMetadata).filter((key) => key !== 'icc' && key !== 'iptc').length > 0) {
60
61
  // Put all data under 'exif' and rename/move keys afterwards
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.down = exports.up = void 0;
4
4
  const nanoid_1 = require("nanoid");
5
+ const parse_json_1 = require("../../utils/parse-json");
5
6
  async function up(knex) {
6
7
  var _a;
7
8
  await knex.schema.alterTable('directus_presets', (table) => {
@@ -12,7 +13,7 @@ async function up(knex) {
12
13
  .from('directus_presets');
13
14
  for (const preset of presets) {
14
15
  if (preset.filters) {
15
- const oldFilters = (_a = (typeof preset.filters === 'string' ? JSON.parse(preset.filters) : preset.filters)) !== null && _a !== void 0 ? _a : [];
16
+ const oldFilters = (_a = (typeof preset.filters === 'string' ? (0, parse_json_1.parseJSON)(preset.filters) : preset.filters)) !== null && _a !== void 0 ? _a : [];
16
17
  if (oldFilters.length === 0)
17
18
  continue;
18
19
  const newFilter = {
@@ -34,7 +35,7 @@ async function up(knex) {
34
35
  }
35
36
  }
36
37
  if (preset.layout_query) {
37
- const layoutQuery = typeof preset.layout_query === 'string' ? JSON.parse(preset.layout_query) : preset.layout_query;
38
+ const layoutQuery = typeof preset.layout_query === 'string' ? (0, parse_json_1.parseJSON)(preset.layout_query) : preset.layout_query;
38
39
  for (const [layout, query] of Object.entries(layoutQuery)) {
39
40
  if (query.sort) {
40
41
  query.sort = [query.sort];
@@ -61,7 +62,7 @@ async function down(knex) {
61
62
  .from('directus_presets');
62
63
  for (const preset of presets) {
63
64
  if (preset.filter) {
64
- const newFilter = (_a = (typeof preset.filter === 'string' ? JSON.parse(preset.filter) : preset.filter)) !== null && _a !== void 0 ? _a : {};
65
+ const newFilter = (_a = (typeof preset.filter === 'string' ? (0, parse_json_1.parseJSON)(preset.filter) : preset.filter)) !== null && _a !== void 0 ? _a : {};
65
66
  if (Object.keys(newFilter).length === 0)
66
67
  continue;
67
68
  const oldFilters = [];
@@ -85,7 +86,7 @@ async function down(knex) {
85
86
  }
86
87
  }
87
88
  if (preset.layout_query) {
88
- const layoutQuery = typeof preset.layout_query === 'string' ? JSON.parse(preset.layout_query) : preset.layout_query;
89
+ const layoutQuery = typeof preset.layout_query === 'string' ? (0, parse_json_1.parseJSON)(preset.layout_query) : preset.layout_query;
89
90
  for (const [layout, query] of Object.entries(layoutQuery)) {
90
91
  if (query.sort && Array.isArray(query.sort)) {
91
92
  query.sort = (_l = (_k = query.sort) === null || _k === void 0 ? void 0 : _k[0]) !== null && _l !== void 0 ? _l : null;
@@ -93,14 +93,9 @@ async function parseCurrentLevel(schema, collection, children, query) {
93
93
  const nestedCollectionNodes = [];
94
94
  for (const child of children) {
95
95
  if (child.type === 'field') {
96
- const fieldKey = (0, strip_function_1.stripFunction)(child.name);
97
- if (columnsInCollection.includes(fieldKey) || fieldKey === '*') {
98
- columnsToSelectInternal.push(child.name); // maintain original name here (includes functions)
99
- if (query.alias) {
100
- columnsToSelectInternal.push(...Object.entries(query.alias)
101
- .filter(([_key, value]) => value === child.name)
102
- .map(([key]) => key));
103
- }
96
+ const fieldName = (0, strip_function_1.stripFunction)(child.name);
97
+ if (columnsInCollection.includes(fieldName)) {
98
+ columnsToSelectInternal.push(child.fieldKey);
104
99
  }
105
100
  continue;
106
101
  }
@@ -126,7 +121,7 @@ async function parseCurrentLevel(schema, collection, children, query) {
126
121
  const columnsToSelect = [...new Set(columnsToSelectInternal)];
127
122
  const fieldNodes = columnsToSelect.map((column) => {
128
123
  var _a;
129
- return (_a = children.find((childNode) => childNode.type === 'field' && (childNode.fieldKey === column || childNode.name === column))) !== null && _a !== void 0 ? _a : {
124
+ return (_a = children.find((childNode) => childNode.type === 'field' && childNode.fieldKey === column)) !== null && _a !== void 0 ? _a : {
130
125
  type: 'field',
131
126
  name: column,
132
127
  fieldKey: column,
@@ -137,6 +132,11 @@ async function parseCurrentLevel(schema, collection, children, query) {
137
132
  function getColumnPreprocessor(knex, schema, table) {
138
133
  const helpers = (0, helpers_1.getHelpers)(knex);
139
134
  return function (fieldNode) {
135
+ var _a;
136
+ let alias = undefined;
137
+ if (fieldNode.name !== fieldNode.fieldKey) {
138
+ alias = fieldNode.fieldKey;
139
+ }
140
140
  let field;
141
141
  if (fieldNode.type === 'field') {
142
142
  field = schema.collections[table].fields[(0, strip_function_1.stripFunction)(fieldNode.name)];
@@ -144,11 +144,7 @@ function getColumnPreprocessor(knex, schema, table) {
144
144
  else {
145
145
  field = schema.collections[fieldNode.relation.collection].fields[fieldNode.relation.field];
146
146
  }
147
- let alias = undefined;
148
- if (fieldNode.name !== fieldNode.fieldKey) {
149
- alias = fieldNode.fieldKey;
150
- }
151
- if (field.type.startsWith('geometry')) {
147
+ if ((_a = field === null || field === void 0 ? void 0 : field.type) === null || _a === void 0 ? void 0 : _a.startsWith('geometry')) {
152
148
  return helpers.st.asText(table, field.field);
153
149
  }
154
150
  return (0, get_column_1.getColumn)(knex, table, fieldNode.name, alias, schema);
@@ -37,6 +37,9 @@ fields:
37
37
 
38
38
  - field: timestamp
39
39
  display: datetime
40
+ special:
41
+ - date-created
42
+ - cast-timestamp
40
43
  options:
41
44
  relative: true
42
45
  width: half
@@ -8,7 +8,9 @@ fields:
8
8
  - field: panels
9
9
  special: o2m
10
10
  - field: date_created
11
- special: date-created
11
+ special:
12
+ - date-created
13
+ - cast-timestamp
12
14
  - field: user_created
13
15
  special: user-created
14
16
  - field: note
@@ -3,7 +3,9 @@ table: directus_notifications
3
3
  fields:
4
4
  - field: id
5
5
  - field: timestamp
6
- special: date-created
6
+ special:
7
+ - date-created
8
+ - cast-timestamp
7
9
  - field: status
8
10
  - field: recipient
9
11
  - field: sender
@@ -17,7 +17,9 @@ fields:
17
17
  - field: options
18
18
  special: cast-json
19
19
  - field: date_created
20
- special: date-created
20
+ special:
21
+ - date-created
22
+ - cast-timestamp
21
23
  - field: user_created
22
24
  special: user-created
23
25
  - field: dashboard
@@ -51,7 +51,9 @@ fields:
51
51
  readonly: true
52
52
 
53
53
  - field: date_created
54
- special: date-created
54
+ special:
55
+ - date-created
56
+ - cast-timestamp
55
57
  width: half
56
58
  readonly: true
57
59
  conditions: