directus 9.8.0 → 9.9.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.
Files changed (52) hide show
  1. package/dist/__mocks__/cache.d.ts +5 -0
  2. package/dist/__mocks__/cache.js +7 -0
  3. package/dist/auth/drivers/ldap.js +10 -11
  4. package/dist/auth/drivers/oauth2.js +9 -4
  5. package/dist/auth/drivers/openid.js +7 -4
  6. package/dist/cli/commands/schema/apply.js +9 -3
  7. package/dist/controllers/assets.js +5 -0
  8. package/dist/controllers/files.d.ts +2 -0
  9. package/dist/controllers/files.js +10 -5
  10. package/dist/database/helpers/date/dialects/mssql.d.ts +4 -0
  11. package/dist/database/helpers/date/dialects/mssql.js +12 -0
  12. package/dist/database/helpers/date/dialects/mysql.d.ts +5 -0
  13. package/dist/database/helpers/date/dialects/mysql.js +16 -0
  14. package/dist/database/helpers/date/dialects/oracle.d.ts +4 -0
  15. package/dist/database/helpers/date/dialects/oracle.js +15 -0
  16. package/dist/database/helpers/date/dialects/sqlite.d.ts +1 -0
  17. package/dist/database/helpers/date/dialects/sqlite.js +8 -0
  18. package/dist/database/helpers/date/index.d.ts +3 -3
  19. package/dist/database/helpers/date/index.js +6 -6
  20. package/dist/database/helpers/date/types.d.ts +3 -0
  21. package/dist/database/helpers/date/types.js +10 -0
  22. package/dist/database/helpers/index.d.ts +1 -1
  23. package/dist/database/migrations/20220402A-remove-default-value-panel-icon.d.ts +3 -0
  24. package/dist/database/migrations/20220402A-remove-default-value-panel-icon.js +22 -0
  25. package/dist/database/system-data/fields/settings.yaml +0 -1
  26. package/dist/database/system-data/fields/users.yaml +3 -0
  27. package/dist/env.js +3 -1
  28. package/dist/exceptions/index.d.ts +1 -0
  29. package/dist/exceptions/index.js +1 -0
  30. package/dist/exceptions/token-expired.d.ts +4 -0
  31. package/dist/exceptions/token-expired.js +10 -0
  32. package/dist/middleware/cache.js +10 -0
  33. package/dist/services/authorization.js +72 -30
  34. package/dist/services/collections.d.ts +2 -0
  35. package/dist/services/collections.js +8 -0
  36. package/dist/services/fields.js +24 -1
  37. package/dist/services/files.d.ts +5 -1
  38. package/dist/services/files.js +59 -40
  39. package/dist/services/graphql.d.ts +2 -3
  40. package/dist/services/graphql.js +39 -9
  41. package/dist/services/payload.d.ts +1 -0
  42. package/dist/services/payload.js +19 -16
  43. package/dist/types/files.d.ts +8 -0
  44. package/dist/utils/apply-query.js +17 -7
  45. package/dist/utils/apply-snapshot.d.ts +3 -1
  46. package/dist/utils/apply-snapshot.js +34 -5
  47. package/dist/utils/get-graphql-type.js +1 -0
  48. package/dist/utils/get-local-type.js +5 -0
  49. package/dist/utils/jwt.js +1 -1
  50. package/dist/utils/validate-query.js +19 -15
  51. package/example.env +4 -0
  52. package/package.json +14 -13
@@ -0,0 +1,5 @@
1
+ /// <reference types="jest" />
2
+ export declare const getCache: jest.Mock<any, any>;
3
+ export declare const flushCaches: jest.Mock<any, any>;
4
+ export declare const clearSystemCache: jest.Mock<any, any>;
5
+ export declare const setSystemCache: jest.Mock<any, any>;
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.setSystemCache = exports.clearSystemCache = exports.flushCaches = exports.getCache = void 0;
4
+ exports.getCache = jest.fn().mockReturnValue({ cache: undefined, systemCache: undefined, lockCache: undefined });
5
+ exports.flushCaches = jest.fn();
6
+ exports.clearSystemCache = jest.fn();
7
+ exports.setSystemCache = jest.fn();
@@ -71,12 +71,8 @@ class LDAPAuthDriver extends auth_1.AuthDriver {
71
71
  res.on('searchEntry', () => {
72
72
  resolve();
73
73
  });
74
- res.on('error', (err) => {
75
- if (!(err instanceof ldapjs_1.OperationsError)) {
76
- reject(handleError(err));
77
- return;
78
- }
79
- // Rebind on OperationsError
74
+ res.on('error', () => {
75
+ // Attempt to rebind on search error
80
76
  this.bindClient.bind(bindDn, bindPassword, (err) => {
81
77
  if (err) {
82
78
  const error = handleError(err);
@@ -93,8 +89,8 @@ class LDAPAuthDriver extends auth_1.AuthDriver {
93
89
  });
94
90
  });
95
91
  res.on('end', (result) => {
92
+ // Handle edge case where authenticated bind user cannot read their own DN
96
93
  if ((result === null || result === void 0 ? void 0 : result.status) === 0) {
97
- // Handle edge case where authenticated bind user could not fetch their own DN
98
94
  reject(new exceptions_1.UnexpectedResponseException('Failed to find bind user record'));
99
95
  }
100
96
  });
@@ -177,12 +173,12 @@ class LDAPAuthDriver extends auth_1.AuthDriver {
177
173
  return user === null || user === void 0 ? void 0 : user.id;
178
174
  }
179
175
  async getUserID(payload) {
180
- var _a;
176
+ var _a, _b, _c;
181
177
  if (!payload.identifier) {
182
178
  throw new exceptions_1.InvalidCredentialsException();
183
179
  }
184
180
  await this.validateBindClient();
185
- const { userDn, userScope, userAttribute, groupDn, groupScope, groupAttribute } = this.config;
181
+ const { userDn, userScope, userAttribute, groupDn, groupScope, groupAttribute, defaultRoleId } = this.config;
186
182
  const userInfo = await this.fetchUserInfo(userDn, new ldapjs_1.EqualityFilter({
187
183
  attribute: userAttribute !== null && userAttribute !== void 0 ? userAttribute : 'cn',
188
184
  value: payload.identifier,
@@ -209,7 +205,10 @@ class LDAPAuthDriver extends auth_1.AuthDriver {
209
205
  }
210
206
  const userId = await this.fetchUserId(userInfo.dn);
211
207
  if (userId) {
212
- await this.usersService.updateOne(userId, { role: (_a = userRole === null || userRole === void 0 ? void 0 : userRole.id) !== null && _a !== void 0 ? _a : null });
208
+ // Only sync roles if the AD groups are configured
209
+ if (groupDn) {
210
+ await this.usersService.updateOne(userId, { role: (_b = (_a = userRole === null || userRole === void 0 ? void 0 : userRole.id) !== null && _a !== void 0 ? _a : defaultRoleId) !== null && _b !== void 0 ? _b : null });
211
+ }
213
212
  return userId;
214
213
  }
215
214
  if (!userInfo) {
@@ -221,7 +220,7 @@ class LDAPAuthDriver extends auth_1.AuthDriver {
221
220
  last_name: userInfo.lastName,
222
221
  email: userInfo.email,
223
222
  external_identifier: userInfo.dn,
224
- role: userRole === null || userRole === void 0 ? void 0 : userRole.id,
223
+ role: (_c = userRole === null || userRole === void 0 ? void 0 : userRole.id) !== null && _c !== void 0 ? _c : defaultRoleId,
225
224
  });
226
225
  return (await this.fetchUserId(userInfo.dn));
227
226
  }
@@ -8,6 +8,7 @@ const express_1 = require("express");
8
8
  const openid_client_1 = require("openid-client");
9
9
  const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
10
10
  const ms_1 = __importDefault(require("ms"));
11
+ const flat_1 = __importDefault(require("flat"));
11
12
  const local_1 = require("./local");
12
13
  const auth_1 = require("../../auth");
13
14
  const env_1 = __importDefault(require("../../env"));
@@ -91,12 +92,14 @@ class OAuth2AuthDriver extends local_1.LocalAuthDriver {
91
92
  catch (e) {
92
93
  throw handleError(e);
93
94
  }
94
- const { emailKey, identifierKey, allowPublicRegistration } = this.config;
95
+ // Flatten response to support dot indexes
96
+ userInfo = (0, flat_1.default)(userInfo);
97
+ const { provider, emailKey, identifierKey, allowPublicRegistration } = this.config;
95
98
  const email = userInfo[emailKey !== null && emailKey !== void 0 ? emailKey : 'email'];
96
99
  // Fallback to email if explicit identifier not found
97
100
  const identifier = (_a = userInfo[identifierKey]) !== null && _a !== void 0 ? _a : email;
98
101
  if (!identifier) {
99
- logger_1.default.warn(`[OAuth2] Failed to find user identifier for provider "${this.config.provider}"`);
102
+ logger_1.default.warn(`[OAuth2] Failed to find user identifier for provider "${provider}"`);
100
103
  throw new exceptions_1.InvalidCredentialsException();
101
104
  }
102
105
  const userId = await this.fetchUserId(identifier);
@@ -111,11 +114,13 @@ class OAuth2AuthDriver extends local_1.LocalAuthDriver {
111
114
  }
112
115
  // Is public registration allowed?
113
116
  if (!allowPublicRegistration) {
114
- logger_1.default.trace(`[OAuth2] User doesn't exist, and public registration not allowed for provider "${this.config.provider}"`);
117
+ logger_1.default.trace(`[OAuth2] User doesn't exist, and public registration not allowed for provider "${provider}"`);
115
118
  throw new exceptions_1.InvalidCredentialsException();
116
119
  }
117
120
  await this.usersService.createOne({
118
- provider: this.config.provider,
121
+ provider,
122
+ first_name: userInfo[this.config.firstNameKey],
123
+ last_name: userInfo[this.config.lastNameKey],
119
124
  email: email,
120
125
  external_identifier: identifier,
121
126
  role: this.config.defaultRoleId,
@@ -8,6 +8,7 @@ const express_1 = require("express");
8
8
  const openid_client_1 = require("openid-client");
9
9
  const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
10
10
  const ms_1 = __importDefault(require("ms"));
11
+ const flat_1 = __importDefault(require("flat"));
11
12
  const local_1 = require("./local");
12
13
  const auth_1 = require("../../auth");
13
14
  const env_1 = __importDefault(require("../../env"));
@@ -105,12 +106,14 @@ class OpenIDAuthDriver extends local_1.LocalAuthDriver {
105
106
  catch (e) {
106
107
  throw handleError(e);
107
108
  }
108
- const { identifierKey, allowPublicRegistration, requireVerifiedEmail } = this.config;
109
+ // Flatten response to support dot indexes
110
+ userInfo = (0, flat_1.default)(userInfo);
111
+ const { provider, identifierKey, allowPublicRegistration, requireVerifiedEmail } = this.config;
109
112
  const email = userInfo.email;
110
113
  // Fallback to email if explicit identifier not found
111
114
  const identifier = (_a = userInfo[identifierKey !== null && identifierKey !== void 0 ? identifierKey : 'sub']) !== null && _a !== void 0 ? _a : email;
112
115
  if (!identifier) {
113
- logger_1.default.warn(`[OpenID] Failed to find user identifier for provider "${this.config.provider}"`);
116
+ logger_1.default.warn(`[OpenID] Failed to find user identifier for provider "${provider}"`);
114
117
  throw new exceptions_1.InvalidCredentialsException();
115
118
  }
116
119
  const userId = await this.fetchUserId(identifier);
@@ -126,11 +129,11 @@ class OpenIDAuthDriver extends local_1.LocalAuthDriver {
126
129
  const isEmailVerified = !requireVerifiedEmail || userInfo.email_verified;
127
130
  // Is public registration allowed?
128
131
  if (!allowPublicRegistration || !isEmailVerified) {
129
- logger_1.default.trace(`[OpenID] User doesn't exist, and public registration not allowed for provider "${this.config.provider}"`);
132
+ logger_1.default.trace(`[OpenID] User doesn't exist, and public registration not allowed for provider "${provider}"`);
130
133
  throw new exceptions_1.InvalidCredentialsException();
131
134
  }
132
135
  await this.usersService.createOne({
133
- provider: this.config.provider,
136
+ provider,
134
137
  first_name: userInfo.given_name,
135
138
  last_name: userInfo.family_name,
136
139
  email: email,
@@ -93,13 +93,19 @@ async function apply(snapshotPath, options) {
93
93
  if (snapshotDiff.fields.length > 0) {
94
94
  message += '\n\n' + chalk_1.default.black.underline.bold('Fields:');
95
95
  for (const { collection, field, diff } of snapshotDiff.fields) {
96
- if (((_e = diff[0]) === null || _e === void 0 ? void 0 : _e.kind) === 'E') {
96
+ if (((_e = diff[0]) === null || _e === void 0 ? void 0 : _e.kind) === 'E' || (0, apply_snapshot_1.isNestedMetaUpdate)(diff[0])) {
97
97
  message += `\n - ${chalk_1.default.blue('Update')} ${collection}.${field}`;
98
98
  for (const change of diff) {
99
+ const path = change.path.slice(1).join('.');
99
100
  if (change.kind === 'E') {
100
- const path = change.path.slice(1).join('.');
101
101
  message += `\n - Set ${path} to ${change.rhs}`;
102
102
  }
103
+ else if (change.kind === 'D') {
104
+ message += `\n - Remove ${path}`;
105
+ }
106
+ else if (change.kind === 'N') {
107
+ message += `\n - Add ${path} and set it to ${change.rhs}`;
108
+ }
103
109
  }
104
110
  }
105
111
  else if (((_f = diff[0]) === null || _f === void 0 ? void 0 : _f.kind) === 'D') {
@@ -143,7 +149,7 @@ async function apply(snapshotPath, options) {
143
149
  }
144
150
  }
145
151
  }
146
- message += 'The following changes will be applied:\n\n' + chalk_1.default.black(message);
152
+ message = 'The following changes will be applied:\n\n' + chalk_1.default.black(message);
147
153
  if (dryRun) {
148
154
  logger_1.default.info(message);
149
155
  process.exit(0);
@@ -126,6 +126,11 @@ router.get('/:pk',
126
126
  res.setHeader('Content-Type', file.type);
127
127
  res.setHeader('Accept-Ranges', 'bytes');
128
128
  res.setHeader('Cache-Control', `${access}, max-age=${(0, ms_1.default)(env_1.default.ASSETS_CACHE_TTL) / 1000}`);
129
+ const unixTime = Date.parse(file.modified_on);
130
+ if (!Number.isNaN(unixTime)) {
131
+ const lastModifiedDate = new Date(unixTime);
132
+ res.setHeader('Last-Modified', lastModifiedDate.toUTCString());
133
+ }
129
134
  if (range) {
130
135
  res.setHeader('Content-Range', `bytes ${range.start}-${range.end || stat.size - 1}/${stat.size}`);
131
136
  res.status(206);
@@ -1,2 +1,4 @@
1
+ import { RequestHandler } from 'express';
1
2
  declare const router: import("express-serve-static-core").Router;
3
+ export declare const multipartHandler: RequestHandler;
2
4
  export default router;
@@ -3,7 +3,9 @@ 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.multipartHandler = void 0;
6
7
  const format_title_1 = __importDefault(require("@directus/format-title"));
8
+ const utils_1 = require("@directus/shared/utils");
7
9
  const busboy_1 = __importDefault(require("busboy"));
8
10
  const express_1 = __importDefault(require("express"));
9
11
  const joi_1 = __importDefault(require("joi"));
@@ -15,10 +17,9 @@ const use_collection_1 = __importDefault(require("../middleware/use-collection")
15
17
  const validate_batch_1 = require("../middleware/validate-batch");
16
18
  const services_1 = require("../services");
17
19
  const async_handler_1 = __importDefault(require("../utils/async-handler"));
18
- const utils_1 = require("@directus/shared/utils");
19
20
  const router = express_1.default.Router();
20
21
  router.use((0, use_collection_1.default)('directus_files'));
21
- const multipartHandler = (0, async_handler_1.default)(async (req, res, next) => {
22
+ const multipartHandler = (req, res, next) => {
22
23
  if (req.is('multipart/form-data') === false)
23
24
  return next();
24
25
  let headers;
@@ -87,12 +88,16 @@ const multipartHandler = (0, async_handler_1.default)(async (req, res, next) =>
87
88
  req.pipe(busboy);
88
89
  function tryDone() {
89
90
  if (savedFiles.length === fileCount) {
91
+ if (fileCount === 0) {
92
+ return next(new exceptions_1.InvalidPayloadException(`No files where included in the body`));
93
+ }
90
94
  res.locals.savedFiles = savedFiles;
91
95
  return next();
92
96
  }
93
97
  }
94
- });
95
- router.post('/', multipartHandler, (0, async_handler_1.default)(async (req, res, next) => {
98
+ };
99
+ exports.multipartHandler = multipartHandler;
100
+ router.post('/', (0, async_handler_1.default)(exports.multipartHandler), (0, async_handler_1.default)(async (req, res, next) => {
96
101
  if (req.is('multipart/form-data') === false) {
97
102
  throw new exceptions_1.UnsupportedMediaTypeException(`Unsupported Content-Type header`);
98
103
  }
@@ -214,7 +219,7 @@ router.patch('/', (0, validate_batch_1.validateBatch)('update'), (0, async_handl
214
219
  }
215
220
  return next();
216
221
  }), respond_1.respond);
217
- router.patch('/:pk', multipartHandler, (0, async_handler_1.default)(async (req, res, next) => {
222
+ router.patch('/:pk', (0, async_handler_1.default)(exports.multipartHandler), (0, async_handler_1.default)(async (req, res, next) => {
218
223
  const service = new services_1.FilesService({
219
224
  accountability: req.accountability,
220
225
  schema: req.schema,
@@ -0,0 +1,4 @@
1
+ import { DateHelper } from '../types';
2
+ export declare class DateHelperMSSQL extends DateHelper {
3
+ writeTimestamp(date: string): Date;
4
+ }
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DateHelperMSSQL = void 0;
4
+ const types_1 = require("../types");
5
+ const date_fns_1 = require("date-fns");
6
+ class DateHelperMSSQL extends types_1.DateHelper {
7
+ writeTimestamp(date) {
8
+ const parsedDate = (0, date_fns_1.parseISO)(date);
9
+ return new Date(parsedDate.getTime() + parsedDate.getTimezoneOffset() * 60000);
10
+ }
11
+ }
12
+ exports.DateHelperMSSQL = DateHelperMSSQL;
@@ -0,0 +1,5 @@
1
+ import { DateHelper } from '../types';
2
+ export declare class DateHelperMySQL extends DateHelper {
3
+ readTimestampString(date: string): string;
4
+ writeTimestamp(date: string): Date;
5
+ }
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DateHelperMySQL = void 0;
4
+ const types_1 = require("../types");
5
+ const date_fns_1 = require("date-fns");
6
+ class DateHelperMySQL extends types_1.DateHelper {
7
+ readTimestampString(date) {
8
+ const parsedDate = new Date(date);
9
+ return new Date(parsedDate.getTime() - parsedDate.getTimezoneOffset() * 60000).toISOString();
10
+ }
11
+ writeTimestamp(date) {
12
+ const parsedDate = (0, date_fns_1.parseISO)(date);
13
+ return new Date(parsedDate.getTime() + parsedDate.getTimezoneOffset() * 60000);
14
+ }
15
+ }
16
+ exports.DateHelperMySQL = DateHelperMySQL;
@@ -0,0 +1,4 @@
1
+ import { DateHelper } from '../types';
2
+ export declare class DateHelperOracle extends DateHelper {
3
+ fieldFlagForField(fieldType: string): string;
4
+ }
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DateHelperOracle = void 0;
4
+ const types_1 = require("../types");
5
+ class DateHelperOracle extends types_1.DateHelper {
6
+ fieldFlagForField(fieldType) {
7
+ switch (fieldType) {
8
+ case 'dateTime':
9
+ return 'cast-datetime';
10
+ default:
11
+ return '';
12
+ }
13
+ }
14
+ }
15
+ exports.DateHelperOracle = DateHelperOracle;
@@ -1,4 +1,5 @@
1
1
  import { DateHelper } from '../types';
2
2
  export declare class DateHelperSQLite extends DateHelper {
3
3
  parse(date: string): string;
4
+ fieldFlagForField(fieldType: string): string;
4
5
  }
@@ -7,5 +7,13 @@ class DateHelperSQLite extends types_1.DateHelper {
7
7
  const newDate = new Date(date);
8
8
  return (newDate.getTime() - newDate.getTimezoneOffset() * 60 * 1000).toString();
9
9
  }
10
+ fieldFlagForField(fieldType) {
11
+ switch (fieldType) {
12
+ case 'timestamp':
13
+ return 'cast-timestamp';
14
+ default:
15
+ return '';
16
+ }
17
+ }
10
18
  }
11
19
  exports.DateHelperSQLite = DateHelperSQLite;
@@ -1,7 +1,7 @@
1
1
  export { DateHelperDefault as postgres } from './dialects/default';
2
2
  export { DateHelperDefault as redshift } from './dialects/default';
3
3
  export { DateHelperDefault as cockroachdb } from './dialects/default';
4
- export { DateHelperDefault as oracle } from './dialects/default';
5
- export { DateHelperDefault as mysql } from './dialects/default';
6
- export { DateHelperDefault as mssql } from './dialects/default';
4
+ export { DateHelperOracle as oracle } from './dialects/oracle';
5
+ export { DateHelperMySQL as mysql } from './dialects/mysql';
6
+ export { DateHelperMSSQL as mssql } from './dialects/mssql';
7
7
  export { DateHelperSQLite as sqlite } from './dialects/sqlite';
@@ -7,11 +7,11 @@ var default_2 = require("./dialects/default");
7
7
  Object.defineProperty(exports, "redshift", { enumerable: true, get: function () { return default_2.DateHelperDefault; } });
8
8
  var default_3 = require("./dialects/default");
9
9
  Object.defineProperty(exports, "cockroachdb", { enumerable: true, get: function () { return default_3.DateHelperDefault; } });
10
- var default_4 = require("./dialects/default");
11
- Object.defineProperty(exports, "oracle", { enumerable: true, get: function () { return default_4.DateHelperDefault; } });
12
- var default_5 = require("./dialects/default");
13
- Object.defineProperty(exports, "mysql", { enumerable: true, get: function () { return default_5.DateHelperDefault; } });
14
- var default_6 = require("./dialects/default");
15
- Object.defineProperty(exports, "mssql", { enumerable: true, get: function () { return default_6.DateHelperDefault; } });
10
+ var oracle_1 = require("./dialects/oracle");
11
+ Object.defineProperty(exports, "oracle", { enumerable: true, get: function () { return oracle_1.DateHelperOracle; } });
12
+ var mysql_1 = require("./dialects/mysql");
13
+ Object.defineProperty(exports, "mysql", { enumerable: true, get: function () { return mysql_1.DateHelperMySQL; } });
14
+ var mssql_1 = require("./dialects/mssql");
15
+ Object.defineProperty(exports, "mssql", { enumerable: true, get: function () { return mssql_1.DateHelperMSSQL; } });
16
16
  var sqlite_1 = require("./dialects/sqlite");
17
17
  Object.defineProperty(exports, "sqlite", { enumerable: true, get: function () { return sqlite_1.DateHelperSQLite; } });
@@ -1,4 +1,7 @@
1
1
  import { DatabaseHelper } from '../types';
2
2
  export declare abstract class DateHelper extends DatabaseHelper {
3
3
  parse(date: string): string;
4
+ readTimestampString(date: string): string;
5
+ writeTimestamp(date: string): Date;
6
+ fieldFlagForField(_fieldType: string): string;
4
7
  }
@@ -2,9 +2,19 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DateHelper = void 0;
4
4
  const types_1 = require("../types");
5
+ const date_fns_1 = require("date-fns");
5
6
  class DateHelper extends types_1.DatabaseHelper {
6
7
  parse(date) {
7
8
  return date;
8
9
  }
10
+ readTimestampString(date) {
11
+ return date;
12
+ }
13
+ writeTimestamp(date) {
14
+ return (0, date_fns_1.parseISO)(date);
15
+ }
16
+ fieldFlagForField(_fieldType) {
17
+ return '';
18
+ }
9
19
  }
10
20
  exports.DateHelper = DateHelper;
@@ -5,7 +5,7 @@ import * as fnHelpers from './fn';
5
5
  import * as geometryHelpers from './geometry';
6
6
  import * as schemaHelpers from './schema';
7
7
  export declare function getHelpers(database: Knex): {
8
- date: dateHelpers.postgres | dateHelpers.sqlite;
8
+ date: dateHelpers.postgres | dateHelpers.oracle | dateHelpers.mysql | dateHelpers.mssql | dateHelpers.sqlite;
9
9
  st: geometryHelpers.postgres | geometryHelpers.redshift | geometryHelpers.oracle | geometryHelpers.sqlite | geometryHelpers.mysql | geometryHelpers.mssql;
10
10
  schema: schemaHelpers.postgres | schemaHelpers.cockroachdb | schemaHelpers.oracle;
11
11
  };
@@ -0,0 +1,3 @@
1
+ import { Knex } from 'knex';
2
+ export declare function up(knex: Knex): Promise<void>;
3
+ export declare function down(knex: Knex): Promise<void>;
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.down = exports.up = void 0;
4
+ const helpers_1 = require("../helpers");
5
+ async function up(knex) {
6
+ const helper = (0, helpers_1.getHelpers)(knex).schema;
7
+ await helper.changeToString('directus_panels', 'icon', {
8
+ nullable: true,
9
+ default: null,
10
+ length: 30,
11
+ });
12
+ }
13
+ exports.up = up;
14
+ async function down(knex) {
15
+ const helper = (0, helpers_1.getHelpers)(knex).schema;
16
+ await helper.changeToString('directus_panels', 'icon', {
17
+ nullable: true,
18
+ default: 'insert_chart',
19
+ length: 30,
20
+ });
21
+ }
22
+ exports.down = down;
@@ -39,7 +39,6 @@ fields:
39
39
  options:
40
40
  iconRight: language
41
41
  placeholder: en-US
42
- includeProjectDefault: false
43
42
  translations:
44
43
  language: en-US
45
44
  translations: Default Language
@@ -72,6 +72,8 @@ fields:
72
72
  - field: language
73
73
  interface: system-language
74
74
  width: half
75
+ options:
76
+ includeProjectDefault: true
75
77
 
76
78
  - field: theme
77
79
  interface: select-dropdown
@@ -152,6 +154,7 @@ fields:
152
154
  - field: last_access
153
155
  width: half
154
156
  display: datetime
157
+ readonly: true
155
158
  display_options:
156
159
  relative: true
157
160
 
package/dist/env.js CHANGED
@@ -14,7 +14,7 @@ const lodash_1 = require("lodash");
14
14
  const path_1 = __importDefault(require("path"));
15
15
  const require_yaml_1 = require("./utils/require-yaml");
16
16
  const utils_1 = require("@directus/shared/utils");
17
- const acceptedEnvTypes = ['string', 'number', 'regex', 'array'];
17
+ const acceptedEnvTypes = ['string', 'number', 'regex', 'array', 'json'];
18
18
  const defaults = {
19
19
  CONFIG_PATH: path_1.default.resolve(process.cwd(), '.env'),
20
20
  HOST: '0.0.0.0',
@@ -69,6 +69,7 @@ const defaults = {
69
69
  SERVE_APP: true,
70
70
  RELATIONAL_BATCH_SIZE: 25000,
71
71
  EXPORT_BATCH_SIZE: 5000,
72
+ FILE_METADATA_ALLOW_LIST: 'ifd0.Make,ifd0.Model,exif.FNumber,exif.ExposureTime,exif.FocalLength,exif.ISO',
72
73
  };
73
74
  // Allows us to force certain environment variable into a type, instead of relying
74
75
  // on the auto-parsed type in processValues. ref #3705
@@ -82,6 +83,7 @@ const typeMap = {
82
83
  DB_PORT: 'number',
83
84
  DB_EXCLUDE_TABLES: 'array',
84
85
  IMPORT_IP_DENY_LIST: 'array',
86
+ FILE_METADATA_ALLOW_LIST: 'array',
85
87
  };
86
88
  let env = {
87
89
  ...defaults,
@@ -13,6 +13,7 @@ export * from './method-not-allowed';
13
13
  export * from './range-not-satisfiable';
14
14
  export * from './route-not-found';
15
15
  export * from './service-unavailable';
16
+ export * from './token-expired';
16
17
  export * from './unprocessable-entity';
17
18
  export * from './unsupported-media-type';
18
19
  export * from './user-suspended';
@@ -25,6 +25,7 @@ __exportStar(require("./method-not-allowed"), exports);
25
25
  __exportStar(require("./range-not-satisfiable"), exports);
26
26
  __exportStar(require("./route-not-found"), exports);
27
27
  __exportStar(require("./service-unavailable"), exports);
28
+ __exportStar(require("./token-expired"), exports);
28
29
  __exportStar(require("./unprocessable-entity"), exports);
29
30
  __exportStar(require("./unsupported-media-type"), exports);
30
31
  __exportStar(require("./user-suspended"), exports);
@@ -0,0 +1,4 @@
1
+ import { BaseException } from '@directus/shared/exceptions';
2
+ export declare class TokenExpiredException extends BaseException {
3
+ constructor(message?: string);
4
+ }
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TokenExpiredException = void 0;
4
+ const exceptions_1 = require("@directus/shared/exceptions");
5
+ class TokenExpiredException extends exceptions_1.BaseException {
6
+ constructor(message = 'Token expired.') {
7
+ super(message, 401, 'TOKEN_EXPIRED');
8
+ }
9
+ }
10
+ exports.TokenExpiredException = TokenExpiredException;
@@ -19,6 +19,8 @@ const checkCacheMiddleware = (0, async_handler_1.default)(async (req, res, next)
19
19
  if (!cache)
20
20
  return next();
21
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'))) {
22
+ if (env_1.default.CACHE_STATUS_HEADER)
23
+ res.setHeader(`${env_1.default.CACHE_STATUS_HEADER}`, 'MISS');
22
24
  return next();
23
25
  }
24
26
  const key = (0, get_cache_key_1.getCacheKey)(req);
@@ -28,6 +30,8 @@ const checkCacheMiddleware = (0, async_handler_1.default)(async (req, res, next)
28
30
  }
29
31
  catch (err) {
30
32
  logger_1.default.warn(err, `[cache] Couldn't read key ${key}. ${err.message}`);
33
+ if (env_1.default.CACHE_STATUS_HEADER)
34
+ res.setHeader(`${env_1.default.CACHE_STATUS_HEADER}`, 'MISS');
31
35
  return next();
32
36
  }
33
37
  if (cachedData) {
@@ -37,14 +41,20 @@ const checkCacheMiddleware = (0, async_handler_1.default)(async (req, res, next)
37
41
  }
38
42
  catch (err) {
39
43
  logger_1.default.warn(err, `[cache] Couldn't read key ${`${key}__expires_at`}. ${err.message}`);
44
+ if (env_1.default.CACHE_STATUS_HEADER)
45
+ res.setHeader(`${env_1.default.CACHE_STATUS_HEADER}`, 'MISS');
40
46
  return next();
41
47
  }
42
48
  const cacheTTL = cacheExpiryDate ? cacheExpiryDate - Date.now() : null;
43
49
  res.setHeader('Cache-Control', (0, get_cache_headers_1.getCacheControlHeader)(req, cacheTTL));
44
50
  res.setHeader('Vary', 'Origin, Cache-Control');
51
+ if (env_1.default.CACHE_STATUS_HEADER)
52
+ res.setHeader(`${env_1.default.CACHE_STATUS_HEADER}`, 'HIT');
45
53
  return res.json(cachedData);
46
54
  }
47
55
  else {
56
+ if (env_1.default.CACHE_STATUS_HEADER)
57
+ res.setHeader(`${env_1.default.CACHE_STATUS_HEADER}`, 'MISS');
48
58
  return next();
49
59
  }
50
60
  });