directus 9.12.2 → 9.13.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 +5 -1
- package/dist/auth/drivers/index.js +5 -1
- package/dist/auth/drivers/ldap.js +5 -1
- package/dist/auth/drivers/oauth2.js +15 -23
- package/dist/auth/drivers/openid.js +16 -24
- package/dist/cli/commands/bootstrap/index.js +5 -1
- package/dist/cli/commands/schema/apply.js +7 -3
- package/dist/cli/commands/schema/snapshot.d.ts +1 -1
- package/dist/cli/commands/schema/snapshot.js +33 -25
- package/dist/cli/index.js +1 -1
- package/dist/cli/utils/create-env/env-stub.liquid +11 -11
- package/dist/controllers/assets.js +5 -5
- package/dist/controllers/dashboards.js +4 -1
- package/dist/controllers/files.js +8 -5
- package/dist/controllers/flows.js +4 -1
- package/dist/controllers/folders.js +4 -1
- package/dist/controllers/items.js +4 -1
- package/dist/controllers/notifications.js +4 -1
- package/dist/controllers/operations.js +4 -1
- package/dist/controllers/panels.js +4 -1
- package/dist/controllers/permissions.js +4 -1
- package/dist/controllers/presets.js +4 -1
- package/dist/controllers/roles.js +4 -1
- package/dist/controllers/shares.js +4 -1
- package/dist/controllers/users.js +75 -3
- package/dist/controllers/utils.js +3 -3
- package/dist/database/helpers/date/dialects/sqlite.js +3 -0
- package/dist/database/helpers/index.js +5 -1
- package/dist/database/index.js +2 -0
- package/dist/database/migrations/20210225A-add-relations-sort-field.js +2 -2
- package/dist/database/migrations/20210506A-rename-interfaces.js +2 -2
- package/dist/database/migrations/20210802A-replace-groups.js +2 -2
- package/dist/database/migrations/20210805A-update-groups.js +2 -2
- package/dist/database/migrations/20210805B-change-image-metadata-structure.js +3 -3
- package/dist/database/migrations/20211007A-update-presets.js +5 -5
- package/dist/database/migrations/20220429A-add-flows.js +1 -2
- package/dist/database/migrations/20220614A-rename-hook-trigger-to-event.d.ts +3 -0
- package/dist/database/migrations/20220614A-rename-hook-trigger-to-event.js +11 -0
- package/dist/database/system-data/fields/dashboards.yaml +1 -0
- package/dist/env.d.ts +1 -1
- package/dist/env.js +8 -3
- package/dist/exceptions/database/translate.js +5 -1
- package/dist/exceptions/index.js +5 -1
- package/dist/extensions.js +5 -1
- package/dist/flows.js +15 -11
- package/dist/index.js +5 -1
- package/dist/logger.js +5 -1
- package/dist/messenger.js +2 -2
- package/dist/middleware/graphql.js +2 -2
- package/dist/middleware/respond.js +10 -1
- package/dist/middleware/validate-batch.js +3 -1
- package/dist/operations/item-create/index.js +1 -2
- package/dist/operations/item-delete/index.d.ts +1 -0
- package/dist/operations/item-delete/index.js +8 -7
- package/dist/operations/item-read/index.js +7 -6
- package/dist/operations/item-update/index.d.ts +1 -0
- package/dist/operations/item-update/index.js +9 -8
- package/dist/operations/log/index.js +1 -2
- package/dist/operations/notification/index.js +1 -2
- package/dist/operations/transform/index.js +1 -2
- package/dist/operations/trigger/index.js +1 -2
- package/dist/server.js +5 -1
- package/dist/services/assets.js +5 -1
- package/dist/services/collections.js +5 -1
- package/dist/services/fields.d.ts +3 -3
- package/dist/services/fields.js +25 -17
- package/dist/services/files.js +5 -1
- package/dist/services/flows.d.ts +1 -0
- package/dist/services/flows.js +6 -0
- package/dist/services/{graphql.d.ts → graphql/index.d.ts} +3 -5
- package/dist/services/{graphql.js → graphql/index.js} +109 -101
- package/dist/services/graphql/types/date.d.ts +2 -0
- package/dist/services/graphql/types/date.js +9 -0
- package/dist/services/graphql/types/geojson.d.ts +2 -0
- package/dist/services/graphql/types/geojson.js +10 -0
- package/dist/services/graphql/types/string-or-float.d.ts +5 -0
- package/dist/services/graphql/types/string-or-float.js +34 -0
- package/dist/services/graphql/types/void.d.ts +2 -0
- package/dist/services/graphql/types/void.js +17 -0
- package/dist/services/graphql/utils/add-path-to-validation-error.d.ts +2 -0
- package/dist/services/graphql/utils/add-path-to-validation-error.js +20 -0
- package/dist/services/import-export.js +6 -7
- package/dist/services/index.js +5 -1
- package/dist/services/items.d.ts +5 -1
- package/dist/services/items.js +22 -2
- package/dist/services/mail/index.js +8 -6
- package/dist/services/operations.d.ts +1 -0
- package/dist/services/operations.js +6 -0
- package/dist/services/payload.js +2 -3
- package/dist/services/permissions.d.ts +1 -0
- package/dist/services/permissions.js +5 -0
- package/dist/services/relations.js +5 -1
- package/dist/services/roles.d.ts +1 -0
- package/dist/services/roles.js +9 -0
- package/dist/services/server.js +5 -1
- package/dist/services/users.d.ts +1 -0
- package/dist/services/users.js +17 -0
- package/dist/types/index.js +5 -1
- package/dist/utils/apply-query.js +24 -15
- package/dist/utils/calculate-field-depth.d.ts +33 -0
- package/dist/utils/calculate-field-depth.js +75 -0
- package/dist/utils/get-default-value.js +3 -13
- package/dist/utils/get-graphql-type.js +4 -3
- package/dist/utils/get-local-type.d.ts +6 -3
- package/dist/utils/get-permissions.js +3 -4
- package/dist/utils/get-schema.js +1 -2
- package/dist/utils/get-string-byte-size.d.ts +4 -0
- package/dist/utils/get-string-byte-size.js +10 -0
- package/dist/utils/jwt.js +5 -1
- package/dist/utils/sanitize-query.js +4 -5
- package/dist/utils/validate-query.js +50 -0
- package/dist/webhooks.js +5 -1
- package/package.json +74 -73
- package/dist/utils/operation-options.d.ts +0 -3
- package/dist/utils/operation-options.js +0 -45
- package/dist/utils/parse-json.d.ts +0 -5
- package/dist/utils/parse-json.js +0 -19
|
@@ -23,12 +23,14 @@ class MailService {
|
|
|
23
23
|
this.accountability = opts.accountability || null;
|
|
24
24
|
this.knex = (opts === null || opts === void 0 ? void 0 : opts.knex) || (0, database_1.default)();
|
|
25
25
|
this.mailer = (0, mailer_1.default)();
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
26
|
+
if (env_1.default.EMAIL_VERIFY_SETUP) {
|
|
27
|
+
this.mailer.verify((error) => {
|
|
28
|
+
if (error) {
|
|
29
|
+
logger_1.default.warn(`Email connection failed:`);
|
|
30
|
+
logger_1.default.warn(error);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
32
34
|
}
|
|
33
35
|
async send(options) {
|
|
34
36
|
const { template, ...emailOptions } = options;
|
|
@@ -6,6 +6,7 @@ export declare class OperationsService extends ItemsService<OperationRaw> {
|
|
|
6
6
|
createOne(data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
|
|
7
7
|
createMany(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
8
8
|
updateOne(key: PrimaryKey, data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
|
|
9
|
+
updateBatch(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
9
10
|
updateMany(keys: PrimaryKey[], data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
10
11
|
deleteOne(key: PrimaryKey, opts?: MutationOptions): Promise<PrimaryKey>;
|
|
11
12
|
deleteMany(keys: PrimaryKey[], opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
@@ -25,6 +25,12 @@ class OperationsService extends items_1.ItemsService {
|
|
|
25
25
|
await flowManager.reload();
|
|
26
26
|
return result;
|
|
27
27
|
}
|
|
28
|
+
async updateBatch(data, opts) {
|
|
29
|
+
const flowManager = (0, flows_1.getFlowManager)();
|
|
30
|
+
const result = await super.updateBatch(data, opts);
|
|
31
|
+
await flowManager.reload();
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
28
34
|
async updateMany(keys, data, opts) {
|
|
29
35
|
const flowManager = (0, flows_1.getFlowManager)();
|
|
30
36
|
const result = await super.updateMany(keys, data, opts);
|
package/dist/services/payload.js
CHANGED
|
@@ -15,7 +15,6 @@ const database_1 = __importDefault(require("../database"));
|
|
|
15
15
|
const helpers_1 = require("../database/helpers");
|
|
16
16
|
const exceptions_1 = require("../exceptions");
|
|
17
17
|
const generate_hash_1 = require("../utils/generate-hash");
|
|
18
|
-
const parse_json_1 = require("../utils/parse-json");
|
|
19
18
|
const items_1 = require("./items");
|
|
20
19
|
/**
|
|
21
20
|
* Process a given payload for a collection to ensure the special fields (hash, uuid, date etc) are
|
|
@@ -56,7 +55,7 @@ class PayloadService {
|
|
|
56
55
|
if (action === 'read') {
|
|
57
56
|
if (typeof value === 'string') {
|
|
58
57
|
try {
|
|
59
|
-
return (0,
|
|
58
|
+
return (0, utils_1.parseJSON)(value);
|
|
60
59
|
}
|
|
61
60
|
catch {
|
|
62
61
|
return value;
|
|
@@ -197,7 +196,7 @@ class PayloadService {
|
|
|
197
196
|
processGeometries(payloads, action) {
|
|
198
197
|
const process = action == 'read'
|
|
199
198
|
? (value) => (typeof value === 'string' ? (0, wellknown_1.parse)(value) : value)
|
|
200
|
-
: (value) => this.helpers.st.fromGeoJSON(typeof value == 'string' ? (0,
|
|
199
|
+
: (value) => this.helpers.st.fromGeoJSON(typeof value == 'string' ? (0, utils_1.parseJSON)(value) : value);
|
|
201
200
|
const fieldsInCollection = Object.entries(this.schema.collections[this.collection].fields);
|
|
202
201
|
const geometryColumns = fieldsInCollection.filter(([_, field]) => field.type.startsWith('geometry'));
|
|
203
202
|
for (const [name] of geometryColumns) {
|
|
@@ -12,6 +12,7 @@ export declare class PermissionsService extends ItemsService {
|
|
|
12
12
|
createMany(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
13
13
|
updateByQuery(query: Query, data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
14
14
|
updateOne(key: PrimaryKey, data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
|
|
15
|
+
updateBatch(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
15
16
|
updateMany(keys: PrimaryKey[], data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
16
17
|
upsertOne(payload: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
|
|
17
18
|
upsertMany(payloads: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
@@ -70,6 +70,11 @@ class PermissionsService extends items_1.ItemsService {
|
|
|
70
70
|
await (0, cache_1.clearSystemCache)();
|
|
71
71
|
return res;
|
|
72
72
|
}
|
|
73
|
+
async updateBatch(data, opts) {
|
|
74
|
+
const res = await super.updateBatch(data, opts);
|
|
75
|
+
await (0, cache_1.clearSystemCache)();
|
|
76
|
+
return res;
|
|
77
|
+
}
|
|
73
78
|
async updateMany(keys, data, opts) {
|
|
74
79
|
const res = await super.updateMany(keys, data, opts);
|
|
75
80
|
await (0, cache_1.clearSystemCache)();
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
3
|
if (k2 === undefined) k2 = k;
|
|
4
|
-
Object.
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
5
9
|
}) : (function(o, m, k, k2) {
|
|
6
10
|
if (k2 === undefined) k2 = k;
|
|
7
11
|
o[k2] = m[k];
|
package/dist/services/roles.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export declare class RolesService extends ItemsService {
|
|
|
6
6
|
private checkForOtherAdminRoles;
|
|
7
7
|
private checkForOtherAdminUsers;
|
|
8
8
|
updateOne(key: PrimaryKey, data: Record<string, any>, opts?: MutationOptions): Promise<PrimaryKey>;
|
|
9
|
+
updateBatch(data: Record<string, any>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
9
10
|
updateMany(keys: PrimaryKey[], data: Record<string, any>, opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
10
11
|
deleteOne(key: PrimaryKey): Promise<PrimaryKey>;
|
|
11
12
|
deleteMany(keys: PrimaryKey[]): Promise<PrimaryKey[]>;
|
package/dist/services/roles.js
CHANGED
|
@@ -64,6 +64,15 @@ class RolesService extends items_1.ItemsService {
|
|
|
64
64
|
}
|
|
65
65
|
return super.updateOne(key, data, opts);
|
|
66
66
|
}
|
|
67
|
+
async updateBatch(data, opts) {
|
|
68
|
+
const primaryKeyField = this.schema.collections[this.collection].primary;
|
|
69
|
+
const keys = data.map((item) => item[primaryKeyField]);
|
|
70
|
+
const setsToNoAdmin = data.some((item) => item.admin_access === false);
|
|
71
|
+
if (setsToNoAdmin) {
|
|
72
|
+
await this.checkForOtherAdminRoles(keys);
|
|
73
|
+
}
|
|
74
|
+
return super.updateBatch(data, opts);
|
|
75
|
+
}
|
|
67
76
|
async updateMany(keys, data, opts) {
|
|
68
77
|
if ('admin_access' in data && data.admin_access === false) {
|
|
69
78
|
await this.checkForOtherAdminRoles(keys);
|
package/dist/services/server.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
3
|
if (k2 === undefined) k2 = k;
|
|
4
|
-
Object.
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
5
9
|
}) : (function(o, m, k, k2) {
|
|
6
10
|
if (k2 === undefined) k2 = k;
|
|
7
11
|
o[k2] = m[k];
|
package/dist/services/users.d.ts
CHANGED
|
@@ -38,6 +38,7 @@ export declare class UsersService extends ItemsService {
|
|
|
38
38
|
* Update a single user by primary key
|
|
39
39
|
*/
|
|
40
40
|
updateOne(key: PrimaryKey, data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
|
|
41
|
+
updateBatch(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
41
42
|
/**
|
|
42
43
|
* Update many users by primary key
|
|
43
44
|
*/
|
package/dist/services/users.js
CHANGED
|
@@ -153,6 +153,23 @@ class UsersService extends items_1.ItemsService {
|
|
|
153
153
|
await this.updateMany([key], data, opts);
|
|
154
154
|
return key;
|
|
155
155
|
}
|
|
156
|
+
async updateBatch(data, opts) {
|
|
157
|
+
const primaryKeyField = this.schema.collections[this.collection].primary;
|
|
158
|
+
const keys = [];
|
|
159
|
+
await this.knex.transaction(async (trx) => {
|
|
160
|
+
const service = new UsersService({
|
|
161
|
+
accountability: this.accountability,
|
|
162
|
+
knex: trx,
|
|
163
|
+
schema: this.schema,
|
|
164
|
+
});
|
|
165
|
+
for (const item of data) {
|
|
166
|
+
if (!item[primaryKeyField])
|
|
167
|
+
throw new exceptions_2.InvalidPayloadException(`User in update misses primary key.`);
|
|
168
|
+
keys.push(await service.updateOne(item[primaryKeyField], item, opts));
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
return keys;
|
|
172
|
+
}
|
|
156
173
|
/**
|
|
157
174
|
* Update many users by primary key
|
|
158
175
|
*/
|
package/dist/types/index.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
3
|
if (k2 === undefined) k2 = k;
|
|
4
|
-
Object.
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
5
9
|
}) : (function(o, m, k, k2) {
|
|
6
10
|
if (k2 === undefined) k2 = k;
|
|
7
11
|
o[k2] = m[k];
|
|
@@ -211,7 +211,15 @@ function applyFilter(knex, schema, rootQuery, rootFilter, collection, subQuery =
|
|
|
211
211
|
const columnName = (0, get_column_path_1.getColumnPath)({ path: filterPath, collection, relations, aliasMap });
|
|
212
212
|
if (!columnName)
|
|
213
213
|
continue;
|
|
214
|
-
|
|
214
|
+
if (relation === null || relation === void 0 ? void 0 : relation.related_collection) {
|
|
215
|
+
applyFilterToQuery(columnName, filterOperator, filterValue, logical, relation.related_collection); // m2o
|
|
216
|
+
}
|
|
217
|
+
else if (filterPath[0].includes(':')) {
|
|
218
|
+
applyFilterToQuery(columnName, filterOperator, filterValue, logical, filterPath[0].split(':')[1]); // a2o
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
applyFilterToQuery(columnName, filterOperator, filterValue, logical);
|
|
222
|
+
}
|
|
215
223
|
}
|
|
216
224
|
else {
|
|
217
225
|
applyFilterToQuery(`${collection}.${filterPath[0]}`, filterOperator, filterValue, logical);
|
|
@@ -245,7 +253,7 @@ function applyFilter(knex, schema, rootQuery, rootFilter, collection, subQuery =
|
|
|
245
253
|
}
|
|
246
254
|
}
|
|
247
255
|
}
|
|
248
|
-
function applyFilterToQuery(key, operator, compareValue, logical = 'and') {
|
|
256
|
+
function applyFilterToQuery(key, operator, compareValue, logical = 'and', originalCollectionName) {
|
|
249
257
|
const [table, column] = key.split('.');
|
|
250
258
|
// Is processed through Knex.Raw, so should be safe to string-inject into these where queries
|
|
251
259
|
const selectionRaw = (0, get_column_1.getColumn)(knex, table, column, false, schema);
|
|
@@ -268,6 +276,17 @@ function applyFilter(knex, schema, rootQuery, rootFilter, collection, subQuery =
|
|
|
268
276
|
query.where(key, '!=', '');
|
|
269
277
|
});
|
|
270
278
|
}
|
|
279
|
+
// The following fields however, require a value to be run. If no value is passed, we
|
|
280
|
+
// ignore them. This allows easier use in GraphQL, where you wouldn't be able to
|
|
281
|
+
// conditionally build out your filter structure (#4471)
|
|
282
|
+
if (compareValue === undefined)
|
|
283
|
+
return;
|
|
284
|
+
if (Array.isArray(compareValue)) {
|
|
285
|
+
// Tip: when using a `[Type]` type in GraphQL, but don't provide the variable, it'll be
|
|
286
|
+
// reported as [undefined].
|
|
287
|
+
// We need to remove any undefined values, as they are useless
|
|
288
|
+
compareValue = compareValue.filter((val) => val !== undefined);
|
|
289
|
+
}
|
|
271
290
|
// Cast filter value (compareValue) based on function used
|
|
272
291
|
if (column.includes('(') && column.includes(')')) {
|
|
273
292
|
const functionName = column.split('(')[0];
|
|
@@ -278,8 +297,9 @@ function applyFilter(knex, schema, rootQuery, rootFilter, collection, subQuery =
|
|
|
278
297
|
}
|
|
279
298
|
// Cast filter value (compareValue) based on type of field being filtered against
|
|
280
299
|
const [collection, field] = key.split('.');
|
|
281
|
-
|
|
282
|
-
|
|
300
|
+
const mappedCollection = originalCollectionName || collection;
|
|
301
|
+
if (mappedCollection in schema.collections && field in schema.collections[mappedCollection].fields) {
|
|
302
|
+
const type = schema.collections[mappedCollection].fields[field].type;
|
|
283
303
|
if (['date', 'dateTime', 'time', 'timestamp'].includes(type)) {
|
|
284
304
|
if (Array.isArray(compareValue)) {
|
|
285
305
|
compareValue = compareValue.map((val) => helpers.date.parse(val));
|
|
@@ -297,17 +317,6 @@ function applyFilter(knex, schema, rootQuery, rootFilter, collection, subQuery =
|
|
|
297
317
|
}
|
|
298
318
|
}
|
|
299
319
|
}
|
|
300
|
-
// The following fields however, require a value to be run. If no value is passed, we
|
|
301
|
-
// ignore them. This allows easier use in GraphQL, where you wouldn't be able to
|
|
302
|
-
// conditionally build out your filter structure (#4471)
|
|
303
|
-
if (compareValue === undefined)
|
|
304
|
-
return;
|
|
305
|
-
if (Array.isArray(compareValue)) {
|
|
306
|
-
// Tip: when using a `[Type]` type in GraphQL, but don't provide the variable, it'll be
|
|
307
|
-
// reported as [undefined].
|
|
308
|
-
// We need to remove any undefined values, as they are useless
|
|
309
|
-
compareValue = compareValue.filter((val) => val !== undefined);
|
|
310
|
-
}
|
|
311
320
|
if (operator === '_eq') {
|
|
312
321
|
dbQuery[logical].where(selectionRaw, '=', compareValue);
|
|
313
322
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Calculates the depth of a given JSON structure, not counting any _ prefixed properties
|
|
3
|
+
*
|
|
4
|
+
* Used to calculate the field depth in a filter or deep query structure
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
*
|
|
8
|
+
* ```js
|
|
9
|
+
* const deep = {
|
|
10
|
+
* translations: {
|
|
11
|
+
* _filter: {
|
|
12
|
+
* _and: [
|
|
13
|
+
* {
|
|
14
|
+
* language_id: {
|
|
15
|
+
* name: {
|
|
16
|
+
* _eq: 'English'
|
|
17
|
+
* }
|
|
18
|
+
* }
|
|
19
|
+
* },
|
|
20
|
+
* {
|
|
21
|
+
* status: {
|
|
22
|
+
* _eq: 'Published'
|
|
23
|
+
* }
|
|
24
|
+
* }
|
|
25
|
+
* ]
|
|
26
|
+
* }
|
|
27
|
+
* }
|
|
28
|
+
* };
|
|
29
|
+
*
|
|
30
|
+
* const result = calculateFieldDepth(deep); // => 3
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export declare function calculateFieldDepth(obj?: Record<string, any> | null, dotNotationKeys?: string[]): number;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.calculateFieldDepth = void 0;
|
|
4
|
+
const lodash_1 = require("lodash");
|
|
5
|
+
/**
|
|
6
|
+
* Calculates the depth of a given JSON structure, not counting any _ prefixed properties
|
|
7
|
+
*
|
|
8
|
+
* Used to calculate the field depth in a filter or deep query structure
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
*
|
|
12
|
+
* ```js
|
|
13
|
+
* const deep = {
|
|
14
|
+
* translations: {
|
|
15
|
+
* _filter: {
|
|
16
|
+
* _and: [
|
|
17
|
+
* {
|
|
18
|
+
* language_id: {
|
|
19
|
+
* name: {
|
|
20
|
+
* _eq: 'English'
|
|
21
|
+
* }
|
|
22
|
+
* }
|
|
23
|
+
* },
|
|
24
|
+
* {
|
|
25
|
+
* status: {
|
|
26
|
+
* _eq: 'Published'
|
|
27
|
+
* }
|
|
28
|
+
* }
|
|
29
|
+
* ]
|
|
30
|
+
* }
|
|
31
|
+
* }
|
|
32
|
+
* };
|
|
33
|
+
*
|
|
34
|
+
* const result = calculateFieldDepth(deep); // => 3
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
function calculateFieldDepth(obj, dotNotationKeys = []) {
|
|
38
|
+
if (!obj) {
|
|
39
|
+
return 0;
|
|
40
|
+
}
|
|
41
|
+
let depth = 0;
|
|
42
|
+
const keys = Object.keys(obj);
|
|
43
|
+
for (const key of keys) {
|
|
44
|
+
const nestedValue = obj[key];
|
|
45
|
+
if (dotNotationKeys.includes(key) && nestedValue) {
|
|
46
|
+
let sortDepth = 0;
|
|
47
|
+
for (const sortKey of nestedValue) {
|
|
48
|
+
if (sortKey) {
|
|
49
|
+
sortDepth = Math.max(sortKey.split('.').length, sortDepth);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (sortDepth > depth) {
|
|
53
|
+
depth = sortDepth;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
if (!(0, lodash_1.isPlainObject)(nestedValue) && !(0, lodash_1.isArray)(nestedValue))
|
|
58
|
+
continue;
|
|
59
|
+
let nestedDepth = 0;
|
|
60
|
+
if (Array.isArray(nestedValue)) {
|
|
61
|
+
nestedDepth = Math.max(...nestedValue.map((val) => calculateFieldDepth(val, dotNotationKeys)));
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
nestedDepth = calculateFieldDepth(nestedValue, dotNotationKeys);
|
|
65
|
+
}
|
|
66
|
+
if (key.startsWith('_') === false)
|
|
67
|
+
nestedDepth += 1;
|
|
68
|
+
if (nestedDepth > depth) {
|
|
69
|
+
depth = nestedDepth;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return depth;
|
|
74
|
+
}
|
|
75
|
+
exports.calculateFieldDepth = calculateFieldDepth;
|
|
@@ -3,26 +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 env_1 = __importDefault(require("../env"));
|
|
7
8
|
const logger_1 = __importDefault(require("../logger"));
|
|
8
9
|
const get_local_type_1 = __importDefault(require("./get-local-type"));
|
|
9
|
-
const parse_json_1 = require("./parse-json");
|
|
10
10
|
function getDefaultValue(column) {
|
|
11
11
|
var _a;
|
|
12
12
|
const type = (0, get_local_type_1.default)(column);
|
|
13
|
-
|
|
13
|
+
const defaultValue = (_a = column.default_value) !== null && _a !== void 0 ? _a : null;
|
|
14
14
|
if (defaultValue === null)
|
|
15
15
|
return null;
|
|
16
|
-
if (defaultValue === 'null')
|
|
17
|
-
return null;
|
|
18
|
-
if (defaultValue === 'NULL')
|
|
19
|
-
return null;
|
|
20
|
-
// Check if the default is wrapped in an extra pair of quotes, this happens in SQLite / MariaDB
|
|
21
|
-
if (typeof defaultValue === 'string' &&
|
|
22
|
-
((defaultValue.startsWith(`'`) && defaultValue.endsWith(`'`)) ||
|
|
23
|
-
(defaultValue.startsWith(`"`) && defaultValue.endsWith(`"`)))) {
|
|
24
|
-
defaultValue = defaultValue.slice(1, -1);
|
|
25
|
-
}
|
|
26
16
|
if (defaultValue === '0000-00-00 00:00:00')
|
|
27
17
|
return null;
|
|
28
18
|
switch (type) {
|
|
@@ -60,7 +50,7 @@ function castToObject(value) {
|
|
|
60
50
|
return value;
|
|
61
51
|
if (typeof value === 'string') {
|
|
62
52
|
try {
|
|
63
|
-
return (0,
|
|
53
|
+
return (0, utils_1.parseJSON)(value);
|
|
64
54
|
}
|
|
65
55
|
catch (err) {
|
|
66
56
|
if (env_1.default.NODE_ENV === 'development') {
|
|
@@ -3,7 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.getGraphQLType = void 0;
|
|
4
4
|
const graphql_1 = require("graphql");
|
|
5
5
|
const graphql_compose_1 = require("graphql-compose");
|
|
6
|
-
const
|
|
6
|
+
const date_1 = require("../services/graphql/types/date");
|
|
7
|
+
const geojson_1 = require("../services/graphql/types/geojson");
|
|
7
8
|
function getGraphQLType(localType) {
|
|
8
9
|
switch (localType) {
|
|
9
10
|
case 'boolean':
|
|
@@ -20,11 +21,11 @@ function getGraphQLType(localType) {
|
|
|
20
21
|
case 'json':
|
|
21
22
|
return graphql_compose_1.GraphQLJSON;
|
|
22
23
|
case 'geometry':
|
|
23
|
-
return
|
|
24
|
+
return geojson_1.GraphQLGeoJSON;
|
|
24
25
|
case 'timestamp':
|
|
25
26
|
case 'dateTime':
|
|
26
27
|
case 'date':
|
|
27
|
-
return
|
|
28
|
+
return date_1.GraphQLDate;
|
|
28
29
|
default:
|
|
29
30
|
return graphql_1.GraphQLString;
|
|
30
31
|
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import { SchemaOverview } from '@directus/schema/dist/types/overview';
|
|
2
|
-
import { Column } from 'knex-schema-inspector/dist/types/column';
|
|
3
1
|
import { FieldMeta, Type } from '@directus/shared/types';
|
|
4
|
-
export default function getLocalType(column?:
|
|
2
|
+
export default function getLocalType(column?: {
|
|
3
|
+
data_type: string;
|
|
4
|
+
numeric_precision?: null | number;
|
|
5
|
+
numeric_scale?: null | number;
|
|
6
|
+
max_length?: null | number;
|
|
7
|
+
}, field?: {
|
|
5
8
|
special?: FieldMeta['special'];
|
|
6
9
|
}): Type | 'unknown';
|
|
@@ -15,7 +15,6 @@ const roles_1 = require("../services/roles");
|
|
|
15
15
|
const users_1 = require("../services/users");
|
|
16
16
|
const merge_permissions_1 = require("../utils/merge-permissions");
|
|
17
17
|
const merge_permissions_for_share_1 = require("./merge-permissions-for-share");
|
|
18
|
-
const parse_json_1 = require("./parse-json");
|
|
19
18
|
async function getPermissions(accountability, schema) {
|
|
20
19
|
const database = (0, database_1.default)();
|
|
21
20
|
const { systemCache, cache } = (0, cache_1.getCache)();
|
|
@@ -85,19 +84,19 @@ function parsePermissions(permissions) {
|
|
|
85
84
|
permissions = permissions.map((permissionRaw) => {
|
|
86
85
|
const permission = (0, lodash_1.cloneDeep)(permissionRaw);
|
|
87
86
|
if (permission.permissions && typeof permission.permissions === 'string') {
|
|
88
|
-
permission.permissions = (0,
|
|
87
|
+
permission.permissions = (0, utils_1.parseJSON)(permission.permissions);
|
|
89
88
|
}
|
|
90
89
|
else if (permission.permissions === null) {
|
|
91
90
|
permission.permissions = {};
|
|
92
91
|
}
|
|
93
92
|
if (permission.validation && typeof permission.validation === 'string') {
|
|
94
|
-
permission.validation = (0,
|
|
93
|
+
permission.validation = (0, utils_1.parseJSON)(permission.validation);
|
|
95
94
|
}
|
|
96
95
|
else if (permission.validation === null) {
|
|
97
96
|
permission.validation = {};
|
|
98
97
|
}
|
|
99
98
|
if (permission.presets && typeof permission.presets === 'string') {
|
|
100
|
-
permission.presets = (0,
|
|
99
|
+
permission.presets = (0, utils_1.parseJSON)(permission.presets);
|
|
101
100
|
}
|
|
102
101
|
else if (permission.presets === null) {
|
|
103
102
|
permission.presets = {};
|
package/dist/utils/get-schema.js
CHANGED
|
@@ -17,7 +17,6 @@ const logger_1 = __importDefault(require("../logger"));
|
|
|
17
17
|
const services_1 = require("../services");
|
|
18
18
|
const get_default_value_1 = __importDefault(require("./get-default-value"));
|
|
19
19
|
const get_local_type_1 = __importDefault(require("./get-local-type"));
|
|
20
|
-
const parse_json_1 = require("./parse-json");
|
|
21
20
|
async function getSchema(options) {
|
|
22
21
|
const database = (options === null || options === void 0 ? void 0 : options.database) || (0, database_1.default)();
|
|
23
22
|
const schemaInspector = (0, schema_1.default)(database);
|
|
@@ -120,7 +119,7 @@ async function getDatabaseSchema(database, schemaInspector) {
|
|
|
120
119
|
const type = (existing && (0, get_local_type_1.default)(column, { special })) || 'alias';
|
|
121
120
|
let validation = (_a = field.validation) !== null && _a !== void 0 ? _a : null;
|
|
122
121
|
if (validation && typeof validation === 'string')
|
|
123
|
-
validation = (0,
|
|
122
|
+
validation = (0, utils_1.parseJSON)(validation);
|
|
124
123
|
result.collections[field.collection].fields[field.field] = {
|
|
125
124
|
field: field.field,
|
|
126
125
|
defaultValue: (_b = existing === null || existing === void 0 ? void 0 : existing.defaultValue) !== null && _b !== void 0 ? _b : null,
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.stringByteSize = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Returns the byte size for a given input string
|
|
6
|
+
*/
|
|
7
|
+
function stringByteSize(string) {
|
|
8
|
+
return Buffer.byteLength(string, 'utf-8');
|
|
9
|
+
}
|
|
10
|
+
exports.stringByteSize = stringByteSize;
|
package/dist/utils/jwt.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
3
|
if (k2 === undefined) k2 = k;
|
|
4
|
-
Object.
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
5
9
|
}) : (function(o, m, k, k2) {
|
|
6
10
|
if (k2 === undefined) k2 = k;
|
|
7
11
|
o[k2] = m[k];
|
|
@@ -8,7 +8,6 @@ const utils_1 = require("@directus/shared/utils");
|
|
|
8
8
|
const lodash_1 = require("lodash");
|
|
9
9
|
const logger_1 = __importDefault(require("../logger"));
|
|
10
10
|
const types_1 = require("../types");
|
|
11
|
-
const parse_json_1 = require("./parse-json");
|
|
12
11
|
function sanitizeQuery(rawQuery, accountability) {
|
|
13
12
|
const query = {};
|
|
14
13
|
if (rawQuery.limit !== undefined) {
|
|
@@ -83,7 +82,7 @@ function sanitizeAggregate(rawAggregate) {
|
|
|
83
82
|
let aggregate = rawAggregate;
|
|
84
83
|
if (typeof rawAggregate === 'string') {
|
|
85
84
|
try {
|
|
86
|
-
aggregate = (0,
|
|
85
|
+
aggregate = (0, utils_1.parseJSON)(rawAggregate);
|
|
87
86
|
}
|
|
88
87
|
catch {
|
|
89
88
|
logger_1.default.warn('Invalid value passed for filter query parameter.');
|
|
@@ -101,7 +100,7 @@ function sanitizeFilter(rawFilter, accountability) {
|
|
|
101
100
|
let filters = rawFilter;
|
|
102
101
|
if (typeof rawFilter === 'string') {
|
|
103
102
|
try {
|
|
104
|
-
filters = (0,
|
|
103
|
+
filters = (0, utils_1.parseJSON)(rawFilter);
|
|
105
104
|
}
|
|
106
105
|
catch {
|
|
107
106
|
logger_1.default.warn('Invalid value passed for filter query parameter.');
|
|
@@ -136,7 +135,7 @@ function sanitizeDeep(deep, accountability) {
|
|
|
136
135
|
const result = {};
|
|
137
136
|
if (typeof deep === 'string') {
|
|
138
137
|
try {
|
|
139
|
-
deep = (0,
|
|
138
|
+
deep = (0, utils_1.parseJSON)(deep);
|
|
140
139
|
}
|
|
141
140
|
catch {
|
|
142
141
|
logger_1.default.warn('Invalid value passed for deep query parameter.');
|
|
@@ -170,7 +169,7 @@ function sanitizeAlias(rawAlias) {
|
|
|
170
169
|
let alias = rawAlias;
|
|
171
170
|
if (typeof rawAlias === 'string') {
|
|
172
171
|
try {
|
|
173
|
-
alias = (0,
|
|
172
|
+
alias = (0, utils_1.parseJSON)(rawAlias);
|
|
174
173
|
}
|
|
175
174
|
catch (err) {
|
|
176
175
|
logger_1.default.warn('Invalid value passed for alias query parameter.');
|
|
@@ -8,6 +8,8 @@ const joi_1 = __importDefault(require("joi"));
|
|
|
8
8
|
const lodash_1 = require("lodash");
|
|
9
9
|
const exceptions_1 = require("../exceptions");
|
|
10
10
|
const wellknown_1 = require("wellknown");
|
|
11
|
+
const calculate_field_depth_1 = require("./calculate-field-depth");
|
|
12
|
+
const env_1 = __importDefault(require("../env"));
|
|
11
13
|
const querySchema = joi_1.default.object({
|
|
12
14
|
fields: joi_1.default.array().items(joi_1.default.string()),
|
|
13
15
|
group: joi_1.default.array().items(joi_1.default.string()),
|
|
@@ -31,6 +33,7 @@ function validateQuery(query) {
|
|
|
31
33
|
if (query.alias) {
|
|
32
34
|
validateAlias(query.alias);
|
|
33
35
|
}
|
|
36
|
+
validateRelationalDepth(query);
|
|
34
37
|
if (error) {
|
|
35
38
|
throw new exceptions_1.InvalidQueryException(error.message);
|
|
36
39
|
}
|
|
@@ -153,3 +156,50 @@ function validateAlias(alias) {
|
|
|
153
156
|
}
|
|
154
157
|
}
|
|
155
158
|
}
|
|
159
|
+
function validateRelationalDepth(query) {
|
|
160
|
+
const maxRelationalDepth = Number(env_1.default.MAX_RELATIONAL_DEPTH) > 2 ? Number(env_1.default.MAX_RELATIONAL_DEPTH) : 2;
|
|
161
|
+
// Process the fields in the same way as api/src/utils/get-ast-from-query.ts
|
|
162
|
+
let fields = ['*'];
|
|
163
|
+
if (query.fields) {
|
|
164
|
+
fields = query.fields;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* When using aggregate functions, you can't have any other regular fields
|
|
168
|
+
* selected. This makes sure you never end up in a non-aggregate fields selection error
|
|
169
|
+
*/
|
|
170
|
+
if (Object.keys(query.aggregate || {}).length > 0) {
|
|
171
|
+
fields = [];
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Similarly, when grouping on a specific field, you can't have other non-aggregated fields.
|
|
175
|
+
* The group query will override the fields query
|
|
176
|
+
*/
|
|
177
|
+
if (query.group) {
|
|
178
|
+
fields = query.group;
|
|
179
|
+
}
|
|
180
|
+
fields = (0, lodash_1.uniq)(fields);
|
|
181
|
+
for (const field of fields) {
|
|
182
|
+
if (field.split('.').length > maxRelationalDepth) {
|
|
183
|
+
throw new exceptions_1.InvalidQueryException('Max relational depth exceeded.');
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (query.filter) {
|
|
187
|
+
const filterRelationalDepth = (0, calculate_field_depth_1.calculateFieldDepth)(query.filter);
|
|
188
|
+
if (filterRelationalDepth > maxRelationalDepth) {
|
|
189
|
+
throw new exceptions_1.InvalidQueryException('Max relational depth exceeded.');
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (query.sort) {
|
|
193
|
+
for (const sort of query.sort) {
|
|
194
|
+
if (sort.split('.').length > maxRelationalDepth) {
|
|
195
|
+
throw new exceptions_1.InvalidQueryException('Max relational depth exceeded.');
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
if (query.deep) {
|
|
200
|
+
const deepRelationalDepth = (0, calculate_field_depth_1.calculateFieldDepth)(query.deep, ['_sort']);
|
|
201
|
+
if (deepRelationalDepth > maxRelationalDepth) {
|
|
202
|
+
throw new exceptions_1.InvalidQueryException('Max relational depth exceeded.');
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|