directus 9.23.3 → 9.24.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.
- package/dist/app.js +15 -15
- package/dist/auth/drivers/ldap.js +22 -22
- package/dist/auth/drivers/local.js +7 -7
- package/dist/auth/drivers/oauth2.js +27 -25
- package/dist/auth/drivers/openid.js +32 -30
- package/dist/auth/drivers/saml.js +10 -10
- package/dist/auth.js +4 -3
- package/dist/cache.js +16 -11
- package/dist/cli/commands/bootstrap/index.js +5 -4
- package/dist/cli/utils/create-db-connection.js +1 -1
- package/dist/cli/utils/create-env/index.js +1 -1
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +6 -5
- package/dist/controllers/activity.js +10 -10
- package/dist/controllers/assets.js +19 -18
- package/dist/controllers/auth.js +16 -16
- package/dist/controllers/collections.js +11 -11
- package/dist/controllers/dashboards.js +11 -11
- package/dist/controllers/extensions.js +3 -3
- package/dist/controllers/fields.js +17 -17
- package/dist/controllers/files.js +18 -17
- package/dist/controllers/flows.js +13 -13
- package/dist/controllers/folders.js +11 -11
- package/dist/controllers/graphql.js +6 -6
- package/dist/controllers/items.js +19 -19
- package/dist/controllers/notifications.js +11 -11
- package/dist/controllers/operations.js +11 -11
- package/dist/controllers/panels.js +11 -11
- package/dist/controllers/permissions.js +11 -11
- package/dist/controllers/presets.js +11 -11
- package/dist/controllers/relations.js +11 -11
- package/dist/controllers/revisions.js +3 -3
- package/dist/controllers/roles.js +11 -11
- package/dist/controllers/schema.js +5 -5
- package/dist/controllers/server.js +7 -7
- package/dist/controllers/settings.js +2 -2
- package/dist/controllers/shares.js +14 -14
- package/dist/controllers/users.js +16 -16
- package/dist/controllers/utils.js +7 -7
- package/dist/controllers/webhooks.js +11 -11
- package/dist/database/helpers/fn/types.d.ts +0 -1
- package/dist/database/helpers/fn/types.js +0 -2
- package/dist/database/helpers/index.d.ts +3 -3
- package/dist/database/index.js +5 -5
- package/dist/database/migrations/20210805B-change-image-metadata-structure.js +15 -15
- package/dist/database/migrations/run.js +1 -1
- package/dist/database/run-ast.js +6 -6
- package/dist/database/system-data/collections/index.js +2 -2
- package/dist/database/system-data/fields/index.js +3 -3
- package/dist/env.js +1 -1
- package/dist/exceptions/database/dialects/mssql.js +2 -2
- package/dist/exceptions/database/dialects/mysql.js +6 -6
- package/dist/exceptions/database/record-not-unique.d.ts +1 -1
- package/dist/extensions.js +10 -10
- package/dist/flows.js +33 -31
- package/dist/logger.d.ts +1 -0
- package/dist/logger.js +32 -32
- package/dist/mailer.js +16 -16
- package/dist/messenger.js +4 -4
- package/dist/middleware/authenticate.d.ts +1 -1
- package/dist/middleware/authenticate.js +2 -2
- package/dist/middleware/cache.js +11 -11
- package/dist/middleware/collection-exists.js +4 -4
- package/dist/middleware/cors.js +8 -8
- package/dist/middleware/error-handler.js +2 -2
- package/dist/middleware/extract-token.js +3 -3
- package/dist/middleware/get-permissions.js +1 -1
- package/dist/middleware/graphql.js +12 -6
- package/dist/middleware/rate-limiter-global.js +5 -5
- package/dist/middleware/rate-limiter-ip.js +2 -2
- package/dist/middleware/respond.js +16 -16
- package/dist/middleware/sanitize-query.js +1 -1
- package/dist/middleware/schema.js +1 -1
- package/dist/middleware/use-collection.js +1 -1
- package/dist/middleware/validate-batch.js +1 -1
- package/dist/operations/exec/index.js +2 -2
- package/dist/rate-limiter.js +1 -1
- package/dist/request/validate-ip.js +2 -2
- package/dist/server.js +4 -4
- package/dist/services/activity.js +14 -14
- package/dist/services/assets.js +6 -6
- package/dist/services/authentication.js +9 -9
- package/dist/services/collections.js +9 -9
- package/dist/services/fields.js +5 -5
- package/dist/services/files.js +18 -18
- package/dist/services/graphql/index.js +170 -116
- package/dist/services/import-export.js +6 -6
- package/dist/services/items.js +6 -6
- package/dist/services/mail/index.js +5 -5
- package/dist/services/meta.js +1 -0
- package/dist/services/notifications.js +4 -4
- package/dist/services/relations.js +4 -4
- package/dist/services/revisions.js +3 -3
- package/dist/services/roles.js +5 -5
- package/dist/services/server.js +27 -27
- package/dist/services/shares.js +9 -9
- package/dist/services/specifications.js +5 -3
- package/dist/services/users.d.ts +1 -5
- package/dist/services/users.js +24 -27
- package/dist/storage/register-locations.js +1 -1
- package/dist/utils/apply-diff.js +12 -12
- package/dist/utils/apply-query.js +3 -2
- package/dist/utils/dynamic-import.js +1 -1
- package/dist/utils/generate-hash.js +1 -1
- package/dist/utils/get-ast-from-query.js +2 -2
- package/dist/utils/get-auth-providers.js +1 -1
- package/dist/utils/get-cache-headers.js +3 -3
- package/dist/utils/get-collection-from-alias.js +1 -0
- package/dist/utils/get-column-path.js +2 -1
- package/dist/utils/get-default-value.js +1 -1
- package/dist/utils/get-ip-from-req.js +2 -2
- package/dist/utils/get-permissions.js +11 -11
- package/dist/utils/get-schema.js +5 -5
- package/dist/utils/get-snapshot-diff.js +1 -1
- package/dist/utils/is-url-allowed.js +5 -2
- package/dist/utils/parse-image-metadata.js +3 -3
- package/dist/utils/reduce-schema.js +5 -5
- package/dist/utils/sanitize-query.js +26 -26
- package/dist/utils/should-skip-cache.js +13 -4
- package/dist/utils/strip-function.js +1 -1
- package/dist/utils/telemetry.d.ts +1 -0
- package/dist/utils/telemetry.js +30 -0
- package/dist/utils/validate-keys.js +1 -1
- package/dist/utils/validate-query.js +1 -1
- package/dist/utils/validate-storage.js +8 -8
- package/dist/webhooks.js +2 -2
- package/package.json +13 -13
- package/dist/utils/redact-header-cookies.d.ts +0 -1
- package/dist/utils/redact-header-cookies.js +0 -11
- package/dist/utils/track.d.ts +0 -1
- package/dist/utils/track.js +0 -81
- /package/dist/{utils/redact-header-cookies.test.d.ts → logger.test.d.ts} +0 -0
|
@@ -30,15 +30,15 @@ router.post('/auth', (0, async_handler_1.default)(async (req, res, next) => {
|
|
|
30
30
|
throw new exceptions_1.InvalidPayloadException(error.message);
|
|
31
31
|
}
|
|
32
32
|
const { accessToken, refreshToken, expires } = await service.login(req.body);
|
|
33
|
-
res.cookie(env_1.default
|
|
34
|
-
res.locals
|
|
33
|
+
res.cookie(env_1.default['REFRESH_TOKEN_COOKIE_NAME'], refreshToken, constants_1.COOKIE_OPTIONS);
|
|
34
|
+
res.locals['payload'] = { data: { access_token: accessToken, expires } };
|
|
35
35
|
return next();
|
|
36
36
|
}), respond_1.respond);
|
|
37
37
|
const sharedInviteSchema = joi_1.default.object({
|
|
38
38
|
share: joi_1.default.string().required(),
|
|
39
39
|
emails: joi_1.default.array().items(joi_1.default.string()),
|
|
40
40
|
}).unknown();
|
|
41
|
-
router.post('/invite', (0, async_handler_1.default)(async (req,
|
|
41
|
+
router.post('/invite', (0, async_handler_1.default)(async (req, _res, next) => {
|
|
42
42
|
const service = new services_1.SharesService({
|
|
43
43
|
schema: req.schema,
|
|
44
44
|
accountability: req.accountability,
|
|
@@ -67,11 +67,11 @@ router.post('/', (0, async_handler_1.default)(async (req, res, next) => {
|
|
|
67
67
|
try {
|
|
68
68
|
if (Array.isArray(req.body)) {
|
|
69
69
|
const items = await service.readMany(savedKeys, req.sanitizedQuery);
|
|
70
|
-
res.locals
|
|
70
|
+
res.locals['payload'] = { data: items };
|
|
71
71
|
}
|
|
72
72
|
else {
|
|
73
73
|
const item = await service.readOne(savedKeys[0], req.sanitizedQuery);
|
|
74
|
-
res.locals
|
|
74
|
+
res.locals['payload'] = { data: item };
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
catch (error) {
|
|
@@ -88,7 +88,7 @@ const readHandler = (0, async_handler_1.default)(async (req, res, next) => {
|
|
|
88
88
|
schema: req.schema,
|
|
89
89
|
});
|
|
90
90
|
const records = await service.readByQuery(req.sanitizedQuery);
|
|
91
|
-
res.locals
|
|
91
|
+
res.locals['payload'] = { data: records || null };
|
|
92
92
|
return next();
|
|
93
93
|
});
|
|
94
94
|
router.get('/', (0, validate_batch_1.validateBatch)('read'), readHandler, respond_1.respond);
|
|
@@ -97,7 +97,7 @@ router.get(`/info/:pk(${constants_1.UUID_REGEX})`, (0, async_handler_1.default)(
|
|
|
97
97
|
const service = new services_1.SharesService({
|
|
98
98
|
schema: req.schema,
|
|
99
99
|
});
|
|
100
|
-
const record = await service.readOne(req.params
|
|
100
|
+
const record = await service.readOne(req.params['pk'], {
|
|
101
101
|
fields: ['id', 'collection', 'item', 'password', 'max_uses', 'times_used', 'date_start', 'date_end'],
|
|
102
102
|
filter: {
|
|
103
103
|
_and: [
|
|
@@ -132,7 +132,7 @@ router.get(`/info/:pk(${constants_1.UUID_REGEX})`, (0, async_handler_1.default)(
|
|
|
132
132
|
],
|
|
133
133
|
},
|
|
134
134
|
});
|
|
135
|
-
res.locals
|
|
135
|
+
res.locals['payload'] = { data: record || null };
|
|
136
136
|
return next();
|
|
137
137
|
}), respond_1.respond);
|
|
138
138
|
router.get(`/:pk(${constants_1.UUID_REGEX})`, (0, async_handler_1.default)(async (req, res, next) => {
|
|
@@ -140,8 +140,8 @@ router.get(`/:pk(${constants_1.UUID_REGEX})`, (0, async_handler_1.default)(async
|
|
|
140
140
|
accountability: req.accountability,
|
|
141
141
|
schema: req.schema,
|
|
142
142
|
});
|
|
143
|
-
const record = await service.readOne(req.params
|
|
144
|
-
res.locals
|
|
143
|
+
const record = await service.readOne(req.params['pk'], req.sanitizedQuery);
|
|
144
|
+
res.locals['payload'] = { data: record || null };
|
|
145
145
|
return next();
|
|
146
146
|
}), respond_1.respond);
|
|
147
147
|
router.patch('/', (0, validate_batch_1.validateBatch)('update'), (0, async_handler_1.default)(async (req, res, next) => {
|
|
@@ -162,7 +162,7 @@ router.patch('/', (0, validate_batch_1.validateBatch)('update'), (0, async_handl
|
|
|
162
162
|
}
|
|
163
163
|
try {
|
|
164
164
|
const result = await service.readMany(keys, req.sanitizedQuery);
|
|
165
|
-
res.locals
|
|
165
|
+
res.locals['payload'] = { data: result };
|
|
166
166
|
}
|
|
167
167
|
catch (error) {
|
|
168
168
|
if (error instanceof exceptions_1.ForbiddenException) {
|
|
@@ -177,10 +177,10 @@ router.patch(`/:pk(${constants_1.UUID_REGEX})`, (0, async_handler_1.default)(asy
|
|
|
177
177
|
accountability: req.accountability,
|
|
178
178
|
schema: req.schema,
|
|
179
179
|
});
|
|
180
|
-
const primaryKey = await service.updateOne(req.params
|
|
180
|
+
const primaryKey = await service.updateOne(req.params['pk'], req.body);
|
|
181
181
|
try {
|
|
182
182
|
const item = await service.readOne(primaryKey, req.sanitizedQuery);
|
|
183
|
-
res.locals
|
|
183
|
+
res.locals['payload'] = { data: item || null };
|
|
184
184
|
}
|
|
185
185
|
catch (error) {
|
|
186
186
|
if (error instanceof exceptions_1.ForbiddenException) {
|
|
@@ -212,7 +212,7 @@ router.delete(`/:pk(${constants_1.UUID_REGEX})`, (0, async_handler_1.default)(as
|
|
|
212
212
|
accountability: req.accountability,
|
|
213
213
|
schema: req.schema,
|
|
214
214
|
});
|
|
215
|
-
await service.deleteOne(req.params
|
|
215
|
+
await service.deleteOne(req.params['pk']);
|
|
216
216
|
return next();
|
|
217
217
|
}), respond_1.respond);
|
|
218
218
|
exports.default = router;
|
|
@@ -31,11 +31,11 @@ router.post('/', (0, async_handler_1.default)(async (req, res, next) => {
|
|
|
31
31
|
try {
|
|
32
32
|
if (Array.isArray(req.body)) {
|
|
33
33
|
const items = await service.readMany(savedKeys, req.sanitizedQuery);
|
|
34
|
-
res.locals
|
|
34
|
+
res.locals['payload'] = { data: items };
|
|
35
35
|
}
|
|
36
36
|
else {
|
|
37
37
|
const item = await service.readOne(savedKeys[0], req.sanitizedQuery);
|
|
38
|
-
res.locals
|
|
38
|
+
res.locals['payload'] = { data: item };
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
catch (error) {
|
|
@@ -57,7 +57,7 @@ const readHandler = (0, async_handler_1.default)(async (req, res, next) => {
|
|
|
57
57
|
});
|
|
58
58
|
const item = await service.readByQuery(req.sanitizedQuery);
|
|
59
59
|
const meta = await metaService.getMetaForQuery('directus_users', req.sanitizedQuery);
|
|
60
|
-
res.locals
|
|
60
|
+
res.locals['payload'] = { data: item || null, meta };
|
|
61
61
|
return next();
|
|
62
62
|
});
|
|
63
63
|
router.get('/', (0, validate_batch_1.validateBatch)('read'), readHandler, respond_1.respond);
|
|
@@ -72,7 +72,7 @@ router.get('/me', (0, async_handler_1.default)(async (req, res, next) => {
|
|
|
72
72
|
app_access: false,
|
|
73
73
|
},
|
|
74
74
|
};
|
|
75
|
-
res.locals
|
|
75
|
+
res.locals['payload'] = { data: user };
|
|
76
76
|
return next();
|
|
77
77
|
}
|
|
78
78
|
if (!req.accountability?.user) {
|
|
@@ -84,11 +84,11 @@ router.get('/me', (0, async_handler_1.default)(async (req, res, next) => {
|
|
|
84
84
|
});
|
|
85
85
|
try {
|
|
86
86
|
const item = await service.readOne(req.accountability.user, req.sanitizedQuery);
|
|
87
|
-
res.locals
|
|
87
|
+
res.locals['payload'] = { data: item || null };
|
|
88
88
|
}
|
|
89
89
|
catch (error) {
|
|
90
90
|
if (error instanceof exceptions_1.ForbiddenException) {
|
|
91
|
-
res.locals
|
|
91
|
+
res.locals['payload'] = { data: { id: req.accountability.user } };
|
|
92
92
|
return next();
|
|
93
93
|
}
|
|
94
94
|
throw error;
|
|
@@ -102,8 +102,8 @@ router.get('/:pk', (0, async_handler_1.default)(async (req, res, next) => {
|
|
|
102
102
|
accountability: req.accountability,
|
|
103
103
|
schema: req.schema,
|
|
104
104
|
});
|
|
105
|
-
const items = await service.readOne(req.params
|
|
106
|
-
res.locals
|
|
105
|
+
const items = await service.readOne(req.params['pk'], req.sanitizedQuery);
|
|
106
|
+
res.locals['payload'] = { data: items || null };
|
|
107
107
|
return next();
|
|
108
108
|
}), respond_1.respond);
|
|
109
109
|
router.patch('/me', (0, async_handler_1.default)(async (req, res, next) => {
|
|
@@ -116,7 +116,7 @@ router.patch('/me', (0, async_handler_1.default)(async (req, res, next) => {
|
|
|
116
116
|
});
|
|
117
117
|
const primaryKey = await service.updateOne(req.accountability.user, req.body);
|
|
118
118
|
const item = await service.readOne(primaryKey, req.sanitizedQuery);
|
|
119
|
-
res.locals
|
|
119
|
+
res.locals['payload'] = { data: item || null };
|
|
120
120
|
return next();
|
|
121
121
|
}), respond_1.respond);
|
|
122
122
|
router.patch('/me/track/page', (0, async_handler_1.default)(async (req, _res, next) => {
|
|
@@ -148,7 +148,7 @@ router.patch('/', (0, validate_batch_1.validateBatch)('update'), (0, async_handl
|
|
|
148
148
|
}
|
|
149
149
|
try {
|
|
150
150
|
const result = await service.readMany(keys, req.sanitizedQuery);
|
|
151
|
-
res.locals
|
|
151
|
+
res.locals['payload'] = { data: result };
|
|
152
152
|
}
|
|
153
153
|
catch (error) {
|
|
154
154
|
if (error instanceof exceptions_1.ForbiddenException) {
|
|
@@ -163,10 +163,10 @@ router.patch('/:pk', (0, async_handler_1.default)(async (req, res, next) => {
|
|
|
163
163
|
accountability: req.accountability,
|
|
164
164
|
schema: req.schema,
|
|
165
165
|
});
|
|
166
|
-
const primaryKey = await service.updateOne(req.params
|
|
166
|
+
const primaryKey = await service.updateOne(req.params['pk'], req.body);
|
|
167
167
|
try {
|
|
168
168
|
const item = await service.readOne(primaryKey, req.sanitizedQuery);
|
|
169
|
-
res.locals
|
|
169
|
+
res.locals['payload'] = { data: item || null };
|
|
170
170
|
}
|
|
171
171
|
catch (error) {
|
|
172
172
|
if (error instanceof exceptions_1.ForbiddenException) {
|
|
@@ -198,7 +198,7 @@ router.delete('/:pk', (0, async_handler_1.default)(async (req, _res, next) => {
|
|
|
198
198
|
accountability: req.accountability,
|
|
199
199
|
schema: req.schema,
|
|
200
200
|
});
|
|
201
|
-
await service.deleteOne(req.params
|
|
201
|
+
await service.deleteOne(req.params['pk']);
|
|
202
202
|
return next();
|
|
203
203
|
}), respond_1.respond);
|
|
204
204
|
const inviteSchema = joi_1.default.object({
|
|
@@ -249,7 +249,7 @@ router.post('/me/tfa/generate/', (0, async_handler_1.default)(async (req, res, n
|
|
|
249
249
|
});
|
|
250
250
|
await authService.verifyPassword(req.accountability.user, req.body.password);
|
|
251
251
|
const { url, secret } = await service.generateTFA(req.accountability.user);
|
|
252
|
-
res.locals
|
|
252
|
+
res.locals['payload'] = { data: { secret, otpauth_url: url } };
|
|
253
253
|
return next();
|
|
254
254
|
}), respond_1.respond);
|
|
255
255
|
router.post('/me/tfa/enable/', (0, async_handler_1.default)(async (req, _res, next) => {
|
|
@@ -345,14 +345,14 @@ router.post('/:pk/tfa/disable', (0, async_handler_1.default)(async (req, _res, n
|
|
|
345
345
|
if (!req.accountability?.user) {
|
|
346
346
|
throw new exceptions_1.InvalidCredentialsException();
|
|
347
347
|
}
|
|
348
|
-
if (!req.accountability.admin || !req.params
|
|
348
|
+
if (!req.accountability.admin || !req.params['pk']) {
|
|
349
349
|
throw new exceptions_1.ForbiddenException();
|
|
350
350
|
}
|
|
351
351
|
const service = new services_1.TFAService({
|
|
352
352
|
accountability: req.accountability,
|
|
353
353
|
schema: req.schema,
|
|
354
354
|
});
|
|
355
|
-
await service.disableTFA(req.params
|
|
355
|
+
await service.disableTFA(req.params['pk']);
|
|
356
356
|
return next();
|
|
357
357
|
}), respond_1.respond);
|
|
358
358
|
exports.default = router;
|
|
@@ -18,9 +18,9 @@ const sanitize_query_1 = require("../utils/sanitize-query");
|
|
|
18
18
|
const router = (0, express_1.Router)();
|
|
19
19
|
router.get('/random/string', (0, async_handler_1.default)(async (req, res) => {
|
|
20
20
|
const { nanoid } = await import('nanoid');
|
|
21
|
-
if (req.query && req.query
|
|
21
|
+
if (req.query && req.query['length'] && Number(req.query['length']) > 500)
|
|
22
22
|
throw new exceptions_1.InvalidQueryException(`"length" can't be more than 500 characters`);
|
|
23
|
-
const string = nanoid(req.query?.length ? Number(req.query
|
|
23
|
+
const string = nanoid(req.query?.['length'] ? Number(req.query['length']) : 32);
|
|
24
24
|
return res.json({ data: string });
|
|
25
25
|
}));
|
|
26
26
|
router.post('/hash/generate', (0, async_handler_1.default)(async (req, res) => {
|
|
@@ -55,12 +55,12 @@ router.post('/sort/:collection', collection_exists_1.default, (0, async_handler_
|
|
|
55
55
|
await service.sort(req.collection, req.body);
|
|
56
56
|
return res.status(200).end();
|
|
57
57
|
}));
|
|
58
|
-
router.post('/revert/:revision', (0, async_handler_1.default)(async (req,
|
|
58
|
+
router.post('/revert/:revision', (0, async_handler_1.default)(async (req, _res, next) => {
|
|
59
59
|
const service = new services_1.RevisionsService({
|
|
60
60
|
accountability: req.accountability,
|
|
61
61
|
schema: req.schema,
|
|
62
62
|
});
|
|
63
|
-
await service.revert(req.params
|
|
63
|
+
await service.revert(req.params['revision']);
|
|
64
64
|
next();
|
|
65
65
|
}), respond_1.respond);
|
|
66
66
|
router.post('/import/:collection', collection_exists_1.default, (0, async_handler_1.default)(async (req, res, next) => {
|
|
@@ -83,7 +83,7 @@ router.post('/import/:collection', collection_exists_1.default, (0, async_handle
|
|
|
83
83
|
const busboy = (0, busboy_1.default)({ headers });
|
|
84
84
|
busboy.on('file', async (_fieldname, fileStream, { mimeType }) => {
|
|
85
85
|
try {
|
|
86
|
-
await service.import(req.params
|
|
86
|
+
await service.import(req.params['collection'], mimeType, fileStream);
|
|
87
87
|
}
|
|
88
88
|
catch (err) {
|
|
89
89
|
return next(err);
|
|
@@ -93,7 +93,7 @@ router.post('/import/:collection', collection_exists_1.default, (0, async_handle
|
|
|
93
93
|
busboy.on('error', (err) => next(err));
|
|
94
94
|
req.pipe(busboy);
|
|
95
95
|
}));
|
|
96
|
-
router.post('/export/:collection', collection_exists_1.default, (0, async_handler_1.default)(async (req,
|
|
96
|
+
router.post('/export/:collection', collection_exists_1.default, (0, async_handler_1.default)(async (req, _res, next) => {
|
|
97
97
|
if (!req.body.query) {
|
|
98
98
|
throw new exceptions_1.InvalidPayloadException(`"query" is required.`);
|
|
99
99
|
}
|
|
@@ -106,7 +106,7 @@ router.post('/export/:collection', collection_exists_1.default, (0, async_handle
|
|
|
106
106
|
});
|
|
107
107
|
const sanitizedQuery = (0, sanitize_query_1.sanitizeQuery)(req.body.query, req.accountability ?? null);
|
|
108
108
|
// We're not awaiting this, as it's supposed to run async in the background
|
|
109
|
-
service.exportToFile(req.params
|
|
109
|
+
service.exportToFile(req.params['collection'], sanitizedQuery, req.body.format, {
|
|
110
110
|
file: req.body.file,
|
|
111
111
|
});
|
|
112
112
|
return next();
|
|
@@ -30,11 +30,11 @@ router.post('/', (0, async_handler_1.default)(async (req, res, next) => {
|
|
|
30
30
|
try {
|
|
31
31
|
if (Array.isArray(req.body)) {
|
|
32
32
|
const items = await service.readMany(savedKeys, req.sanitizedQuery);
|
|
33
|
-
res.locals
|
|
33
|
+
res.locals['payload'] = { data: items };
|
|
34
34
|
}
|
|
35
35
|
else {
|
|
36
36
|
const item = await service.readOne(savedKeys[0], req.sanitizedQuery);
|
|
37
|
-
res.locals
|
|
37
|
+
res.locals['payload'] = { data: item };
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
catch (error) {
|
|
@@ -56,7 +56,7 @@ const readHandler = (0, async_handler_1.default)(async (req, res, next) => {
|
|
|
56
56
|
});
|
|
57
57
|
const records = await service.readByQuery(req.sanitizedQuery);
|
|
58
58
|
const meta = await metaService.getMetaForQuery(req.collection, req.sanitizedQuery);
|
|
59
|
-
res.locals
|
|
59
|
+
res.locals['payload'] = { data: records || null, meta };
|
|
60
60
|
return next();
|
|
61
61
|
});
|
|
62
62
|
router.get('/', (0, validate_batch_1.validateBatch)('read'), readHandler, respond_1.respond);
|
|
@@ -66,8 +66,8 @@ router.get('/:pk', (0, async_handler_1.default)(async (req, res, next) => {
|
|
|
66
66
|
accountability: req.accountability,
|
|
67
67
|
schema: req.schema,
|
|
68
68
|
});
|
|
69
|
-
const record = await service.readOne(req.params
|
|
70
|
-
res.locals
|
|
69
|
+
const record = await service.readOne(req.params['pk'], req.sanitizedQuery);
|
|
70
|
+
res.locals['payload'] = { data: record || null };
|
|
71
71
|
return next();
|
|
72
72
|
}), respond_1.respond);
|
|
73
73
|
router.patch('/', (0, validate_batch_1.validateBatch)('update'), (0, async_handler_1.default)(async (req, res, next) => {
|
|
@@ -85,7 +85,7 @@ router.patch('/', (0, validate_batch_1.validateBatch)('update'), (0, async_handl
|
|
|
85
85
|
}
|
|
86
86
|
try {
|
|
87
87
|
const result = await service.readMany(keys, req.sanitizedQuery);
|
|
88
|
-
res.locals
|
|
88
|
+
res.locals['payload'] = { data: result };
|
|
89
89
|
}
|
|
90
90
|
catch (error) {
|
|
91
91
|
if (error instanceof exceptions_1.ForbiddenException) {
|
|
@@ -100,10 +100,10 @@ router.patch('/:pk', (0, async_handler_1.default)(async (req, res, next) => {
|
|
|
100
100
|
accountability: req.accountability,
|
|
101
101
|
schema: req.schema,
|
|
102
102
|
});
|
|
103
|
-
const primaryKey = await service.updateOne(req.params
|
|
103
|
+
const primaryKey = await service.updateOne(req.params['pk'], req.body);
|
|
104
104
|
try {
|
|
105
105
|
const item = await service.readOne(primaryKey, req.sanitizedQuery);
|
|
106
|
-
res.locals
|
|
106
|
+
res.locals['payload'] = { data: item || null };
|
|
107
107
|
}
|
|
108
108
|
catch (error) {
|
|
109
109
|
if (error instanceof exceptions_1.ForbiddenException) {
|
|
@@ -113,7 +113,7 @@ router.patch('/:pk', (0, async_handler_1.default)(async (req, res, next) => {
|
|
|
113
113
|
}
|
|
114
114
|
return next();
|
|
115
115
|
}), respond_1.respond);
|
|
116
|
-
router.delete('/', (0, async_handler_1.default)(async (req,
|
|
116
|
+
router.delete('/', (0, async_handler_1.default)(async (req, _res, next) => {
|
|
117
117
|
const service = new services_1.WebhooksService({
|
|
118
118
|
accountability: req.accountability,
|
|
119
119
|
schema: req.schema,
|
|
@@ -130,12 +130,12 @@ router.delete('/', (0, async_handler_1.default)(async (req, res, next) => {
|
|
|
130
130
|
}
|
|
131
131
|
return next();
|
|
132
132
|
}), respond_1.respond);
|
|
133
|
-
router.delete('/:pk', (0, async_handler_1.default)(async (req,
|
|
133
|
+
router.delete('/:pk', (0, async_handler_1.default)(async (req, _res, next) => {
|
|
134
134
|
const service = new services_1.WebhooksService({
|
|
135
135
|
accountability: req.accountability,
|
|
136
136
|
schema: req.schema,
|
|
137
137
|
});
|
|
138
|
-
await service.deleteOne(req.params
|
|
138
|
+
await service.deleteOne(req.params['pk']);
|
|
139
139
|
return next();
|
|
140
140
|
}), respond_1.respond);
|
|
141
141
|
exports.default = router;
|
|
@@ -7,7 +7,6 @@ export type FnHelperOptions = {
|
|
|
7
7
|
originalCollectionName: string | undefined;
|
|
8
8
|
};
|
|
9
9
|
export declare abstract class FnHelper extends DatabaseHelper {
|
|
10
|
-
protected knex: Knex;
|
|
11
10
|
protected schema: SchemaOverview;
|
|
12
11
|
constructor(knex: Knex, schema: SchemaOverview);
|
|
13
12
|
abstract year(table: string, column: string, options?: FnHelperOptions): Knex.Raw;
|
|
@@ -4,11 +4,9 @@ exports.FnHelper = void 0;
|
|
|
4
4
|
const apply_query_1 = require("../../../utils/apply-query");
|
|
5
5
|
const types_1 = require("../types");
|
|
6
6
|
class FnHelper extends types_1.DatabaseHelper {
|
|
7
|
-
knex;
|
|
8
7
|
schema;
|
|
9
8
|
constructor(knex, schema) {
|
|
10
9
|
super(knex);
|
|
11
|
-
this.knex = knex;
|
|
12
10
|
this.schema = schema;
|
|
13
11
|
this.schema = schema;
|
|
14
12
|
}
|
|
@@ -6,8 +6,8 @@ import * as geometryHelpers from './geometry';
|
|
|
6
6
|
import * as schemaHelpers from './schema';
|
|
7
7
|
export declare function getHelpers(database: Knex): {
|
|
8
8
|
date: dateHelpers.postgres | dateHelpers.oracle | dateHelpers.mysql | dateHelpers.mssql | dateHelpers.sqlite;
|
|
9
|
-
st: geometryHelpers.
|
|
10
|
-
schema: schemaHelpers.
|
|
9
|
+
st: geometryHelpers.mysql | geometryHelpers.postgres | geometryHelpers.mssql | geometryHelpers.sqlite | geometryHelpers.oracle | geometryHelpers.redshift;
|
|
10
|
+
schema: schemaHelpers.mysql | schemaHelpers.cockroachdb | schemaHelpers.mssql | schemaHelpers.postgres | schemaHelpers.sqlite | schemaHelpers.oracle;
|
|
11
11
|
};
|
|
12
|
-
export declare function getFunctions(database: Knex, schema: SchemaOverview): fnHelpers.
|
|
12
|
+
export declare function getFunctions(database: Knex, schema: SchemaOverview): fnHelpers.mysql | fnHelpers.postgres | fnHelpers.mssql | fnHelpers.sqlite | fnHelpers.oracle;
|
|
13
13
|
export type Helpers = ReturnType<typeof getHelpers>;
|
package/dist/database/index.js
CHANGED
|
@@ -30,7 +30,7 @@ function getDatabase() {
|
|
|
30
30
|
requiredEnvVars.push('DB_FILENAME');
|
|
31
31
|
break;
|
|
32
32
|
case 'oracledb':
|
|
33
|
-
if (!env_1.default
|
|
33
|
+
if (!env_1.default['DB_CONNECT_STRING']) {
|
|
34
34
|
requiredEnvVars.push('DB_HOST', 'DB_PORT', 'DB_DATABASE', 'DB_USER', 'DB_PASSWORD');
|
|
35
35
|
}
|
|
36
36
|
else {
|
|
@@ -47,7 +47,7 @@ function getDatabase() {
|
|
|
47
47
|
}
|
|
48
48
|
break;
|
|
49
49
|
case 'mssql':
|
|
50
|
-
if (!env_1.default
|
|
50
|
+
if (!env_1.default['DB_TYPE'] || env_1.default['DB_TYPE'] === 'default') {
|
|
51
51
|
requiredEnvVars.push('DB_HOST', 'DB_PORT', 'DB_DATABASE', 'DB_USER', 'DB_PASSWORD');
|
|
52
52
|
}
|
|
53
53
|
break;
|
|
@@ -211,7 +211,7 @@ async function validateMigrations() {
|
|
|
211
211
|
const database = getDatabase();
|
|
212
212
|
try {
|
|
213
213
|
let migrationFiles = await fs_extra_1.default.readdir(path_1.default.join(__dirname, 'migrations'));
|
|
214
|
-
const customMigrationsPath = path_1.default.resolve(env_1.default
|
|
214
|
+
const customMigrationsPath = path_1.default.resolve(env_1.default['EXTENSIONS_PATH'], 'migrations');
|
|
215
215
|
let customMigrationFiles = ((await fs_extra_1.default.pathExists(customMigrationsPath)) && (await fs_extra_1.default.readdir(customMigrationsPath))) || [];
|
|
216
216
|
migrationFiles = migrationFiles.filter((file) => file.startsWith('run') === false && file.endsWith('.d.ts') === false);
|
|
217
217
|
customMigrationFiles = customMigrationFiles.filter((file) => file.endsWith('.js'));
|
|
@@ -255,10 +255,10 @@ async function validateDatabaseCharset(database) {
|
|
|
255
255
|
const { collation } = await database.select(database.raw(`@@collation_database as collation`)).first();
|
|
256
256
|
const tables = await database('information_schema.tables')
|
|
257
257
|
.select({ name: 'TABLE_NAME', collation: 'TABLE_COLLATION' })
|
|
258
|
-
.where({ TABLE_SCHEMA: env_1.default
|
|
258
|
+
.where({ TABLE_SCHEMA: env_1.default['DB_DATABASE'] });
|
|
259
259
|
const columns = await database('information_schema.columns')
|
|
260
260
|
.select({ table_name: 'TABLE_NAME', name: 'COLUMN_NAME', collation: 'COLLATION_NAME' })
|
|
261
|
-
.where({ TABLE_SCHEMA: env_1.default
|
|
261
|
+
.where({ TABLE_SCHEMA: env_1.default['DB_DATABASE'] })
|
|
262
262
|
.whereNot({ COLLATION_NAME: collation });
|
|
263
263
|
let inconsistencies = '';
|
|
264
264
|
for (const table of tables) {
|
|
@@ -60,25 +60,25 @@ async function down(knex) {
|
|
|
60
60
|
if (Object.keys(prevMetadata).filter((key) => key !== 'icc' && key !== 'iptc').length > 0) {
|
|
61
61
|
// Put all data under 'exif' and rename/move keys afterwards
|
|
62
62
|
const newMetadata = { exif: prevMetadata };
|
|
63
|
-
if (newMetadata.exif
|
|
64
|
-
newMetadata.exif
|
|
65
|
-
delete newMetadata.exif
|
|
63
|
+
if (newMetadata.exif['ifd0']) {
|
|
64
|
+
newMetadata.exif['image'] = newMetadata.exif['ifd0'];
|
|
65
|
+
delete newMetadata.exif['ifd0'];
|
|
66
66
|
}
|
|
67
|
-
if (newMetadata.exif
|
|
68
|
-
newMetadata.exif
|
|
69
|
-
delete newMetadata.exif
|
|
67
|
+
if (newMetadata.exif['ifd1']) {
|
|
68
|
+
newMetadata.exif['thumbnail'] = newMetadata.exif['ifd1'];
|
|
69
|
+
delete newMetadata.exif['ifd1'];
|
|
70
70
|
}
|
|
71
|
-
if (newMetadata.exif
|
|
72
|
-
newMetadata.exif
|
|
73
|
-
delete newMetadata.exif
|
|
71
|
+
if (newMetadata.exif['interop']) {
|
|
72
|
+
newMetadata.exif['interoperability'] = newMetadata.exif['interop'];
|
|
73
|
+
delete newMetadata.exif['interop'];
|
|
74
74
|
}
|
|
75
|
-
if (newMetadata.exif
|
|
76
|
-
newMetadata.icc = newMetadata.exif
|
|
77
|
-
delete newMetadata.exif
|
|
75
|
+
if (newMetadata.exif['icc']) {
|
|
76
|
+
newMetadata.icc = newMetadata.exif['icc'];
|
|
77
|
+
delete newMetadata.exif['icc'];
|
|
78
78
|
}
|
|
79
|
-
if (newMetadata.exif
|
|
80
|
-
newMetadata.iptc = newMetadata.exif
|
|
81
|
-
delete newMetadata.exif
|
|
79
|
+
if (newMetadata.exif['iptc']) {
|
|
80
|
+
newMetadata.iptc = newMetadata.exif['iptc'];
|
|
81
|
+
delete newMetadata.exif['iptc'];
|
|
82
82
|
}
|
|
83
83
|
await knex('directus_files')
|
|
84
84
|
.update({ metadata: JSON.stringify(newMetadata) })
|
|
@@ -13,7 +13,7 @@ const dynamic_import_1 = require("../../utils/dynamic-import");
|
|
|
13
13
|
const format_title_1 = __importDefault(require("@directus/format-title"));
|
|
14
14
|
async function run(database, direction, log = true) {
|
|
15
15
|
let migrationFiles = await fs_extra_1.default.readdir(__dirname);
|
|
16
|
-
const customMigrationsPath = path_1.default.resolve(env_1.default
|
|
16
|
+
const customMigrationsPath = path_1.default.resolve(env_1.default['EXTENSIONS_PATH'], 'migrations');
|
|
17
17
|
let customMigrationFiles = ((await fs_extra_1.default.pathExists(customMigrationsPath)) && (await fs_extra_1.default.readdir(customMigrationsPath))) || [];
|
|
18
18
|
migrationFiles = migrationFiles.filter((file) => /^[0-9]+[A-Z]-[^.]+\.(?:js|ts)$/.test(file));
|
|
19
19
|
customMigrationFiles = customMigrationFiles.filter((file) => file.endsWith('.js'));
|
package/dist/database/run-ast.js
CHANGED
|
@@ -64,7 +64,7 @@ async function runAST(originalAST, schema, options) {
|
|
|
64
64
|
// Run the items through the special transforms
|
|
65
65
|
const payloadService = new payload_1.PayloadService(collection, { knex, schema });
|
|
66
66
|
let items = await payloadService.processValues('read', rawItems);
|
|
67
|
-
if (!items || items.length === 0)
|
|
67
|
+
if (!items || (Array.isArray(items) && items.length === 0))
|
|
68
68
|
return items;
|
|
69
69
|
// Apply the `_in` filters to the nested collection batches
|
|
70
70
|
const nestedNodes = applyParentFilters(schema, nestedCollectionNodes, items);
|
|
@@ -76,8 +76,8 @@ async function runAST(originalAST, schema, options) {
|
|
|
76
76
|
while (hasMore) {
|
|
77
77
|
const node = (0, lodash_1.merge)({}, nestedNode, {
|
|
78
78
|
query: {
|
|
79
|
-
limit: env_1.default
|
|
80
|
-
offset: batchCount * env_1.default
|
|
79
|
+
limit: env_1.default['RELATIONAL_BATCH_SIZE'],
|
|
80
|
+
offset: batchCount * env_1.default['RELATIONAL_BATCH_SIZE'],
|
|
81
81
|
page: null,
|
|
82
82
|
},
|
|
83
83
|
});
|
|
@@ -85,7 +85,7 @@ async function runAST(originalAST, schema, options) {
|
|
|
85
85
|
if (nestedItems) {
|
|
86
86
|
items = mergeWithParentItems(schema, nestedItems, items, nestedNode);
|
|
87
87
|
}
|
|
88
|
-
if (!nestedItems || nestedItems.length < env_1.default
|
|
88
|
+
if (!nestedItems || nestedItems.length < env_1.default['RELATIONAL_BATCH_SIZE']) {
|
|
89
89
|
hasMore = false;
|
|
90
90
|
}
|
|
91
91
|
batchCount++;
|
|
@@ -280,7 +280,7 @@ function applyParentFilters(schema, nestedCollectionNodes, parentItem) {
|
|
|
280
280
|
continue;
|
|
281
281
|
if (nestedNode.type === 'm2o') {
|
|
282
282
|
const foreignField = schema.collections[nestedNode.relation.related_collection].primary;
|
|
283
|
-
const foreignIds = (0, lodash_1.uniq)(parentItems.map((res) => res[nestedNode.relation.field])).filter((id) => id);
|
|
283
|
+
const foreignIds = (0, lodash_1.uniq)(parentItems.map((res) => res[nestedNode.relation.field])).filter((id) => !(0, lodash_1.isNil)(id));
|
|
284
284
|
(0, lodash_1.merge)(nestedNode, { query: { filter: { [foreignField]: { _in: foreignIds } } } });
|
|
285
285
|
}
|
|
286
286
|
else if (nestedNode.type === 'o2m') {
|
|
@@ -302,7 +302,7 @@ function applyParentFilters(schema, nestedCollectionNodes, parentItem) {
|
|
|
302
302
|
});
|
|
303
303
|
}
|
|
304
304
|
const foreignField = nestedNode.relation.field;
|
|
305
|
-
const foreignIds = (0, lodash_1.uniq)(parentItems.map((res) => res[nestedNode.parentKey])).filter((id) => id);
|
|
305
|
+
const foreignIds = (0, lodash_1.uniq)(parentItems.map((res) => res[nestedNode.parentKey])).filter((id) => !(0, lodash_1.isNil)(id));
|
|
306
306
|
(0, lodash_1.merge)(nestedNode, { query: { filter: { [foreignField]: { _in: foreignIds } } } });
|
|
307
307
|
}
|
|
308
308
|
else if (nestedNode.type === 'a2o') {
|
|
@@ -4,6 +4,6 @@ exports.systemCollectionRows = void 0;
|
|
|
4
4
|
const lodash_1 = require("lodash");
|
|
5
5
|
const require_yaml_1 = require("../../../utils/require-yaml");
|
|
6
6
|
const systemData = (0, require_yaml_1.requireYAML)(require.resolve('./collections.yaml'));
|
|
7
|
-
exports.systemCollectionRows = systemData
|
|
8
|
-
return (0, lodash_1.merge)({ system: true }, systemData
|
|
7
|
+
exports.systemCollectionRows = systemData['data'].map((row) => {
|
|
8
|
+
return (0, lodash_1.merge)({ system: true }, systemData['defaults'], row);
|
|
9
9
|
});
|
|
@@ -18,15 +18,15 @@ for (const filepath of fieldData) {
|
|
|
18
18
|
if (filepath.includes('_defaults') || filepath.includes('index'))
|
|
19
19
|
continue;
|
|
20
20
|
const systemFields = (0, require_yaml_1.requireYAML)(path_1.default.resolve(__dirname, filepath));
|
|
21
|
-
systemFields
|
|
21
|
+
systemFields['fields'].forEach((field, index) => {
|
|
22
22
|
const systemField = (0, lodash_1.merge)({ system: true }, defaults, field, {
|
|
23
|
-
collection: systemFields
|
|
23
|
+
collection: systemFields['table'],
|
|
24
24
|
sort: index + 1,
|
|
25
25
|
});
|
|
26
26
|
// Dynamically populate auth providers field
|
|
27
27
|
if (systemField.collection === 'directus_users' && systemField.field === 'provider') {
|
|
28
28
|
(0, get_auth_providers_1.getAuthProviders)().forEach(({ name }) => {
|
|
29
|
-
systemField.options?.choices?.push({
|
|
29
|
+
systemField.options?.['choices']?.push({
|
|
30
30
|
text: (0, format_title_1.default)(name),
|
|
31
31
|
value: name,
|
|
32
32
|
});
|
package/dist/env.js
CHANGED
|
@@ -308,7 +308,7 @@ function refreshEnv() {
|
|
|
308
308
|
}
|
|
309
309
|
exports.refreshEnv = refreshEnv;
|
|
310
310
|
function processConfiguration() {
|
|
311
|
-
const configPath = path_1.default.resolve(process.env
|
|
311
|
+
const configPath = path_1.default.resolve(process.env['CONFIG_PATH'] || defaults['CONFIG_PATH']);
|
|
312
312
|
if (fs_1.default.existsSync(configPath) === false)
|
|
313
313
|
return {};
|
|
314
314
|
const fileExt = path_1.default.extname(configPath).toLowerCase();
|
|
@@ -54,8 +54,8 @@ async function uniqueViolation(error) {
|
|
|
54
54
|
const parenMatches = error.message.match(betweenParens);
|
|
55
55
|
if (!quoteMatches || !parenMatches)
|
|
56
56
|
return error;
|
|
57
|
-
const keyName = quoteMatches[1]
|
|
58
|
-
let collection = quoteMatches[0]
|
|
57
|
+
const keyName = quoteMatches[1].slice(1, -1);
|
|
58
|
+
let collection = quoteMatches[0].slice(1, -1);
|
|
59
59
|
let field = null;
|
|
60
60
|
if (keyName) {
|
|
61
61
|
const database = (0, database_1.default)();
|
|
@@ -49,7 +49,7 @@ function uniqueViolation(error) {
|
|
|
49
49
|
*/
|
|
50
50
|
/** MySQL 8+ style error message */
|
|
51
51
|
if (matches[1].includes('.')) {
|
|
52
|
-
const collection = matches[1]
|
|
52
|
+
const collection = matches[1].slice(1, -1).split('.')[0];
|
|
53
53
|
let field = null;
|
|
54
54
|
const indexName = matches[1]?.slice(1, -1).split('.')[1];
|
|
55
55
|
if (indexName?.startsWith(`${collection}_`) && indexName.endsWith('_unique')) {
|
|
@@ -64,7 +64,7 @@ function uniqueViolation(error) {
|
|
|
64
64
|
}
|
|
65
65
|
else {
|
|
66
66
|
/** MySQL 5.7 style error message */
|
|
67
|
-
const indexName = matches[1]
|
|
67
|
+
const indexName = matches[1].slice(1, -1);
|
|
68
68
|
const collection = indexName.split('_')[0];
|
|
69
69
|
let field = null;
|
|
70
70
|
if (indexName?.startsWith(`${collection}_`) && indexName.endsWith('_unique')) {
|
|
@@ -127,9 +127,9 @@ function foreignKeyViolation(error) {
|
|
|
127
127
|
const parenMatches = error.sql.match(betweenParens);
|
|
128
128
|
if (!tickMatches || !parenMatches)
|
|
129
129
|
return error;
|
|
130
|
-
const collection = tickMatches[1]
|
|
131
|
-
const field = tickMatches[3]
|
|
132
|
-
const invalid = parenMatches[1]
|
|
130
|
+
const collection = tickMatches[1].slice(1, -1);
|
|
131
|
+
const field = tickMatches[3].slice(1, -1);
|
|
132
|
+
const invalid = parenMatches[1].slice(1, -1);
|
|
133
133
|
return new invalid_foreign_key_1.InvalidForeignKeyException(field, {
|
|
134
134
|
collection,
|
|
135
135
|
field,
|
|
@@ -143,6 +143,6 @@ function containsNullValues(error) {
|
|
|
143
143
|
const tickMatches = error.sql.match(betweenTicks);
|
|
144
144
|
if (!tickMatches)
|
|
145
145
|
return error;
|
|
146
|
-
const field = tickMatches[1]
|
|
146
|
+
const field = tickMatches[1].slice(1, -1);
|
|
147
147
|
return new contains_null_values_1.ContainsNullValuesException(field);
|
|
148
148
|
}
|
|
@@ -2,7 +2,7 @@ import { BaseException } from '@directus/shared/exceptions';
|
|
|
2
2
|
type Extensions = {
|
|
3
3
|
collection: string;
|
|
4
4
|
field: string | null;
|
|
5
|
-
invalid?: string;
|
|
5
|
+
invalid?: string | undefined;
|
|
6
6
|
};
|
|
7
7
|
export declare class RecordNotUniqueException extends BaseException {
|
|
8
8
|
constructor(field: string | null, extensions?: Extensions);
|