directus 9.5.2 → 9.6.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/auth/drivers/ldap.d.ts +0 -1
- package/dist/auth/drivers/ldap.js +54 -60
- package/dist/cli/utils/create-env/env-stub.liquid +1 -0
- package/dist/database/migrations/20220303A-remove-default-project-color.d.ts +3 -0
- package/dist/database/migrations/20220303A-remove-default-project-color.js +22 -0
- package/dist/database/run-ast.d.ts +1 -1
- package/dist/database/run-ast.js +42 -35
- package/dist/database/system-data/fields/settings.yaml +19 -3
- package/dist/env.js +3 -0
- package/dist/extensions.js +0 -2
- package/dist/middleware/authenticate.d.ts +5 -3
- package/dist/middleware/authenticate.js +22 -39
- package/dist/server.js +5 -2
- package/dist/services/authorization.js +2 -1
- package/dist/services/mail/templates/base.liquid +2 -2
- package/dist/services/utils.js +10 -0
- package/dist/utils/apply-query.js +2 -24
- package/dist/utils/is-directus-jwt.js +4 -21
- package/dist/utils/jwt.d.ts +2 -0
- package/dist/utils/jwt.js +49 -0
- package/dist/utils/merge-permissions.js +18 -6
- package/example.env +1 -0
- package/package.json +16 -18
- package/dist/__mocks__/cache.d.ts +0 -6
- package/dist/__mocks__/cache.js +0 -8
|
@@ -9,7 +9,6 @@ export declare class LDAPAuthDriver extends AuthDriver {
|
|
|
9
9
|
config: Record<string, any>;
|
|
10
10
|
constructor(options: AuthDriverOptions, config: Record<string, any>);
|
|
11
11
|
private validateBindClient;
|
|
12
|
-
private fetchUserDn;
|
|
13
12
|
private fetchUserInfo;
|
|
14
13
|
private fetchUserGroups;
|
|
15
14
|
private fetchUserId;
|
|
@@ -44,7 +44,11 @@ class LDAPAuthDriver extends auth_1.AuthDriver {
|
|
|
44
44
|
var _a;
|
|
45
45
|
super(options, config);
|
|
46
46
|
const { bindDn, bindPassword, userDn, provider, clientUrl } = config;
|
|
47
|
-
if (
|
|
47
|
+
if (bindDn === undefined ||
|
|
48
|
+
bindPassword === undefined ||
|
|
49
|
+
!userDn ||
|
|
50
|
+
!provider ||
|
|
51
|
+
(!clientUrl && !((_a = config.client) === null || _a === void 0 ? void 0 : _a.socketPath))) {
|
|
48
52
|
throw new exceptions_1.InvalidConfigException('Invalid provider config', { provider });
|
|
49
53
|
}
|
|
50
54
|
const clientConfig = typeof config.client === 'object' ? config.client : {};
|
|
@@ -90,55 +94,38 @@ class LDAPAuthDriver extends auth_1.AuthDriver {
|
|
|
90
94
|
});
|
|
91
95
|
res.on('end', (result) => {
|
|
92
96
|
if ((result === null || result === void 0 ? void 0 : result.status) === 0) {
|
|
93
|
-
// Handle edge case
|
|
97
|
+
// Handle edge case where authenticated bind user could not fetch their own DN
|
|
94
98
|
reject(new exceptions_1.UnexpectedResponseException('Failed to find bind user record'));
|
|
95
99
|
}
|
|
96
100
|
});
|
|
97
101
|
});
|
|
98
102
|
});
|
|
99
103
|
}
|
|
100
|
-
async
|
|
101
|
-
|
|
104
|
+
async fetchUserInfo(baseDn, filter, scope) {
|
|
105
|
+
let { firstNameAttribute, lastNameAttribute, mailAttribute } = this.config;
|
|
106
|
+
firstNameAttribute !== null && firstNameAttribute !== void 0 ? firstNameAttribute : (firstNameAttribute = 'givenName');
|
|
107
|
+
lastNameAttribute !== null && lastNameAttribute !== void 0 ? lastNameAttribute : (lastNameAttribute = 'sn');
|
|
108
|
+
mailAttribute !== null && mailAttribute !== void 0 ? mailAttribute : (mailAttribute = 'mail');
|
|
102
109
|
return new Promise((resolve, reject) => {
|
|
103
|
-
// Search for the user in LDAP by
|
|
104
|
-
this.bindClient.search(
|
|
105
|
-
filter
|
|
106
|
-
scope
|
|
110
|
+
// Search for the user in LDAP by filter
|
|
111
|
+
this.bindClient.search(baseDn, {
|
|
112
|
+
filter,
|
|
113
|
+
scope,
|
|
114
|
+
attributes: ['uid', firstNameAttribute, lastNameAttribute, mailAttribute, 'userAccountControl'],
|
|
107
115
|
}, (err, res) => {
|
|
108
116
|
if (err) {
|
|
109
117
|
reject(handleError(err));
|
|
110
118
|
return;
|
|
111
119
|
}
|
|
112
120
|
res.on('searchEntry', ({ object }) => {
|
|
113
|
-
|
|
114
|
-
});
|
|
115
|
-
res.on('error', (err) => {
|
|
116
|
-
reject(handleError(err));
|
|
117
|
-
});
|
|
118
|
-
res.on('end', () => {
|
|
119
|
-
resolve(undefined);
|
|
120
|
-
});
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
async fetchUserInfo(userDn) {
|
|
125
|
-
const { mailAttribute } = this.config;
|
|
126
|
-
return new Promise((resolve, reject) => {
|
|
127
|
-
// Fetch user info in LDAP by domain component
|
|
128
|
-
this.bindClient.search(userDn, { attributes: ['givenName', 'sn', mailAttribute !== null && mailAttribute !== void 0 ? mailAttribute : 'mail', 'userAccountControl'] }, (err, res) => {
|
|
129
|
-
if (err) {
|
|
130
|
-
reject(handleError(err));
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
res.on('searchEntry', ({ object }) => {
|
|
134
|
-
const email = object[mailAttribute !== null && mailAttribute !== void 0 ? mailAttribute : 'mail'];
|
|
121
|
+
var _a;
|
|
135
122
|
const user = {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
123
|
+
dn: object.dn,
|
|
124
|
+
uid: getEntryValue(object.uid),
|
|
125
|
+
firstName: getEntryValue(object[firstNameAttribute]),
|
|
126
|
+
lastName: getEntryValue(object[lastNameAttribute]),
|
|
127
|
+
email: getEntryValue(object[mailAttribute]),
|
|
128
|
+
userAccountControl: Number((_a = getEntryValue(object.userAccountControl)) !== null && _a !== void 0 ? _a : 0),
|
|
142
129
|
};
|
|
143
130
|
resolve(user);
|
|
144
131
|
});
|
|
@@ -151,18 +138,14 @@ class LDAPAuthDriver extends auth_1.AuthDriver {
|
|
|
151
138
|
});
|
|
152
139
|
});
|
|
153
140
|
}
|
|
154
|
-
async fetchUserGroups(
|
|
155
|
-
const { groupDn, groupAttribute, groupScope } = this.config;
|
|
156
|
-
if (!groupDn) {
|
|
157
|
-
return Promise.resolve([]);
|
|
158
|
-
}
|
|
141
|
+
async fetchUserGroups(baseDn, filter, scope) {
|
|
159
142
|
return new Promise((resolve, reject) => {
|
|
160
143
|
let userGroups = [];
|
|
161
144
|
// Search for the user info in LDAP by group attribute
|
|
162
|
-
this.bindClient.search(
|
|
145
|
+
this.bindClient.search(baseDn, {
|
|
146
|
+
filter,
|
|
147
|
+
scope,
|
|
163
148
|
attributes: ['cn'],
|
|
164
|
-
filter: new ldapjs_1.EqualityFilter({ attribute: groupAttribute !== null && groupAttribute !== void 0 ? groupAttribute : 'member', value: userDn }),
|
|
165
|
-
scope: groupScope !== null && groupScope !== void 0 ? groupScope : 'one',
|
|
166
149
|
}, (err, res) => {
|
|
167
150
|
if (err) {
|
|
168
151
|
reject(handleError(err));
|
|
@@ -199,28 +182,36 @@ class LDAPAuthDriver extends auth_1.AuthDriver {
|
|
|
199
182
|
throw new exceptions_1.InvalidCredentialsException();
|
|
200
183
|
}
|
|
201
184
|
await this.validateBindClient();
|
|
202
|
-
const userDn =
|
|
203
|
-
|
|
185
|
+
const { userDn, userScope, userAttribute, groupDn, groupScope, groupAttribute } = this.config;
|
|
186
|
+
const userInfo = await this.fetchUserInfo(userDn, new ldapjs_1.EqualityFilter({
|
|
187
|
+
attribute: userAttribute !== null && userAttribute !== void 0 ? userAttribute : 'cn',
|
|
188
|
+
value: payload.identifier,
|
|
189
|
+
}), userScope !== null && userScope !== void 0 ? userScope : 'one');
|
|
190
|
+
if (!(userInfo === null || userInfo === void 0 ? void 0 : userInfo.dn)) {
|
|
204
191
|
throw new exceptions_1.InvalidCredentialsException();
|
|
205
192
|
}
|
|
206
|
-
const userId = await this.fetchUserId(userDn);
|
|
207
|
-
const userGroups = await this.fetchUserGroups(userDn);
|
|
208
193
|
let userRole;
|
|
209
|
-
if (
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
.
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
194
|
+
if (groupDn) {
|
|
195
|
+
const userGroups = await this.fetchUserGroups(groupDn, new ldapjs_1.EqualityFilter({
|
|
196
|
+
attribute: groupAttribute !== null && groupAttribute !== void 0 ? groupAttribute : 'member',
|
|
197
|
+
value: (groupAttribute === null || groupAttribute === void 0 ? void 0 : groupAttribute.toLowerCase()) === 'memberuid' && userInfo.uid ? userInfo.uid : userInfo.dn,
|
|
198
|
+
}), groupScope !== null && groupScope !== void 0 ? groupScope : 'one');
|
|
199
|
+
if (userGroups.length) {
|
|
200
|
+
userRole = await this.knex
|
|
201
|
+
.select('id')
|
|
202
|
+
.from('directus_roles')
|
|
203
|
+
.whereRaw(`LOWER(??) IN (${userGroups.map(() => '?')})`, [
|
|
204
|
+
'name',
|
|
205
|
+
...userGroups.map((group) => group.toLowerCase()),
|
|
206
|
+
])
|
|
207
|
+
.first();
|
|
208
|
+
}
|
|
218
209
|
}
|
|
210
|
+
const userId = await this.fetchUserId(userInfo.dn);
|
|
219
211
|
if (userId) {
|
|
220
212
|
await this.usersService.updateOne(userId, { role: (_a = userRole === null || userRole === void 0 ? void 0 : userRole.id) !== null && _a !== void 0 ? _a : null });
|
|
221
213
|
return userId;
|
|
222
214
|
}
|
|
223
|
-
const userInfo = await this.fetchUserInfo(userDn);
|
|
224
215
|
if (!userInfo) {
|
|
225
216
|
throw new exceptions_1.InvalidCredentialsException();
|
|
226
217
|
}
|
|
@@ -229,10 +220,10 @@ class LDAPAuthDriver extends auth_1.AuthDriver {
|
|
|
229
220
|
first_name: userInfo.firstName,
|
|
230
221
|
last_name: userInfo.lastName,
|
|
231
222
|
email: userInfo.email,
|
|
232
|
-
external_identifier:
|
|
223
|
+
external_identifier: userInfo.dn,
|
|
233
224
|
role: userRole === null || userRole === void 0 ? void 0 : userRole.id,
|
|
234
225
|
});
|
|
235
|
-
return (await this.fetchUserId(
|
|
226
|
+
return (await this.fetchUserId(userInfo.dn));
|
|
236
227
|
}
|
|
237
228
|
async verify(user, password) {
|
|
238
229
|
if (!user.external_identifier || !password) {
|
|
@@ -282,6 +273,9 @@ const handleError = (e) => {
|
|
|
282
273
|
message: e.message,
|
|
283
274
|
});
|
|
284
275
|
};
|
|
276
|
+
const getEntryValue = (value) => {
|
|
277
|
+
return typeof value === 'object' ? value[0] : value;
|
|
278
|
+
};
|
|
285
279
|
function createLDAPAuthRouter(provider) {
|
|
286
280
|
const router = (0, express_1.Router)();
|
|
287
281
|
const loginSchema = joi_1.default.object({
|
|
@@ -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_settings', 'project_color', {
|
|
8
|
+
nullable: true,
|
|
9
|
+
default: null,
|
|
10
|
+
length: 50,
|
|
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_settings', 'project_color', {
|
|
17
|
+
nullable: true,
|
|
18
|
+
default: '#00C897',
|
|
19
|
+
length: 10,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
exports.down = down;
|
package/dist/database/run-ast.js
CHANGED
|
@@ -3,15 +3,16 @@ 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
|
+
const utils_1 = require("@directus/shared/utils");
|
|
6
7
|
const lodash_1 = require("lodash");
|
|
8
|
+
const _1 = __importDefault(require("."));
|
|
9
|
+
const helpers_1 = require("../database/helpers");
|
|
10
|
+
const env_1 = __importDefault(require("../env"));
|
|
7
11
|
const payload_1 = require("../services/payload");
|
|
8
12
|
const apply_function_to_column_name_1 = require("../utils/apply-function-to-column-name");
|
|
9
13
|
const apply_query_1 = __importDefault(require("../utils/apply-query"));
|
|
10
14
|
const get_column_1 = require("../utils/get-column");
|
|
11
15
|
const strip_function_1 = require("../utils/strip-function");
|
|
12
|
-
const utils_1 = require("@directus/shared/utils");
|
|
13
|
-
const _1 = __importDefault(require("."));
|
|
14
|
-
const helpers_1 = require("../database/helpers");
|
|
15
16
|
/**
|
|
16
17
|
* Execute a given AST using Knex. Returns array of items based on requested AST.
|
|
17
18
|
*/
|
|
@@ -44,10 +45,30 @@ async function runAST(originalAST, schema, options) {
|
|
|
44
45
|
// Apply the `_in` filters to the nested collection batches
|
|
45
46
|
const nestedNodes = applyParentFilters(schema, nestedCollectionNodes, items);
|
|
46
47
|
for (const nestedNode of nestedNodes) {
|
|
47
|
-
|
|
48
|
-
if (
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
let nestedItems = [];
|
|
49
|
+
if (nestedNode.type === 'o2m') {
|
|
50
|
+
let hasMore = true;
|
|
51
|
+
let batchCount = 0;
|
|
52
|
+
while (hasMore) {
|
|
53
|
+
const node = (0, lodash_1.merge)({}, nestedNode, {
|
|
54
|
+
query: { limit: env_1.default.RELATIONAL_BATCH_SIZE, offset: batchCount * env_1.default.RELATIONAL_BATCH_SIZE },
|
|
55
|
+
});
|
|
56
|
+
nestedItems = (await runAST(node, schema, { knex, nested: true }));
|
|
57
|
+
if (nestedItems) {
|
|
58
|
+
items = mergeWithParentItems(schema, nestedItems, items, nestedNode);
|
|
59
|
+
}
|
|
60
|
+
if (!nestedItems || nestedItems.length < env_1.default.RELATIONAL_BATCH_SIZE) {
|
|
61
|
+
hasMore = false;
|
|
62
|
+
}
|
|
63
|
+
batchCount++;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
nestedItems = (await runAST(nestedNode, schema, { knex, nested: true }));
|
|
68
|
+
if (nestedItems) {
|
|
69
|
+
// Merge all fetched nested records with the parent items
|
|
70
|
+
items = mergeWithParentItems(schema, nestedItems, items, nestedNode);
|
|
71
|
+
}
|
|
51
72
|
}
|
|
52
73
|
}
|
|
53
74
|
// During the fetching of data, we have to inject a couple of required fields for the child nesting
|
|
@@ -146,13 +167,7 @@ function applyParentFilters(schema, nestedCollectionNodes, parentItem) {
|
|
|
146
167
|
if (nestedNode.type === 'm2o') {
|
|
147
168
|
const foreignField = schema.collections[nestedNode.relation.related_collection].primary;
|
|
148
169
|
const foreignIds = (0, lodash_1.uniq)(parentItems.map((res) => res[nestedNode.relation.field])).filter((id) => id);
|
|
149
|
-
|
|
150
|
-
if (limit === -1) {
|
|
151
|
-
(0, lodash_1.merge)(nestedNode, { query: { filter: { [foreignField]: { _in: foreignIds } } } });
|
|
152
|
-
}
|
|
153
|
-
else {
|
|
154
|
-
nestedNode.query.union = [foreignField, foreignIds];
|
|
155
|
-
}
|
|
170
|
+
(0, lodash_1.merge)(nestedNode, { query: { filter: { [foreignField]: { _in: foreignIds } } } });
|
|
156
171
|
}
|
|
157
172
|
else if (nestedNode.type === 'o2m') {
|
|
158
173
|
const relatedM2OisFetched = !!nestedNode.children.find((child) => {
|
|
@@ -174,13 +189,7 @@ function applyParentFilters(schema, nestedCollectionNodes, parentItem) {
|
|
|
174
189
|
}
|
|
175
190
|
const foreignField = nestedNode.relation.field;
|
|
176
191
|
const foreignIds = (0, lodash_1.uniq)(parentItems.map((res) => res[nestedNode.parentKey])).filter((id) => id);
|
|
177
|
-
|
|
178
|
-
if (limit === -1) {
|
|
179
|
-
(0, lodash_1.merge)(nestedNode, { query: { filter: { [foreignField]: { _in: foreignIds } } } });
|
|
180
|
-
}
|
|
181
|
-
else {
|
|
182
|
-
nestedNode.query.union = [foreignField, foreignIds];
|
|
183
|
-
}
|
|
192
|
+
(0, lodash_1.merge)(nestedNode, { query: { filter: { [foreignField]: { _in: foreignIds } } } });
|
|
184
193
|
}
|
|
185
194
|
else if (nestedNode.type === 'a2o') {
|
|
186
195
|
const keysPerCollection = {};
|
|
@@ -193,20 +202,16 @@ function applyParentFilters(schema, nestedCollectionNodes, parentItem) {
|
|
|
193
202
|
for (const relatedCollection of nestedNode.names) {
|
|
194
203
|
const foreignField = nestedNode.relatedKey[relatedCollection];
|
|
195
204
|
const foreignIds = (0, lodash_1.uniq)(keysPerCollection[relatedCollection]);
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
}
|
|
200
|
-
else {
|
|
201
|
-
nestedNode.query[relatedCollection].union = [foreignField, foreignIds];
|
|
202
|
-
}
|
|
205
|
+
(0, lodash_1.merge)(nestedNode, {
|
|
206
|
+
query: { [relatedCollection]: { filter: { [foreignField]: { _in: foreignIds } } } },
|
|
207
|
+
});
|
|
203
208
|
}
|
|
204
209
|
}
|
|
205
210
|
}
|
|
206
211
|
return nestedCollectionNodes;
|
|
207
212
|
}
|
|
208
213
|
function mergeWithParentItems(schema, nestedItem, parentItem, nestedNode) {
|
|
209
|
-
var _a;
|
|
214
|
+
var _a, _b;
|
|
210
215
|
const nestedItems = (0, utils_1.toArray)(nestedItem);
|
|
211
216
|
const parentItems = (0, lodash_1.clone)((0, utils_1.toArray)(parentItem));
|
|
212
217
|
if (nestedNode.type === 'm2o') {
|
|
@@ -220,8 +225,9 @@ function mergeWithParentItems(schema, nestedItem, parentItem, nestedNode) {
|
|
|
220
225
|
}
|
|
221
226
|
else if (nestedNode.type === 'o2m') {
|
|
222
227
|
for (const parentItem of parentItems) {
|
|
223
|
-
|
|
224
|
-
.
|
|
228
|
+
if (!parentItem[nestedNode.fieldKey])
|
|
229
|
+
parentItem[nestedNode.fieldKey] = [];
|
|
230
|
+
const itemChildren = nestedItems.filter((nestedItem) => {
|
|
225
231
|
var _a;
|
|
226
232
|
if (nestedItem === null)
|
|
227
233
|
return false;
|
|
@@ -230,8 +236,10 @@ function mergeWithParentItems(schema, nestedItem, parentItem, nestedNode) {
|
|
|
230
236
|
return (nestedItem[nestedNode.relation.field] ==
|
|
231
237
|
parentItem[schema.collections[nestedNode.relation.related_collection].primary] ||
|
|
232
238
|
((_a = nestedItem[nestedNode.relation.field]) === null || _a === void 0 ? void 0 : _a[schema.collections[nestedNode.relation.related_collection].primary]) == parentItem[schema.collections[nestedNode.relation.related_collection].primary]);
|
|
233
|
-
})
|
|
234
|
-
|
|
239
|
+
});
|
|
240
|
+
parentItem[nestedNode.fieldKey].push(...itemChildren);
|
|
241
|
+
parentItem[nestedNode.fieldKey] = parentItem[nestedNode.fieldKey].slice(0, (_a = nestedNode.query.limit) !== null && _a !== void 0 ? _a : 100);
|
|
242
|
+
parentItem[nestedNode.fieldKey] = parentItem[nestedNode.fieldKey].sort((a, b) => {
|
|
235
243
|
// This is pre-filled in get-ast-from-query
|
|
236
244
|
const sortField = nestedNode.query.sort[0];
|
|
237
245
|
let column = sortField;
|
|
@@ -253,12 +261,11 @@ function mergeWithParentItems(schema, nestedItem, parentItem, nestedNode) {
|
|
|
253
261
|
return a[column] < b[column] ? 1 : -1;
|
|
254
262
|
}
|
|
255
263
|
});
|
|
256
|
-
parentItem[nestedNode.fieldKey] = itemChildren.length > 0 ? itemChildren : [];
|
|
257
264
|
}
|
|
258
265
|
}
|
|
259
266
|
else if (nestedNode.type === 'a2o') {
|
|
260
267
|
for (const parentItem of parentItems) {
|
|
261
|
-
if (!((
|
|
268
|
+
if (!((_b = nestedNode.relation.meta) === null || _b === void 0 ? void 0 : _b.one_collection_field)) {
|
|
262
269
|
parentItem[nestedNode.fieldKey] = null;
|
|
263
270
|
continue;
|
|
264
271
|
}
|
|
@@ -87,9 +87,25 @@ fields:
|
|
|
87
87
|
language: css
|
|
88
88
|
lineNumber: true
|
|
89
89
|
template: |
|
|
90
|
-
#app, #main-content {
|
|
91
|
-
--
|
|
92
|
-
--
|
|
90
|
+
#app, #main-content, body {
|
|
91
|
+
--primary-alt: #F0ECFF !important;
|
|
92
|
+
--primary-10: #F0ECFF !important;
|
|
93
|
+
--primary-25: #D9D0FF !important;
|
|
94
|
+
--primary-50: #B3A1FF !important;
|
|
95
|
+
--primary-75: #8C73FF !important;
|
|
96
|
+
--primary-90: #7557FF !important;
|
|
97
|
+
|
|
98
|
+
--primary: #6644FF !important;
|
|
99
|
+
|
|
100
|
+
--primary-110: #5E41EC !important;
|
|
101
|
+
--primary-125: #523DCF !important;
|
|
102
|
+
--primary-150: #3E369F !important;
|
|
103
|
+
--primary-175: #2B3070 !important;
|
|
104
|
+
--primary-190: #1F2C53 !important;
|
|
105
|
+
|
|
106
|
+
--v-button-background-color: #6644FF !important;
|
|
107
|
+
--v-button-background-color-hover: #5E41EC !important;
|
|
108
|
+
--sidebar-detail-color-active: #5E41EC !important;
|
|
93
109
|
}
|
|
94
110
|
width: full
|
|
95
111
|
|
package/dist/env.js
CHANGED
|
@@ -17,6 +17,7 @@ const utils_1 = require("@directus/shared/utils");
|
|
|
17
17
|
const acceptedEnvTypes = ['string', 'number', 'regex', 'array'];
|
|
18
18
|
const defaults = {
|
|
19
19
|
CONFIG_PATH: path_1.default.resolve(process.cwd(), '.env'),
|
|
20
|
+
HOST: '0.0.0.0',
|
|
20
21
|
PORT: 8055,
|
|
21
22
|
PUBLIC_URL: '/',
|
|
22
23
|
MAX_PAYLOAD_SIZE: '100kb',
|
|
@@ -65,10 +66,12 @@ const defaults = {
|
|
|
65
66
|
IP_TRUST_PROXY: true,
|
|
66
67
|
IP_CUSTOM_HEADER: false,
|
|
67
68
|
SERVE_APP: true,
|
|
69
|
+
RELATIONAL_BATCH_SIZE: 25000,
|
|
68
70
|
};
|
|
69
71
|
// Allows us to force certain environment variable into a type, instead of relying
|
|
70
72
|
// on the auto-parsed type in processValues. ref #3705
|
|
71
73
|
const typeMap = {
|
|
74
|
+
HOST: 'string',
|
|
72
75
|
PORT: 'string',
|
|
73
76
|
DB_NAME: 'string',
|
|
74
77
|
DB_USER: 'string',
|
package/dist/extensions.js
CHANGED
|
@@ -38,8 +38,6 @@ const get_schema_1 = require("./utils/get-schema");
|
|
|
38
38
|
const services = __importStar(require("./services"));
|
|
39
39
|
const node_cron_1 = require("node-cron");
|
|
40
40
|
const rollup_1 = require("rollup");
|
|
41
|
-
// @TODO Remove this once a new version of @rollup/plugin-virtual has been released
|
|
42
|
-
// @ts-expect-error
|
|
43
41
|
const plugin_virtual_1 = __importDefault(require("@rollup/plugin-virtual"));
|
|
44
42
|
const plugin_alias_1 = __importDefault(require("@rollup/plugin-alias"));
|
|
45
43
|
const url_1 = require("./utils/url");
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
/// <reference types="qs" />
|
|
2
|
+
import { NextFunction, Request, Response } from 'express';
|
|
2
3
|
/**
|
|
3
4
|
* Verify the passed JWT and assign the user ID and role to `req`
|
|
4
5
|
*/
|
|
5
|
-
declare const
|
|
6
|
-
|
|
6
|
+
export declare const handler: (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
7
|
+
declare const _default: import("express").RequestHandler<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>;
|
|
8
|
+
export default _default;
|
|
@@ -1,39 +1,23 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
-
}) : (function(o, m, k, k2) {
|
|
6
|
-
if (k2 === undefined) k2 = k;
|
|
7
|
-
o[k2] = m[k];
|
|
8
|
-
}));
|
|
9
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
10
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
11
|
-
}) : function(o, v) {
|
|
12
|
-
o["default"] = v;
|
|
13
|
-
});
|
|
14
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
15
|
-
if (mod && mod.__esModule) return mod;
|
|
16
|
-
var result = {};
|
|
17
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
18
|
-
__setModuleDefault(result, mod);
|
|
19
|
-
return result;
|
|
20
|
-
};
|
|
21
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
22
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
23
4
|
};
|
|
24
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
-
|
|
6
|
+
exports.handler = void 0;
|
|
7
|
+
const lodash_1 = require("lodash");
|
|
26
8
|
const database_1 = __importDefault(require("../database"));
|
|
9
|
+
const emitter_1 = __importDefault(require("../emitter"));
|
|
27
10
|
const env_1 = __importDefault(require("../env"));
|
|
28
11
|
const exceptions_1 = require("../exceptions");
|
|
29
12
|
const async_handler_1 = __importDefault(require("../utils/async-handler"));
|
|
30
13
|
const get_ip_from_req_1 = require("../utils/get-ip-from-req");
|
|
31
14
|
const is_directus_jwt_1 = __importDefault(require("../utils/is-directus-jwt"));
|
|
15
|
+
const jwt_1 = require("../utils/jwt");
|
|
32
16
|
/**
|
|
33
17
|
* Verify the passed JWT and assign the user ID and role to `req`
|
|
34
18
|
*/
|
|
35
|
-
const
|
|
36
|
-
|
|
19
|
+
const handler = async (req, res, next) => {
|
|
20
|
+
const defaultAccountability = {
|
|
37
21
|
user: null,
|
|
38
22
|
role: null,
|
|
39
23
|
admin: false,
|
|
@@ -42,23 +26,21 @@ const authenticate = (0, async_handler_1.default)(async (req, res, next) => {
|
|
|
42
26
|
userAgent: req.get('user-agent'),
|
|
43
27
|
};
|
|
44
28
|
const database = (0, database_1.default)();
|
|
29
|
+
const customAccountability = await emitter_1.default.emitFilter('authenticate', defaultAccountability, {
|
|
30
|
+
req,
|
|
31
|
+
}, {
|
|
32
|
+
database,
|
|
33
|
+
schema: null,
|
|
34
|
+
accountability: null,
|
|
35
|
+
});
|
|
36
|
+
if (customAccountability && (0, lodash_1.isEqual)(customAccountability, defaultAccountability) === false) {
|
|
37
|
+
req.accountability = customAccountability;
|
|
38
|
+
return next();
|
|
39
|
+
}
|
|
40
|
+
req.accountability = defaultAccountability;
|
|
45
41
|
if (req.token) {
|
|
46
42
|
if ((0, is_directus_jwt_1.default)(req.token)) {
|
|
47
|
-
|
|
48
|
-
try {
|
|
49
|
-
payload = jsonwebtoken_1.default.verify(req.token, env_1.default.SECRET, { issuer: 'directus' });
|
|
50
|
-
}
|
|
51
|
-
catch (err) {
|
|
52
|
-
if (err instanceof jsonwebtoken_1.TokenExpiredError) {
|
|
53
|
-
throw new exceptions_1.InvalidCredentialsException('Token expired.');
|
|
54
|
-
}
|
|
55
|
-
else if (err instanceof jsonwebtoken_1.JsonWebTokenError) {
|
|
56
|
-
throw new exceptions_1.InvalidCredentialsException('Token invalid.');
|
|
57
|
-
}
|
|
58
|
-
else {
|
|
59
|
-
throw err;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
43
|
+
const payload = (0, jwt_1.verifyAccessJWT)(req.token, env_1.default.SECRET);
|
|
62
44
|
req.accountability.share = payload.share;
|
|
63
45
|
req.accountability.share_scope = payload.share_scope;
|
|
64
46
|
req.accountability.user = payload.id;
|
|
@@ -87,5 +69,6 @@ const authenticate = (0, async_handler_1.default)(async (req, res, next) => {
|
|
|
87
69
|
}
|
|
88
70
|
}
|
|
89
71
|
return next();
|
|
90
|
-
}
|
|
91
|
-
exports.
|
|
72
|
+
};
|
|
73
|
+
exports.handler = handler;
|
|
74
|
+
exports.default = (0, async_handler_1.default)(exports.handler);
|
package/dist/server.js
CHANGED
|
@@ -36,8 +36,10 @@ const logger_1 = __importDefault(require("./logger"));
|
|
|
36
36
|
const emitter_1 = __importDefault(require("./emitter"));
|
|
37
37
|
const update_check_1 = __importDefault(require("update-check"));
|
|
38
38
|
const package_json_1 = __importDefault(require("../package.json"));
|
|
39
|
+
const get_config_from_env_1 = require("./utils/get-config-from-env");
|
|
39
40
|
async function createServer() {
|
|
40
41
|
const server = http.createServer(await (0, app_1.default)());
|
|
42
|
+
Object.assign(server, (0, get_config_from_env_1.getConfigFromEnv)('SERVER_'));
|
|
41
43
|
server.on('request', function (req, res) {
|
|
42
44
|
const startTime = process.hrtime();
|
|
43
45
|
const complete = (0, lodash_1.once)(function (finished) {
|
|
@@ -124,9 +126,10 @@ async function createServer() {
|
|
|
124
126
|
exports.createServer = createServer;
|
|
125
127
|
async function startServer() {
|
|
126
128
|
const server = await createServer();
|
|
129
|
+
const host = env_1.default.HOST;
|
|
127
130
|
const port = env_1.default.PORT;
|
|
128
131
|
server
|
|
129
|
-
.listen(port, () => {
|
|
132
|
+
.listen(port, host, () => {
|
|
130
133
|
(0, update_check_1.default)(package_json_1.default)
|
|
131
134
|
.then((update) => {
|
|
132
135
|
if (update) {
|
|
@@ -136,7 +139,7 @@ async function startServer() {
|
|
|
136
139
|
.catch(() => {
|
|
137
140
|
// No need to log/warn here. The update message is only an informative nice-to-have
|
|
138
141
|
});
|
|
139
|
-
logger_1.default.info(`Server started at http
|
|
142
|
+
logger_1.default.info(`Server started at http://${host}:${port}`);
|
|
140
143
|
emitter_1.default.emitAction('server.start', { server }, {
|
|
141
144
|
database: (0, database_1.default)(),
|
|
142
145
|
schema: null,
|
|
@@ -11,6 +11,7 @@ const exceptions_2 = require("@directus/shared/exceptions");
|
|
|
11
11
|
const utils_1 = require("@directus/shared/utils");
|
|
12
12
|
const items_1 = require("./items");
|
|
13
13
|
const payload_1 = require("./payload");
|
|
14
|
+
const strip_function_1 = require("../utils/strip-function");
|
|
14
15
|
class AuthorizationService {
|
|
15
16
|
constructor(options) {
|
|
16
17
|
this.knex = options.knex || (0, database_1.default)();
|
|
@@ -97,7 +98,7 @@ class AuthorizationService {
|
|
|
97
98
|
}
|
|
98
99
|
if (allowedFields.includes('*'))
|
|
99
100
|
continue;
|
|
100
|
-
const fieldKey = childNode.name;
|
|
101
|
+
const fieldKey = (0, strip_function_1.stripFunction)(childNode.name);
|
|
101
102
|
if (allowedFields.includes(fieldKey) === false) {
|
|
102
103
|
throw new exceptions_1.ForbiddenException();
|
|
103
104
|
}
|
|
@@ -35,7 +35,7 @@ a[x-apple-data-detectors] {
|
|
|
35
35
|
line-height: inherit !important;
|
|
36
36
|
}
|
|
37
37
|
body a {
|
|
38
|
-
color: #
|
|
38
|
+
color: #6644ff;
|
|
39
39
|
text-decoration: none;
|
|
40
40
|
}
|
|
41
41
|
hr {
|
|
@@ -74,7 +74,7 @@ hr {
|
|
|
74
74
|
color: #FFFFFF !important;
|
|
75
75
|
}
|
|
76
76
|
.link {
|
|
77
|
-
color: #
|
|
77
|
+
color: #6644ff !important;
|
|
78
78
|
}
|
|
79
79
|
.button {
|
|
80
80
|
background-color:#0BA582 !important;
|
package/dist/services/utils.js
CHANGED
|
@@ -7,6 +7,7 @@ exports.UtilsService = void 0;
|
|
|
7
7
|
const database_1 = __importDefault(require("../database"));
|
|
8
8
|
const collections_1 = require("../database/system-data/collections");
|
|
9
9
|
const exceptions_1 = require("../exceptions");
|
|
10
|
+
const emitter_1 = __importDefault(require("../emitter"));
|
|
10
11
|
class UtilsService {
|
|
11
12
|
constructor(options) {
|
|
12
13
|
this.knex = options.knex || (0, database_1.default)();
|
|
@@ -98,6 +99,15 @@ class UtilsService {
|
|
|
98
99
|
.andWhere(sortField, '<=', sourceSortValue)
|
|
99
100
|
.andWhereNot({ [primaryKeyField]: item });
|
|
100
101
|
}
|
|
102
|
+
emitter_1.default.emitAction(['items.sort', `${collection}.items.sort`], {
|
|
103
|
+
collection,
|
|
104
|
+
item,
|
|
105
|
+
to,
|
|
106
|
+
}, {
|
|
107
|
+
database: this.knex,
|
|
108
|
+
schema: this.schema,
|
|
109
|
+
accountability: this.accountability,
|
|
110
|
+
});
|
|
101
111
|
}
|
|
102
112
|
}
|
|
103
113
|
exports.UtilsService = UtilsService;
|
|
@@ -48,29 +48,7 @@ function applyQuery(knex, collection, dbQuery, query, schema, subQuery = false)
|
|
|
48
48
|
if (query.aggregate) {
|
|
49
49
|
applyAggregate(dbQuery, query.aggregate, collection);
|
|
50
50
|
}
|
|
51
|
-
if (query.
|
|
52
|
-
const [field, keys] = query.union;
|
|
53
|
-
const queries = keys.map((key) => {
|
|
54
|
-
const unionFilter = { [field]: { _eq: key } };
|
|
55
|
-
let filter = (0, lodash_1.cloneDeep)(query.filter);
|
|
56
|
-
if (filter) {
|
|
57
|
-
if ('_and' in filter) {
|
|
58
|
-
filter._and.push(unionFilter);
|
|
59
|
-
}
|
|
60
|
-
else {
|
|
61
|
-
filter = {
|
|
62
|
-
_and: [filter, unionFilter],
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
else {
|
|
67
|
-
filter = unionFilter;
|
|
68
|
-
}
|
|
69
|
-
return knex.select('*').from(applyFilter(knex, schema, dbQuery.clone(), filter, collection, subQuery).as('foo'));
|
|
70
|
-
});
|
|
71
|
-
dbQuery = knex.unionAll(queries);
|
|
72
|
-
}
|
|
73
|
-
else if (query.filter) {
|
|
51
|
+
if (query.filter) {
|
|
74
52
|
applyFilter(knex, schema, dbQuery, query.filter, collection, subQuery);
|
|
75
53
|
}
|
|
76
54
|
return dbQuery;
|
|
@@ -234,7 +212,7 @@ function applyFilter(knex, schema, rootQuery, rootFilter, collection, subQuery =
|
|
|
234
212
|
applyFilterToQuery(`${collection}.${filterPath[0]}`, filterOperator, filterValue, logical);
|
|
235
213
|
}
|
|
236
214
|
}
|
|
237
|
-
else if (subQuery === false) {
|
|
215
|
+
else if (subQuery === false || filterPath.length > 1) {
|
|
238
216
|
if (!relation)
|
|
239
217
|
continue;
|
|
240
218
|
let pkField = `${collection}.${schema.collections[relation.related_collection].primary}`;
|
|
@@ -3,37 +3,20 @@ 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
|
-
const
|
|
7
|
-
const logger_1 = __importDefault(require("../logger"));
|
|
6
|
+
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
|
8
7
|
/**
|
|
9
8
|
* Check if a given string conforms to the structure of a JWT
|
|
10
9
|
* and whether it is issued by Directus.
|
|
11
10
|
*/
|
|
12
11
|
function isDirectusJWT(string) {
|
|
13
|
-
const parts = string.split('.');
|
|
14
|
-
// JWTs have the structure header.payload.signature
|
|
15
|
-
if (parts.length !== 3)
|
|
16
|
-
return false;
|
|
17
|
-
// Check if all segments are base64 encoded
|
|
18
|
-
try {
|
|
19
|
-
(0, atob_1.default)(parts[0]);
|
|
20
|
-
(0, atob_1.default)(parts[1]);
|
|
21
|
-
(0, atob_1.default)(parts[2]);
|
|
22
|
-
}
|
|
23
|
-
catch (err) {
|
|
24
|
-
logger_1.default.error(err);
|
|
25
|
-
return false;
|
|
26
|
-
}
|
|
27
|
-
// Check if the header and payload are valid JSON
|
|
28
12
|
try {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
if (payload.iss !== 'directus')
|
|
13
|
+
const payload = jsonwebtoken_1.default.decode(string, { json: true });
|
|
14
|
+
if ((payload === null || payload === void 0 ? void 0 : payload.iss) !== 'directus')
|
|
32
15
|
return false;
|
|
16
|
+
return true;
|
|
33
17
|
}
|
|
34
18
|
catch {
|
|
35
19
|
return false;
|
|
36
20
|
}
|
|
37
|
-
return true;
|
|
38
21
|
}
|
|
39
22
|
exports.default = isDirectusJWT;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
+
}) : (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
o[k2] = m[k];
|
|
8
|
+
}));
|
|
9
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
10
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
11
|
+
}) : function(o, v) {
|
|
12
|
+
o["default"] = v;
|
|
13
|
+
});
|
|
14
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
15
|
+
if (mod && mod.__esModule) return mod;
|
|
16
|
+
var result = {};
|
|
17
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
18
|
+
__setModuleDefault(result, mod);
|
|
19
|
+
return result;
|
|
20
|
+
};
|
|
21
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
exports.verifyAccessJWT = void 0;
|
|
23
|
+
const jsonwebtoken_1 = __importStar(require("jsonwebtoken"));
|
|
24
|
+
const exceptions_1 = require("../exceptions");
|
|
25
|
+
function verifyAccessJWT(token, secret) {
|
|
26
|
+
let payload;
|
|
27
|
+
try {
|
|
28
|
+
payload = jsonwebtoken_1.default.verify(token, secret, {
|
|
29
|
+
issuer: 'directus',
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
if (err instanceof jsonwebtoken_1.TokenExpiredError) {
|
|
34
|
+
throw new exceptions_1.InvalidTokenException('Token expired.');
|
|
35
|
+
}
|
|
36
|
+
else if (err instanceof jsonwebtoken_1.JsonWebTokenError) {
|
|
37
|
+
throw new exceptions_1.InvalidTokenException('Token invalid.');
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
throw new exceptions_1.ServiceUnavailableException(`Couldn't verify token.`, { service: 'jwt' });
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const { id, role, app_access, admin_access, share, share_scope } = payload;
|
|
44
|
+
if (role === undefined || app_access === undefined || admin_access === undefined) {
|
|
45
|
+
throw new exceptions_1.InvalidTokenException('Invalid token payload.');
|
|
46
|
+
}
|
|
47
|
+
return { id, role, app_access, admin_access, share, share_scope };
|
|
48
|
+
}
|
|
49
|
+
exports.verifyAccessJWT = verifyAccessJWT;
|
|
@@ -31,9 +31,15 @@ function mergePermission(strategy, currentPerm, newPerm) {
|
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
33
|
else if (currentPerm.permissions) {
|
|
34
|
-
permissions
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
// Empty {} supersedes other permissions in _OR merge
|
|
35
|
+
if (strategy === 'or' && ((0, lodash_1.isEqual)(currentPerm.permissions, {}) || (0, lodash_1.isEqual)(newPerm.permissions, {}))) {
|
|
36
|
+
permissions = {};
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
permissions = {
|
|
40
|
+
[logicalKey]: [currentPerm.permissions, newPerm.permissions],
|
|
41
|
+
};
|
|
42
|
+
}
|
|
37
43
|
}
|
|
38
44
|
else {
|
|
39
45
|
permissions = {
|
|
@@ -51,9 +57,15 @@ function mergePermission(strategy, currentPerm, newPerm) {
|
|
|
51
57
|
};
|
|
52
58
|
}
|
|
53
59
|
else if (currentPerm.validation) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
60
|
+
// Empty {} supersedes other validations in _OR merge
|
|
61
|
+
if (strategy === 'or' && ((0, lodash_1.isEqual)(currentPerm.validation, {}) || (0, lodash_1.isEqual)(newPerm.validation, {}))) {
|
|
62
|
+
validation = {};
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
validation = {
|
|
66
|
+
[logicalKey]: [currentPerm.validation, newPerm.validation],
|
|
67
|
+
};
|
|
68
|
+
}
|
|
57
69
|
}
|
|
58
70
|
else {
|
|
59
71
|
validation = {
|
package/example.env
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "directus",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.6.0",
|
|
4
4
|
"license": "GPL-3.0-only",
|
|
5
5
|
"homepage": "https://github.com/directus/directus#readme",
|
|
6
6
|
"description": "Directus is a real-time API and App dashboard for managing SQL database content.",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"cli": "cross-env NODE_ENV=development SERVE_APP=false ts-node --script-mode --transpile-only src/cli/run.ts",
|
|
66
66
|
"test": "jest",
|
|
67
67
|
"test:coverage": "jest --coverage",
|
|
68
|
-
"test:watch": "jest --
|
|
68
|
+
"test:watch": "jest --watch"
|
|
69
69
|
},
|
|
70
70
|
"engines": {
|
|
71
71
|
"node": ">=12.20.0"
|
|
@@ -78,23 +78,22 @@
|
|
|
78
78
|
],
|
|
79
79
|
"dependencies": {
|
|
80
80
|
"@aws-sdk/client-ses": "^3.40.0",
|
|
81
|
-
"@directus/app": "9.
|
|
82
|
-
"@directus/drive": "9.
|
|
83
|
-
"@directus/drive-azure": "9.
|
|
84
|
-
"@directus/drive-gcs": "9.
|
|
85
|
-
"@directus/drive-s3": "9.
|
|
86
|
-
"@directus/extensions-sdk": "9.
|
|
87
|
-
"@directus/format-title": "9.
|
|
88
|
-
"@directus/schema": "9.
|
|
89
|
-
"@directus/shared": "9.
|
|
90
|
-
"@directus/specs": "9.
|
|
81
|
+
"@directus/app": "9.6.0",
|
|
82
|
+
"@directus/drive": "9.6.0",
|
|
83
|
+
"@directus/drive-azure": "9.6.0",
|
|
84
|
+
"@directus/drive-gcs": "9.6.0",
|
|
85
|
+
"@directus/drive-s3": "9.6.0",
|
|
86
|
+
"@directus/extensions-sdk": "9.6.0",
|
|
87
|
+
"@directus/format-title": "9.6.0",
|
|
88
|
+
"@directus/schema": "9.6.0",
|
|
89
|
+
"@directus/shared": "9.6.0",
|
|
90
|
+
"@directus/specs": "9.6.0",
|
|
91
91
|
"@godaddy/terminus": "^4.9.0",
|
|
92
92
|
"@rollup/plugin-alias": "^3.1.9",
|
|
93
93
|
"@rollup/plugin-virtual": "^2.0.3",
|
|
94
94
|
"argon2": "^0.28.2",
|
|
95
95
|
"async": "^3.2.0",
|
|
96
96
|
"async-mutex": "^0.3.1",
|
|
97
|
-
"atob": "^2.1.2",
|
|
98
97
|
"axios": "^0.24.0",
|
|
99
98
|
"busboy": "^0.3.1",
|
|
100
99
|
"camelcase": "^6.2.0",
|
|
@@ -173,10 +172,9 @@
|
|
|
173
172
|
"sqlite3": "^5.0.2",
|
|
174
173
|
"tedious": "^13.0.0"
|
|
175
174
|
},
|
|
176
|
-
"gitHead": "
|
|
175
|
+
"gitHead": "eba2576227d493f87160973238bddadbf23bbe01",
|
|
177
176
|
"devDependencies": {
|
|
178
177
|
"@types/async": "3.2.10",
|
|
179
|
-
"@types/atob": "2.1.2",
|
|
180
178
|
"@types/body-parser": "1.19.2",
|
|
181
179
|
"@types/busboy": "0.3.1",
|
|
182
180
|
"@types/cookie-parser": "1.4.2",
|
|
@@ -189,7 +187,7 @@
|
|
|
189
187
|
"@types/flat": "5.0.2",
|
|
190
188
|
"@types/fs-extra": "9.0.13",
|
|
191
189
|
"@types/inquirer": "8.1.3",
|
|
192
|
-
"@types/jest": "27.
|
|
190
|
+
"@types/jest": "27.4.1",
|
|
193
191
|
"@types/js-yaml": "4.0.5",
|
|
194
192
|
"@types/json2csv": "5.0.3",
|
|
195
193
|
"@types/jsonwebtoken": "8.5.6",
|
|
@@ -215,9 +213,9 @@
|
|
|
215
213
|
"@types/wellknown": "0.5.1",
|
|
216
214
|
"copyfiles": "2.4.1",
|
|
217
215
|
"cross-env": "7.0.3",
|
|
218
|
-
"jest": "27.
|
|
216
|
+
"jest": "27.5.1",
|
|
219
217
|
"knex-mock-client": "1.6.1",
|
|
220
|
-
"ts-jest": "27.
|
|
218
|
+
"ts-jest": "27.1.3",
|
|
221
219
|
"ts-node-dev": "1.1.8",
|
|
222
220
|
"typescript": "4.5.2"
|
|
223
221
|
}
|
package/dist/__mocks__/cache.js
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getCache = exports.cache = void 0;
|
|
4
|
-
exports.cache = {
|
|
5
|
-
get: jest.fn().mockResolvedValue(undefined),
|
|
6
|
-
set: jest.fn().mockResolvedValue(true),
|
|
7
|
-
};
|
|
8
|
-
exports.getCache = jest.fn().mockReturnValue({ cache: exports.cache });
|