directus 9.4.1 → 9.5.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 (133) hide show
  1. package/README.md +1 -1
  2. package/dist/app.js +20 -1
  3. package/dist/auth/auth.d.ts +2 -1
  4. package/dist/auth/drivers/ldap.js +2 -1
  5. package/dist/auth/drivers/local.js +2 -1
  6. package/dist/auth/drivers/oauth2.js +12 -21
  7. package/dist/auth/drivers/openid.js +14 -3
  8. package/dist/cache.js +1 -3
  9. package/dist/cli/commands/init/questions.d.ts +3 -0
  10. package/dist/cli/commands/init/questions.js +2 -0
  11. package/dist/cli/index.js +1 -1
  12. package/dist/cli/utils/create-db-connection.d.ts +1 -1
  13. package/dist/cli/utils/create-db-connection.js +11 -1
  14. package/dist/cli/utils/drivers.d.ts +1 -0
  15. package/dist/cli/utils/drivers.js +2 -1
  16. package/dist/controllers/activity.js +2 -1
  17. package/dist/controllers/auth.js +5 -4
  18. package/dist/controllers/extensions.js +1 -1
  19. package/dist/controllers/files.js +3 -0
  20. package/dist/controllers/utils.js +2 -0
  21. package/dist/database/helpers/date/index.d.ts +1 -0
  22. package/dist/database/helpers/date/index.js +3 -1
  23. package/dist/database/helpers/geometry/index.d.ts +1 -0
  24. package/dist/database/helpers/geometry/index.js +3 -1
  25. package/dist/database/helpers/index.d.ts +2 -0
  26. package/dist/database/helpers/index.js +2 -0
  27. package/dist/database/helpers/schema/dialects/cockroachdb.d.ts +16 -0
  28. package/dist/database/helpers/schema/dialects/cockroachdb.js +16 -0
  29. package/dist/database/helpers/schema/dialects/default.d.ts +3 -0
  30. package/dist/database/helpers/schema/dialects/default.js +7 -0
  31. package/dist/database/helpers/schema/dialects/oracle.d.ts +12 -0
  32. package/dist/database/helpers/schema/dialects/oracle.js +13 -0
  33. package/dist/database/helpers/schema/index.d.ts +7 -0
  34. package/dist/database/helpers/schema/index.js +17 -0
  35. package/dist/database/helpers/schema/types.d.ts +25 -0
  36. package/dist/database/helpers/schema/types.js +89 -0
  37. package/dist/database/index.d.ts +1 -1
  38. package/dist/database/index.js +66 -17
  39. package/dist/database/migrations/20201105B-change-webhook-url-type.js +6 -25
  40. package/dist/database/migrations/20210312A-webhooks-collections-text.js +6 -25
  41. package/dist/database/migrations/20210415A-make-filesize-nullable.js +9 -4
  42. package/dist/database/migrations/20210506A-rename-interfaces.js +1 -1
  43. package/dist/database/migrations/20210510A-restructure-relations.js +12 -4
  44. package/dist/database/migrations/20210525A-add-insights.js +2 -2
  45. package/dist/database/migrations/20210626A-change-filesize-bigint.js +5 -7
  46. package/dist/database/migrations/20210903A-add-auth-provider.js +11 -2
  47. package/dist/database/migrations/20210907A-webhooks-collections-not-null.js +6 -20
  48. package/dist/database/migrations/20210920A-webhooks-url-not-null.js +10 -14
  49. package/dist/database/migrations/20211211A-add-shares.js +2 -2
  50. package/dist/database/migrations/20211230A-add-project-descriptor.d.ts +3 -0
  51. package/dist/database/migrations/20211230A-add-project-descriptor.js +15 -0
  52. package/dist/database/migrations/run.js +1 -1
  53. package/dist/database/run-ast.d.ts +1 -1
  54. package/dist/database/seeds/01-collections.yaml +1 -0
  55. package/dist/database/seeds/02-roles.yaml +1 -0
  56. package/dist/database/seeds/03-users.yaml +1 -0
  57. package/dist/database/system-data/fields/settings.yaml +11 -1
  58. package/dist/database/system-data/relations/index.d.ts +1 -1
  59. package/dist/emitter.d.ts +3 -4
  60. package/dist/emitter.js +2 -8
  61. package/dist/env.js +3 -0
  62. package/dist/exceptions/database/translate.js +1 -0
  63. package/dist/exceptions/index.d.ts +1 -0
  64. package/dist/exceptions/index.js +1 -0
  65. package/dist/exceptions/unsupported-media-type.d.ts +4 -0
  66. package/dist/exceptions/unsupported-media-type.js +10 -0
  67. package/dist/extensions.d.ts +14 -8
  68. package/dist/extensions.js +136 -69
  69. package/dist/logger.js +22 -1
  70. package/dist/middleware/authenticate.js +2 -1
  71. package/dist/middleware/extract-token.js +1 -1
  72. package/dist/middleware/rate-limiter.js +2 -1
  73. package/dist/services/assets.js +3 -3
  74. package/dist/services/authentication.d.ts +2 -2
  75. package/dist/services/authentication.js +7 -2
  76. package/dist/services/authorization.d.ts +2 -3
  77. package/dist/services/collections.d.ts +2 -2
  78. package/dist/services/collections.js +18 -18
  79. package/dist/services/fields.d.ts +2 -3
  80. package/dist/services/fields.js +19 -15
  81. package/dist/services/graphql.d.ts +3 -2
  82. package/dist/services/graphql.js +31 -5
  83. package/dist/services/import.d.ts +2 -2
  84. package/dist/services/import.js +2 -1
  85. package/dist/services/items.d.ts +2 -2
  86. package/dist/services/items.js +22 -18
  87. package/dist/services/mail/index.d.ts +2 -2
  88. package/dist/services/meta.d.ts +2 -2
  89. package/dist/services/payload.d.ts +2 -2
  90. package/dist/services/payload.js +7 -3
  91. package/dist/services/relations.d.ts +2 -2
  92. package/dist/services/server.d.ts +2 -2
  93. package/dist/services/server.js +1 -0
  94. package/dist/services/specifications.d.ts +2 -2
  95. package/dist/services/users.d.ts +2 -3
  96. package/dist/services/utils.d.ts +2 -2
  97. package/dist/types/ast.d.ts +1 -2
  98. package/dist/types/auth.d.ts +1 -3
  99. package/dist/types/index.d.ts +0 -3
  100. package/dist/types/index.js +0 -3
  101. package/dist/types/services.d.ts +1 -3
  102. package/dist/types/snapshot.d.ts +1 -2
  103. package/dist/utils/apply-query.d.ts +1 -2
  104. package/dist/utils/apply-query.js +1 -1
  105. package/dist/utils/apply-snapshot.d.ts +2 -1
  106. package/dist/utils/get-ast-from-query.d.ts +2 -3
  107. package/dist/utils/get-ip-from-req.d.ts +2 -0
  108. package/dist/utils/get-ip-from-req.js +24 -0
  109. package/dist/utils/get-local-type.js +1 -1
  110. package/dist/utils/get-permissions.d.ts +1 -2
  111. package/dist/utils/get-permissions.js +1 -1
  112. package/dist/utils/get-relation-type.d.ts +1 -1
  113. package/dist/utils/get-schema.d.ts +1 -2
  114. package/dist/utils/get-snapshot.d.ts +2 -1
  115. package/dist/utils/md.js +1 -1
  116. package/dist/utils/merge-permissions-for-share.d.ts +1 -2
  117. package/dist/utils/reduce-schema.d.ts +1 -2
  118. package/example.env +8 -0
  119. package/package.json +22 -15
  120. package/dist/cli/index.test.d.ts +0 -1
  121. package/dist/cli/index.test.js +0 -58
  122. package/dist/middleware/cache.test.d.ts +0 -1
  123. package/dist/middleware/cache.test.js +0 -62
  124. package/dist/tests/database/migrations/run.test.d.ts +0 -1
  125. package/dist/tests/database/migrations/run.test.js +0 -29
  126. package/dist/types/extensions.d.ts +0 -43
  127. package/dist/types/extensions.js +0 -2
  128. package/dist/types/relation.d.ts +0 -21
  129. package/dist/types/relation.js +0 -2
  130. package/dist/types/schema.d.ts +0 -32
  131. package/dist/types/schema.js +0 -2
  132. package/dist/utils/get-cache-key.test.d.ts +0 -1
  133. package/dist/utils/get-cache-key.test.js +0 -53
package/README.md CHANGED
@@ -33,7 +33,7 @@ Learn more at...
33
33
 
34
34
  ## Installing
35
35
 
36
- Directus requires NodeJS 10+. Create a new project with our simple CLI tool:
36
+ Directus requires NodeJS 10+. Create a new project with our simple CLI tool:
37
37
 
38
38
  ```
39
39
  npm init directus-project my-project
package/dist/app.js CHANGED
@@ -27,6 +27,7 @@ const express_1 = __importDefault(require("express"));
27
27
  const fs_extra_1 = __importDefault(require("fs-extra"));
28
28
  const path_1 = __importDefault(require("path"));
29
29
  const qs_1 = __importDefault(require("qs"));
30
+ const helmet_1 = __importDefault(require("helmet"));
30
31
  const activity_1 = __importDefault(require("./controllers/activity"));
31
32
  const assets_1 = __importDefault(require("./controllers/assets"));
32
33
  const auth_1 = __importDefault(require("./controllers/auth"));
@@ -75,6 +76,8 @@ const webhooks_2 = require("./webhooks");
75
76
  const cache_2 = require("./cache");
76
77
  const auth_2 = require("./auth");
77
78
  const url_1 = require("./utils/url");
79
+ const get_config_from_env_1 = require("./utils/get-config-from-env");
80
+ const lodash_1 = require("lodash");
78
81
  async function createApp() {
79
82
  (0, validate_env_1.validateEnv)(['KEY', 'SECRET']);
80
83
  if (!new url_1.Url(env_1.default.PUBLIC_URL).isAbsolute()) {
@@ -96,8 +99,24 @@ async function createApp() {
96
99
  await extensionManager.initialize();
97
100
  const app = (0, express_1.default)();
98
101
  app.disable('x-powered-by');
99
- app.set('trust proxy', true);
102
+ app.set('trust proxy', env_1.default.IP_TRUST_PROXY);
100
103
  app.set('query parser', (str) => qs_1.default.parse(str, { depth: 10 }));
104
+ app.use(helmet_1.default.contentSecurityPolicy((0, lodash_1.merge)({
105
+ useDefaults: true,
106
+ directives: {
107
+ // Unsafe-eval is required for vue3 / vue-i18n / app extensions
108
+ scriptSrc: ["'self'", "'unsafe-eval'"],
109
+ // Even though this is recommended to have enabled, it breaks most local
110
+ // installations. Making this opt-in rather than opt-out is a little more
111
+ // friendly. Ref #10806
112
+ upgradeInsecureRequests: null,
113
+ // These are required for MapLibre
114
+ workerSrc: ["'self'", 'blob:'],
115
+ childSrc: ["'self'", 'blob:'],
116
+ imgSrc: ["'self'", 'data:', 'blob:'],
117
+ connectSrc: ["'self'", 'https://*'],
118
+ },
119
+ }, (0, get_config_from_env_1.getConfigFromEnv)('CONTENT_SECURITY_POLICY_'))));
101
120
  await emitter_1.default.emitInit('app.before', { app });
102
121
  await emitter_1.default.emitInit('middlewares.before', { app });
103
122
  app.use(logger_1.expressLogger);
@@ -1,5 +1,6 @@
1
+ import { SchemaOverview } from '@directus/shared/types';
1
2
  import { Knex } from 'knex';
2
- import { AuthDriverOptions, SchemaOverview, User } from '../types';
3
+ import { AuthDriverOptions, User } from '../types';
3
4
  export declare abstract class AuthDriver {
4
5
  knex: Knex;
5
6
  schema: SchemaOverview;
@@ -26,6 +26,7 @@ exports.createLDAPAuthRouter = exports.LDAPAuthDriver = void 0;
26
26
  const express_1 = require("express");
27
27
  const ldapjs_1 = __importStar(require("ldapjs"));
28
28
  const ms_1 = __importDefault(require("ms"));
29
+ const get_ip_from_req_1 = require("../../utils/get-ip-from-req");
29
30
  const joi_1 = __importDefault(require("joi"));
30
31
  const auth_1 = require("../auth");
31
32
  const exceptions_1 = require("../../exceptions");
@@ -292,7 +293,7 @@ function createLDAPAuthRouter(provider) {
292
293
  router.post('/', (0, async_handler_1.default)(async (req, res, next) => {
293
294
  var _a, _b;
294
295
  const accountability = {
295
- ip: req.ip,
296
+ ip: (0, get_ip_from_req_1.getIPFromReq)(req),
296
297
  userAgent: req.get('user-agent'),
297
298
  role: null,
298
299
  };
@@ -14,6 +14,7 @@ const async_handler_1 = __importDefault(require("../../utils/async-handler"));
14
14
  const env_1 = __importDefault(require("../../env"));
15
15
  const respond_1 = require("../../middleware/respond");
16
16
  const constants_1 = require("../../constants");
17
+ const get_ip_from_req_1 = require("../../utils/get-ip-from-req");
17
18
  class LocalAuthDriver extends auth_1.AuthDriver {
18
19
  async getUserID(payload) {
19
20
  if (!payload.email) {
@@ -50,7 +51,7 @@ function createLocalAuthRouter(provider) {
50
51
  router.post('/', (0, async_handler_1.default)(async (req, res, next) => {
51
52
  var _a;
52
53
  const accountability = {
53
- ip: req.ip,
54
+ ip: (0, get_ip_from_req_1.getIPFromReq)(req),
54
55
  userAgent: req.get('user-agent'),
55
56
  role: null,
56
57
  };
@@ -17,11 +17,12 @@ const respond_1 = require("../../middleware/respond");
17
17
  const async_handler_1 = __importDefault(require("../../utils/async-handler"));
18
18
  const url_1 = require("../../utils/url");
19
19
  const logger_1 = __importDefault(require("../../logger"));
20
+ const get_ip_from_req_1 = require("../../utils/get-ip-from-req");
20
21
  class OAuth2AuthDriver extends local_1.LocalAuthDriver {
21
22
  constructor(options, config) {
22
23
  super(options, config);
23
24
  const { authorizeUrl, accessUrl, profileUrl, clientId, clientSecret, ...additionalConfig } = config;
24
- if (!authorizeUrl || !accessUrl || !clientId || !clientSecret || !additionalConfig.provider) {
25
+ if (!authorizeUrl || !accessUrl || !profileUrl || !clientId || !clientSecret || !additionalConfig.provider) {
25
26
  throw new exceptions_1.InvalidConfigException('Invalid provider config', { provider: additionalConfig.provider });
26
27
  }
27
28
  const redirectUrl = new url_1.Url(env_1.default.PUBLIC_URL).addPath('auth', 'login', additionalConfig.provider, 'callback');
@@ -32,8 +33,7 @@ class OAuth2AuthDriver extends local_1.LocalAuthDriver {
32
33
  authorization_endpoint: authorizeUrl,
33
34
  token_endpoint: accessUrl,
34
35
  userinfo_endpoint: profileUrl,
35
- // Required for openid providers (openid flow should be preferred!)
36
- issuer: additionalConfig.issuerUrl,
36
+ issuer: additionalConfig.provider,
37
37
  });
38
38
  this.client = new issuer.Client({
39
39
  client_id: clientId,
@@ -83,16 +83,7 @@ class OAuth2AuthDriver extends local_1.LocalAuthDriver {
83
83
  let userInfo;
84
84
  try {
85
85
  tokenSet = await this.client.oauthCallback(this.redirectUrl, { code: payload.code, state: payload.state }, { code_verifier: payload.codeVerifier, state: openid_client_1.generators.codeChallenge(payload.codeVerifier) });
86
- const issuer = this.client.issuer;
87
- if (issuer.metadata.userinfo_endpoint) {
88
- userInfo = await this.client.userinfo(tokenSet.access_token);
89
- }
90
- else if (tokenSet.id_token) {
91
- userInfo = tokenSet.claims();
92
- }
93
- else {
94
- throw new exceptions_1.InvalidConfigException('OAuth profile URL not defined', { provider: this.config.provider });
95
- }
86
+ userInfo = await this.client.userinfo(tokenSet.access_token);
96
87
  }
97
88
  catch (e) {
98
89
  throw handleError(e);
@@ -102,7 +93,7 @@ class OAuth2AuthDriver extends local_1.LocalAuthDriver {
102
93
  // Fallback to email if explicit identifier not found
103
94
  const identifier = (_a = userInfo[identifierKey]) !== null && _a !== void 0 ? _a : email;
104
95
  if (!identifier) {
105
- logger_1.default.warn(`Failed to find user identifier for provider "${this.config.provider}"`);
96
+ logger_1.default.warn(`[OAuth2] Failed to find user identifier for provider "${this.config.provider}"`);
106
97
  throw new exceptions_1.InvalidCredentialsException();
107
98
  }
108
99
  const userId = await this.fetchUserId(identifier);
@@ -139,7 +130,7 @@ class OAuth2AuthDriver extends local_1.LocalAuthDriver {
139
130
  authData = JSON.parse(authData);
140
131
  }
141
132
  catch {
142
- logger_1.default.warn(`Session data isn't valid JSON: ${authData}`);
133
+ logger_1.default.warn(`[OAuth2] Session data isn't valid JSON: ${authData}`);
143
134
  }
144
135
  }
145
136
  if (authData === null || authData === void 0 ? void 0 : authData.refreshToken) {
@@ -162,12 +153,12 @@ exports.OAuth2AuthDriver = OAuth2AuthDriver;
162
153
  const handleError = (e) => {
163
154
  if (e instanceof openid_client_1.errors.OPError) {
164
155
  if (e.error === 'invalid_grant') {
165
- logger_1.default.trace(e, `[OAuth2] Invalid grant.`);
166
156
  // Invalid token
157
+ logger_1.default.trace(e, `[OAuth2] Invalid grant`);
167
158
  return new exceptions_1.InvalidTokenException();
168
159
  }
169
- logger_1.default.trace(e, `[OAuth2] Unknown OP error.`);
170
160
  // Server response error
161
+ logger_1.default.trace(e, `[OAuth2] Unknown OP error`);
171
162
  return new exceptions_1.ServiceUnavailableException('Service returned unexpected response', {
172
163
  service: 'oauth2',
173
164
  message: e.error_description,
@@ -175,10 +166,10 @@ const handleError = (e) => {
175
166
  }
176
167
  else if (e instanceof openid_client_1.errors.RPError) {
177
168
  // Internal client error
178
- logger_1.default.trace(e, `[OAuth2] Unknown RP error.`);
169
+ logger_1.default.trace(e, `[OAuth2] Unknown RP error`);
179
170
  return new exceptions_1.InvalidCredentialsException();
180
171
  }
181
- logger_1.default.trace(e, `[OAuth2] Unknown error.`);
172
+ logger_1.default.trace(e, `[OAuth2] Unknown error`);
182
173
  return e;
183
174
  };
184
175
  function createOAuth2AuthRouter(providerName) {
@@ -210,7 +201,7 @@ function createOAuth2AuthRouter(providerName) {
210
201
  const { verifier, redirect, prompt } = tokenData;
211
202
  const authenticationService = new services_1.AuthenticationService({
212
203
  accountability: {
213
- ip: req.ip,
204
+ ip: (0, get_ip_from_req_1.getIPFromReq)(req),
214
205
  userAgent: req.get('user-agent'),
215
206
  role: null,
216
207
  },
@@ -220,7 +211,7 @@ function createOAuth2AuthRouter(providerName) {
220
211
  try {
221
212
  res.clearCookie(`oauth2.${providerName}`);
222
213
  if (!req.query.code || !req.query.state) {
223
- logger_1.default.warn(`[OAuth2]Couldn't extract OAuth2 code or state from query: ${JSON.stringify(req.query)}`);
214
+ logger_1.default.warn(`[OAuth2] Couldn't extract OAuth2 code or state from query: ${JSON.stringify(req.query)}`);
224
215
  }
225
216
  authResponse = await authenticationService.login(providerName, {
226
217
  code: req.query.code,
@@ -17,6 +17,7 @@ const respond_1 = require("../../middleware/respond");
17
17
  const async_handler_1 = __importDefault(require("../../utils/async-handler"));
18
18
  const url_1 = require("../../utils/url");
19
19
  const logger_1 = __importDefault(require("../../logger"));
20
+ const get_ip_from_req_1 = require("../../utils/get-ip-from-req");
20
21
  class OpenIDAuthDriver extends local_1.LocalAuthDriver {
21
22
  constructor(options, config) {
22
23
  super(options, config);
@@ -82,6 +83,7 @@ class OpenIDAuthDriver extends local_1.LocalAuthDriver {
82
83
  async getUserID(payload) {
83
84
  var _a;
84
85
  if (!payload.code || !payload.codeVerifier) {
86
+ logger_1.default.trace('[OpenID] No code or codeVerifier in payload');
85
87
  throw new exceptions_1.InvalidCredentialsException();
86
88
  }
87
89
  let tokenSet;
@@ -105,7 +107,7 @@ class OpenIDAuthDriver extends local_1.LocalAuthDriver {
105
107
  // Fallback to email if explicit identifier not found
106
108
  const identifier = (_a = userInfo[identifierKey !== null && identifierKey !== void 0 ? identifierKey : 'sub']) !== null && _a !== void 0 ? _a : email;
107
109
  if (!identifier) {
108
- logger_1.default.warn(`Failed to find user identifier for provider "${this.config.provider}"`);
110
+ logger_1.default.warn(`[OpenID] Failed to find user identifier for provider "${this.config.provider}"`);
109
111
  throw new exceptions_1.InvalidCredentialsException();
110
112
  }
111
113
  const userId = await this.fetchUserId(identifier);
@@ -121,6 +123,7 @@ class OpenIDAuthDriver extends local_1.LocalAuthDriver {
121
123
  const isEmailVerified = !requireVerifiedEmail || userInfo.email_verified;
122
124
  // Is public registration allowed?
123
125
  if (!allowPublicRegistration || !isEmailVerified) {
126
+ logger_1.default.trace(`[OpenID] User doesn't exist, and public registration not allowed for provider "${this.config.provider}"`);
124
127
  throw new exceptions_1.InvalidCredentialsException();
125
128
  }
126
129
  await this.usersService.createOne({
@@ -144,7 +147,7 @@ class OpenIDAuthDriver extends local_1.LocalAuthDriver {
144
147
  authData = JSON.parse(authData);
145
148
  }
146
149
  catch {
147
- logger_1.default.warn(`Session data isn't valid JSON: ${authData}`);
150
+ logger_1.default.warn(`[OpenID] Session data isn't valid JSON: ${authData}`);
148
151
  }
149
152
  }
150
153
  if (authData === null || authData === void 0 ? void 0 : authData.refreshToken) {
@@ -169,9 +172,11 @@ const handleError = (e) => {
169
172
  if (e instanceof openid_client_1.errors.OPError) {
170
173
  if (e.error === 'invalid_grant') {
171
174
  // Invalid token
175
+ logger_1.default.trace(e, `[OpenID] Invalid grant`);
172
176
  return new exceptions_1.InvalidTokenException();
173
177
  }
174
178
  // Server response error
179
+ logger_1.default.trace(e, `[OpenID] Unknown OP error`);
175
180
  return new exceptions_1.ServiceUnavailableException('Service returned unexpected response', {
176
181
  service: 'openid',
177
182
  message: e.error_description,
@@ -179,8 +184,10 @@ const handleError = (e) => {
179
184
  }
180
185
  else if (e instanceof openid_client_1.errors.RPError) {
181
186
  // Internal client error
187
+ logger_1.default.trace(e, `[OpenID] Unknown RP error`);
182
188
  return new exceptions_1.InvalidCredentialsException();
183
189
  }
190
+ logger_1.default.trace(e, `[OpenID] Unknown error`);
184
191
  return e;
185
192
  };
186
193
  function createOpenIDAuthRouter(providerName) {
@@ -212,7 +219,7 @@ function createOpenIDAuthRouter(providerName) {
212
219
  const { verifier, redirect, prompt } = tokenData;
213
220
  const authenticationService = new services_1.AuthenticationService({
214
221
  accountability: {
215
- ip: req.ip,
222
+ ip: (0, get_ip_from_req_1.getIPFromReq)(req),
216
223
  userAgent: req.get('user-agent'),
217
224
  role: null,
218
225
  },
@@ -247,8 +254,12 @@ function createOpenIDAuthRouter(providerName) {
247
254
  else if (error instanceof exceptions_1.InvalidTokenException) {
248
255
  reason = 'INVALID_TOKEN';
249
256
  }
257
+ else {
258
+ logger_1.default.warn(error, `[OpenID] Unexpected error during OpenID login`);
259
+ }
250
260
  return res.redirect(`${redirect.split('?')[0]}?reason=${reason}`);
251
261
  }
262
+ logger_1.default.warn(error, `[OpenID] Unexpected error during OpenID login`);
252
263
  throw error;
253
264
  }
254
265
  const { accessToken, refreshToken, expires } = authResponse;
package/dist/cache.js CHANGED
@@ -49,9 +49,7 @@ function getConfig(store = 'memory', ttl, namespaceSuffix = '') {
49
49
  };
50
50
  if (store === 'redis') {
51
51
  const KeyvRedis = require('@keyv/redis');
52
- config.store = new KeyvRedis(env_1.default.CACHE_REDIS || (0, get_config_from_env_1.getConfigFromEnv)('CACHE_REDIS_'), {
53
- commandTimeout: 500,
54
- });
52
+ config.store = new KeyvRedis(env_1.default.CACHE_REDIS || (0, get_config_from_env_1.getConfigFromEnv)('CACHE_REDIS_'));
55
53
  }
56
54
  if (store === 'memcache') {
57
55
  const KeyvMemcache = require('keyv-memcache');
@@ -8,6 +8,9 @@ export declare const databaseQuestions: {
8
8
  pg: (({ client }: {
9
9
  client: string;
10
10
  }) => Record<string, any>)[];
11
+ cockroachdb: (({ client }: {
12
+ client: string;
13
+ }) => Record<string, any>)[];
11
14
  oracledb: (({ client }: {
12
15
  client: string;
13
16
  }) => Record<string, any>)[];
@@ -24,6 +24,7 @@ const port = ({ client }) => ({
24
24
  default() {
25
25
  const ports = {
26
26
  pg: 5432,
27
+ cockroachdb: 26257,
27
28
  mysql: 3306,
28
29
  oracledb: 1521,
29
30
  mssql: 1433,
@@ -64,6 +65,7 @@ exports.databaseQuestions = {
64
65
  sqlite3: [filename],
65
66
  mysql: [host, port, database, user, password],
66
67
  pg: [host, port, database, user, password, ssl],
68
+ cockroachdb: [host, port, database, user, password, ssl],
67
69
  oracledb: [host, port, database, user, password],
68
70
  mssql: [host, port, database, user, password, encrypt],
69
71
  };
package/dist/cli/index.js CHANGED
@@ -22,7 +22,7 @@ const pkg = require('../../package.json');
22
22
  async function createCli() {
23
23
  const program = new commander_1.Command();
24
24
  const extensionManager = (0, extensions_1.getExtensionManager)();
25
- await extensionManager.initialize({ schedule: false });
25
+ await extensionManager.initialize({ schedule: false, watch: false });
26
26
  await emitter_1.default.emitInit('cli.before', { program });
27
27
  program.name('directus').usage('[command] [options]');
28
28
  program.version(pkg.version, '-v, --version');
@@ -9,4 +9,4 @@ export declare type Credentials = {
9
9
  ssl?: boolean;
10
10
  options__encrypt?: boolean;
11
11
  };
12
- export default function createDBConnection(client: 'sqlite3' | 'mysql' | 'pg' | 'oracledb' | 'mssql', credentials: Credentials): Knex<any, unknown[]>;
12
+ export default function createDBConnection(client: 'sqlite3' | 'mysql' | 'pg' | 'oracledb' | 'mssql' | 'cockroachdb', credentials: Credentials): Knex<any, unknown[]>;
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const knex_1 = require("knex");
7
7
  const path_1 = __importDefault(require("path"));
8
+ const util_1 = require("util");
8
9
  function createDBConnection(client, credentials) {
9
10
  let connection = {};
10
11
  if (client === 'sqlite3') {
@@ -22,7 +23,7 @@ function createDBConnection(client, credentials) {
22
23
  user: user,
23
24
  password: password,
24
25
  };
25
- if (client === 'pg') {
26
+ if (client === 'pg' || client === 'cockroachdb') {
26
27
  const { ssl } = credentials;
27
28
  connection['ssl'] = ssl;
28
29
  }
@@ -40,10 +41,19 @@ function createDBConnection(client, credentials) {
40
41
  extension: 'js',
41
42
  directory: path_1.default.resolve(__dirname, '../../database/seeds/'),
42
43
  },
44
+ pool: {},
43
45
  };
44
46
  if (client === 'sqlite3') {
45
47
  knexConfig.useNullAsDefault = true;
46
48
  }
49
+ if (client === 'cockroachdb') {
50
+ knexConfig.pool.afterCreate = async (conn, callback) => {
51
+ const run = (0, util_1.promisify)(conn.query.bind(conn));
52
+ await run('SET serial_normalization = "sql_sequence"');
53
+ await run('SET default_int_size = 4');
54
+ callback(null, conn);
55
+ };
56
+ }
47
57
  const db = (0, knex_1.knex)(knexConfig);
48
58
  return db;
49
59
  }
@@ -1,5 +1,6 @@
1
1
  export declare const drivers: {
2
2
  pg: string;
3
+ cockroachdb: string;
3
4
  mysql: string;
4
5
  sqlite3: string;
5
6
  mssql: string;
@@ -3,10 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getDriverForClient = exports.drivers = void 0;
4
4
  exports.drivers = {
5
5
  pg: 'PostgreSQL / Redshift',
6
+ cockroachdb: 'CockroachDB (Beta)',
6
7
  mysql: 'MySQL / MariaDB / Aurora',
7
8
  sqlite3: 'SQLite',
8
9
  mssql: 'Microsoft SQL Server',
9
- oracledb: 'Oracle Database (Alpha)',
10
+ oracledb: 'Oracle Database',
10
11
  };
11
12
  function getDriverForClient(client) {
12
13
  for (const [key, value] of Object.entries(exports.drivers)) {
@@ -12,6 +12,7 @@ const validate_batch_1 = require("../middleware/validate-batch");
12
12
  const services_1 = require("../services");
13
13
  const types_1 = require("../types");
14
14
  const async_handler_1 = __importDefault(require("../utils/async-handler"));
15
+ const get_ip_from_req_1 = require("../utils/get-ip-from-req");
15
16
  const router = express_1.default.Router();
16
17
  router.use((0, use_collection_1.default)('directus_activity'));
17
18
  const readHandler = (0, async_handler_1.default)(async (req, res, next) => {
@@ -72,7 +73,7 @@ router.post('/comment', (0, async_handler_1.default)(async (req, res, next) => {
72
73
  ...req.body,
73
74
  action: types_1.Action.COMMENT,
74
75
  user: (_a = req.accountability) === null || _a === void 0 ? void 0 : _a.user,
75
- ip: req.ip,
76
+ ip: (0, get_ip_from_req_1.getIPFromReq)(req),
76
77
  user_agent: req.get('user-agent'),
77
78
  });
78
79
  try {
@@ -14,6 +14,7 @@ const get_auth_providers_1 = require("../utils/get-auth-providers");
14
14
  const logger_1 = __importDefault(require("../logger"));
15
15
  const drivers_1 = require("../auth/drivers");
16
16
  const constants_1 = require("../constants");
17
+ const get_ip_from_req_1 = require("../utils/get-ip-from-req");
17
18
  const router = (0, express_1.Router)();
18
19
  const authProviders = (0, get_auth_providers_1.getAuthProviders)();
19
20
  for (const authProvider of authProviders) {
@@ -44,7 +45,7 @@ if (!env_1.default.AUTH_DISABLE_DEFAULT) {
44
45
  router.post('/refresh', (0, async_handler_1.default)(async (req, res, next) => {
45
46
  var _a;
46
47
  const accountability = {
47
- ip: req.ip,
48
+ ip: (0, get_ip_from_req_1.getIPFromReq)(req),
48
49
  userAgent: req.get('user-agent'),
49
50
  role: null,
50
51
  };
@@ -79,7 +80,7 @@ router.post('/refresh', (0, async_handler_1.default)(async (req, res, next) => {
79
80
  router.post('/logout', (0, async_handler_1.default)(async (req, res, next) => {
80
81
  var _a;
81
82
  const accountability = {
82
- ip: req.ip,
83
+ ip: (0, get_ip_from_req_1.getIPFromReq)(req),
83
84
  userAgent: req.get('user-agent'),
84
85
  role: null,
85
86
  };
@@ -107,7 +108,7 @@ router.post('/password/request', (0, async_handler_1.default)(async (req, res, n
107
108
  throw new exceptions_1.InvalidPayloadException(`"email" field is required.`);
108
109
  }
109
110
  const accountability = {
110
- ip: req.ip,
111
+ ip: (0, get_ip_from_req_1.getIPFromReq)(req),
111
112
  userAgent: req.get('user-agent'),
112
113
  role: null,
113
114
  };
@@ -134,7 +135,7 @@ router.post('/password/reset', (0, async_handler_1.default)(async (req, res, nex
134
135
  throw new exceptions_1.InvalidPayloadException(`"password" field is required.`);
135
136
  }
136
137
  const accountability = {
137
- ip: req.ip,
138
+ ip: (0, get_ip_from_req_1.getIPFromReq)(req),
138
139
  userAgent: req.get('user-agent'),
139
140
  role: null,
140
141
  };
@@ -16,7 +16,7 @@ router.get('/:type', (0, async_handler_1.default)(async (req, res, next) => {
16
16
  throw new exceptions_1.RouteNotFoundException(req.path);
17
17
  }
18
18
  const extensionManager = (0, extensions_1.getExtensionManager)();
19
- const extensions = extensionManager.listExtensions(type);
19
+ const extensions = extensionManager.getExtensionsList(type);
20
20
  res.locals.payload = {
21
21
  data: extensions,
22
22
  };
@@ -93,6 +93,9 @@ const multipartHandler = (0, async_handler_1.default)(async (req, res, next) =>
93
93
  }
94
94
  });
95
95
  router.post('/', multipartHandler, (0, async_handler_1.default)(async (req, res, next) => {
96
+ if (req.is('multipart/form-data') === false) {
97
+ throw new exceptions_1.UnsupportedMediaTypeException(`Unsupported Content-Type header`);
98
+ }
96
99
  const service = new services_1.FilesService({
97
100
  accountability: req.accountability,
98
101
  schema: req.schema,
@@ -66,6 +66,8 @@ router.post('/revert/:revision', (0, async_handler_1.default)(async (req, res, n
66
66
  next();
67
67
  }), respond_1.respond);
68
68
  router.post('/import/:collection', collection_exists_1.default, (0, async_handler_1.default)(async (req, res, next) => {
69
+ if (req.is('multipart/form-data') === false)
70
+ throw new exceptions_1.UnsupportedMediaTypeException(`Unsupported Content-Type header`);
69
71
  const service = new services_1.ImportService({
70
72
  accountability: req.accountability,
71
73
  schema: req.schema,
@@ -1,5 +1,6 @@
1
1
  export { DateHelperPostgres as postgres } from './dialects/postgres';
2
2
  export { DateHelperPostgres as redshift } from './dialects/postgres';
3
+ export { DateHelperPostgres as cockroachdb } from './dialects/postgres';
3
4
  export { DateHelperOracle as oracle } from './dialects/oracle';
4
5
  export { DateHelperSQLite as sqlite } from './dialects/sqlite';
5
6
  export { DateHelperMySQL as mysql } from './dialects/mysql';
@@ -1,10 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.mssql = exports.mysql = exports.sqlite = exports.oracle = exports.redshift = exports.postgres = void 0;
3
+ exports.mssql = exports.mysql = exports.sqlite = exports.oracle = exports.cockroachdb = exports.redshift = exports.postgres = void 0;
4
4
  var postgres_1 = require("./dialects/postgres");
5
5
  Object.defineProperty(exports, "postgres", { enumerable: true, get: function () { return postgres_1.DateHelperPostgres; } });
6
6
  var postgres_2 = require("./dialects/postgres");
7
7
  Object.defineProperty(exports, "redshift", { enumerable: true, get: function () { return postgres_2.DateHelperPostgres; } });
8
+ var postgres_3 = require("./dialects/postgres");
9
+ Object.defineProperty(exports, "cockroachdb", { enumerable: true, get: function () { return postgres_3.DateHelperPostgres; } });
8
10
  var oracle_1 = require("./dialects/oracle");
9
11
  Object.defineProperty(exports, "oracle", { enumerable: true, get: function () { return oracle_1.DateHelperOracle; } });
10
12
  var sqlite_1 = require("./dialects/sqlite");
@@ -1,4 +1,5 @@
1
1
  export { GeometryHelperPostgres as postgres } from './dialects/postgres';
2
+ export { GeometryHelperPostgres as cockroachdb } from './dialects/postgres';
2
3
  export { GeometryHelperRedshift as redshift } from './dialects/redshift';
3
4
  export { GeometryHelperOracle as oracle } from './dialects/oracle';
4
5
  export { GeometryHelperSQLite as sqlite } from './dialects/sqlite';
@@ -1,8 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.mssql = exports.mysql = exports.sqlite = exports.oracle = exports.redshift = exports.postgres = void 0;
3
+ exports.mssql = exports.mysql = exports.sqlite = exports.oracle = exports.redshift = exports.cockroachdb = exports.postgres = void 0;
4
4
  var postgres_1 = require("./dialects/postgres");
5
5
  Object.defineProperty(exports, "postgres", { enumerable: true, get: function () { return postgres_1.GeometryHelperPostgres; } });
6
+ var postgres_2 = require("./dialects/postgres");
7
+ Object.defineProperty(exports, "cockroachdb", { enumerable: true, get: function () { return postgres_2.GeometryHelperPostgres; } });
6
8
  var redshift_1 = require("./dialects/redshift");
7
9
  Object.defineProperty(exports, "redshift", { enumerable: true, get: function () { return redshift_1.GeometryHelperRedshift; } });
8
10
  var oracle_1 = require("./dialects/oracle");
@@ -1,8 +1,10 @@
1
1
  import { Knex } from 'knex';
2
2
  import * as dateHelpers from './date';
3
3
  import * as geometryHelpers from './geometry';
4
+ import * as schemaHelpers from './schema';
4
5
  export declare function getHelpers(database: Knex): {
5
6
  date: dateHelpers.postgres | dateHelpers.oracle | dateHelpers.sqlite | dateHelpers.mysql | dateHelpers.mssql;
6
7
  st: geometryHelpers.postgres | geometryHelpers.redshift | geometryHelpers.oracle | geometryHelpers.sqlite | geometryHelpers.mysql | geometryHelpers.mssql;
8
+ schema: schemaHelpers.postgres | schemaHelpers.cockroachdb | schemaHelpers.oracle;
7
9
  };
8
10
  export declare type Helpers = ReturnType<typeof getHelpers>;
@@ -23,11 +23,13 @@ exports.getHelpers = void 0;
23
23
  const __1 = require("..");
24
24
  const dateHelpers = __importStar(require("./date"));
25
25
  const geometryHelpers = __importStar(require("./geometry"));
26
+ const schemaHelpers = __importStar(require("./schema"));
26
27
  function getHelpers(database) {
27
28
  const client = (0, __1.getDatabaseClient)(database);
28
29
  return {
29
30
  date: new dateHelpers[client](database),
30
31
  st: new geometryHelpers[client](database),
32
+ schema: new schemaHelpers[client](database),
31
33
  };
32
34
  }
33
35
  exports.getHelpers = getHelpers;
@@ -0,0 +1,16 @@
1
+ import { SchemaHelper } from '../types';
2
+ export declare class SchemaHelperCockroachDb extends SchemaHelper {
3
+ changeToText(table: string, column: string, options?: {
4
+ nullable?: boolean;
5
+ default?: any;
6
+ }): Promise<void>;
7
+ changeToString(table: string, column: string, options?: {
8
+ nullable?: boolean;
9
+ default?: any;
10
+ length?: number;
11
+ }): Promise<void>;
12
+ changeToInteger(table: string, column: string, options?: {
13
+ nullable?: boolean;
14
+ default?: any;
15
+ }): Promise<void>;
16
+ }
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SchemaHelperCockroachDb = void 0;
4
+ const types_1 = require("../types");
5
+ class SchemaHelperCockroachDb extends types_1.SchemaHelper {
6
+ async changeToText(table, column, options = {}) {
7
+ await this.changeToTypeByCopy(table, column, options, (builder, column) => builder.text(column));
8
+ }
9
+ async changeToString(table, column, options = {}) {
10
+ await this.changeToTypeByCopy(table, column, options, (builder, column, options) => builder.string(column, options.length));
11
+ }
12
+ async changeToInteger(table, column, options = {}) {
13
+ await this.changeToTypeByCopy(table, column, options, (builder, column) => builder.integer(column));
14
+ }
15
+ }
16
+ exports.SchemaHelperCockroachDb = SchemaHelperCockroachDb;
@@ -0,0 +1,3 @@
1
+ import { SchemaHelper } from '../types';
2
+ export declare class SchemaHelperDefault extends SchemaHelper {
3
+ }
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SchemaHelperDefault = void 0;
4
+ const types_1 = require("../types");
5
+ class SchemaHelperDefault extends types_1.SchemaHelper {
6
+ }
7
+ exports.SchemaHelperDefault = SchemaHelperDefault;
@@ -0,0 +1,12 @@
1
+ import { SchemaHelper } from '../types';
2
+ export declare class SchemaHelperOracle extends SchemaHelper {
3
+ changeToText(table: string, column: string, options?: {
4
+ nullable?: boolean;
5
+ default?: any;
6
+ }): Promise<void>;
7
+ changeToString(table: string, column: string, options?: {
8
+ nullable?: boolean;
9
+ default?: any;
10
+ length?: number;
11
+ }): Promise<void>;
12
+ }