directus 9.7.1 → 9.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__mocks__/cache.d.ts +5 -0
- package/dist/__mocks__/cache.js +7 -0
- package/dist/auth/drivers/ldap.js +10 -11
- package/dist/auth/drivers/oauth2.js +9 -4
- package/dist/auth/drivers/openid.js +7 -4
- package/dist/cache.js +2 -2
- package/dist/cli/commands/schema/apply.js +9 -3
- package/dist/controllers/assets.js +5 -0
- package/dist/controllers/files.d.ts +2 -0
- package/dist/controllers/files.js +13 -5
- package/dist/database/helpers/date/dialects/default.d.ts +3 -0
- package/dist/database/helpers/date/dialects/default.js +7 -0
- package/dist/database/helpers/date/dialects/mssql.d.ts +1 -9
- package/dist/database/helpers/date/dialects/mssql.js +4 -23
- package/dist/database/helpers/date/dialects/mysql.d.ts +2 -9
- package/dist/database/helpers/date/dialects/mysql.js +7 -22
- package/dist/database/helpers/date/dialects/oracle.d.ts +1 -9
- package/dist/database/helpers/date/dialects/oracle.js +7 -23
- package/dist/database/helpers/date/dialects/sqlite.d.ts +1 -9
- package/dist/database/helpers/date/dialects/sqlite.js +8 -24
- package/dist/database/helpers/date/index.d.ts +4 -4
- package/dist/database/helpers/date/index.js +9 -9
- package/dist/database/helpers/date/types.d.ts +3 -9
- package/dist/database/helpers/date/types.js +10 -0
- package/dist/database/helpers/fn/dialects/mssql.d.ts +13 -0
- package/dist/database/helpers/fn/dialects/mssql.js +42 -0
- package/dist/database/helpers/{date/dialects/postgres.d.ts → fn/dialects/mysql.d.ts} +3 -2
- package/dist/database/helpers/fn/dialects/mysql.js +42 -0
- package/dist/database/helpers/fn/dialects/oracle.d.ts +13 -0
- package/dist/database/helpers/fn/dialects/oracle.js +42 -0
- package/dist/database/helpers/fn/dialects/postgres.d.ts +13 -0
- package/dist/database/helpers/{date → fn}/dialects/postgres.js +18 -3
- package/dist/database/helpers/fn/dialects/sqlite.d.ts +13 -0
- package/dist/database/helpers/fn/dialects/sqlite.js +42 -0
- package/dist/database/helpers/fn/index.d.ts +7 -0
- package/dist/database/helpers/fn/index.js +17 -0
- package/dist/database/helpers/fn/types.d.ts +18 -0
- package/dist/database/helpers/fn/types.js +27 -0
- package/dist/database/helpers/index.d.ts +4 -1
- package/dist/database/helpers/index.js +7 -1
- package/dist/database/migrations/20220308A-add-bookmark-icon-and-color.d.ts +3 -0
- package/dist/database/migrations/20220308A-add-bookmark-icon-and-color.js +17 -0
- package/dist/database/migrations/20220322A-rename-field-typecast-flags.js +6 -2
- package/dist/database/migrations/20220323A-add-field-validation.d.ts +3 -0
- package/dist/database/migrations/20220323A-add-field-validation.js +17 -0
- package/dist/database/migrations/20220325A-fix-typecast-flags.d.ts +3 -0
- package/dist/database/migrations/20220325A-fix-typecast-flags.js +49 -0
- package/dist/database/migrations/20220325B-add-default-language.d.ts +3 -0
- package/dist/database/migrations/20220325B-add-default-language.js +28 -0
- package/dist/database/migrations/20220402A-remove-default-value-panel-icon.d.ts +3 -0
- package/dist/database/migrations/20220402A-remove-default-value-panel-icon.js +22 -0
- package/dist/database/run-ast.js +7 -5
- package/dist/database/system-data/fields/activity.yaml +4 -4
- package/dist/database/system-data/fields/collections.yaml +1 -1
- package/dist/database/system-data/fields/fields.yaml +9 -0
- package/dist/database/system-data/fields/presets.yaml +14 -0
- package/dist/database/system-data/fields/settings.yaml +12 -1
- package/dist/database/system-data/fields/users.yaml +3 -0
- package/dist/env.js +5 -3
- package/dist/exceptions/index.d.ts +1 -0
- package/dist/exceptions/index.js +1 -0
- package/dist/exceptions/token-expired.d.ts +4 -0
- package/dist/exceptions/token-expired.js +10 -0
- package/dist/logger.js +2 -1
- package/dist/middleware/cache.js +10 -0
- package/dist/services/activity.js +4 -1
- package/dist/services/authorization.d.ts +1 -1
- package/dist/services/authorization.js +174 -48
- package/dist/services/collections.d.ts +2 -0
- package/dist/services/collections.js +232 -198
- package/dist/services/fields.js +210 -174
- package/dist/services/files.d.ts +5 -1
- package/dist/services/files.js +59 -40
- package/dist/services/graphql.d.ts +2 -3
- package/dist/services/graphql.js +53 -10
- package/dist/services/items.js +5 -3
- package/dist/services/payload.d.ts +2 -1
- package/dist/services/payload.js +28 -21
- package/dist/services/relations.js +93 -81
- package/dist/services/server.js +1 -0
- package/dist/services/shares.js +2 -1
- package/dist/services/specifications.js +1 -3
- package/dist/services/users.js +7 -2
- package/dist/types/files.d.ts +8 -0
- package/dist/utils/apply-query.js +38 -10
- package/dist/utils/apply-snapshot.d.ts +3 -1
- package/dist/utils/apply-snapshot.js +34 -5
- package/dist/utils/get-ast-from-query.js +15 -3
- package/dist/utils/get-column.d.ts +6 -5
- package/dist/utils/get-column.js +16 -8
- package/dist/utils/get-graphql-type.js +1 -0
- package/dist/utils/get-local-type.js +5 -0
- package/dist/utils/get-schema.d.ts +1 -1
- package/dist/utils/get-schema.js +18 -10
- package/dist/utils/jwt.js +1 -1
- package/dist/utils/reduce-schema.js +20 -12
- package/dist/utils/track.js +3 -2
- package/dist/utils/url.d.ts +1 -1
- package/dist/utils/url.js +1 -1
- package/dist/utils/validate-query.js +19 -15
- package/dist/utils/validate-storage.js +3 -1
- package/example.env +4 -0
- package/package.json +14 -13
package/dist/services/graphql.js
CHANGED
|
@@ -5,20 +5,22 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.GraphQLService = exports.GraphQLDate = exports.GraphQLGeoJSON = void 0;
|
|
7
7
|
const argon2_1 = __importDefault(require("argon2"));
|
|
8
|
-
const validate_query_1 = require("../utils/validate-query");
|
|
9
8
|
const graphql_1 = require("graphql");
|
|
10
9
|
const graphql_compose_1 = require("graphql-compose");
|
|
11
10
|
const lodash_1 = require("lodash");
|
|
12
11
|
const ms_1 = __importDefault(require("ms"));
|
|
13
12
|
const cache_1 = require("../cache");
|
|
13
|
+
const constants_1 = require("../constants");
|
|
14
14
|
const database_1 = __importDefault(require("../database"));
|
|
15
15
|
const env_1 = __importDefault(require("../env"));
|
|
16
16
|
const exceptions_1 = require("../exceptions");
|
|
17
17
|
const extensions_1 = require("../extensions");
|
|
18
18
|
const types_1 = require("../types");
|
|
19
|
+
const generate_hash_1 = require("../utils/generate-hash");
|
|
19
20
|
const get_graphql_type_1 = require("../utils/get-graphql-type");
|
|
20
21
|
const reduce_schema_1 = require("../utils/reduce-schema");
|
|
21
22
|
const sanitize_query_1 = require("../utils/sanitize-query");
|
|
23
|
+
const validate_query_1 = require("../utils/validate-query");
|
|
22
24
|
const activity_1 = require("./activity");
|
|
23
25
|
const authentication_1 = require("./authentication");
|
|
24
26
|
const collections_1 = require("./collections");
|
|
@@ -26,9 +28,9 @@ const fields_1 = require("./fields");
|
|
|
26
28
|
const files_1 = require("./files");
|
|
27
29
|
const folders_1 = require("./folders");
|
|
28
30
|
const items_1 = require("./items");
|
|
31
|
+
const notifications_1 = require("./notifications");
|
|
29
32
|
const permissions_1 = require("./permissions");
|
|
30
33
|
const presets_1 = require("./presets");
|
|
31
|
-
const notifications_1 = require("./notifications");
|
|
32
34
|
const relations_1 = require("./relations");
|
|
33
35
|
const revisions_1 = require("./revisions");
|
|
34
36
|
const roles_1 = require("./roles");
|
|
@@ -40,8 +42,6 @@ const tfa_1 = require("./tfa");
|
|
|
40
42
|
const users_1 = require("./users");
|
|
41
43
|
const utils_1 = require("./utils");
|
|
42
44
|
const webhooks_1 = require("./webhooks");
|
|
43
|
-
const generate_hash_1 = require("../utils/generate-hash");
|
|
44
|
-
const constants_1 = require("../constants");
|
|
45
45
|
const GraphQLVoid = new graphql_1.GraphQLScalarType({
|
|
46
46
|
name: 'Void',
|
|
47
47
|
description: 'Represents NULL values',
|
|
@@ -235,6 +235,14 @@ class GraphQLService {
|
|
|
235
235
|
function getTypes(action) {
|
|
236
236
|
var _a, _b, _c, _d, _e;
|
|
237
237
|
const CollectionTypes = {};
|
|
238
|
+
const CountFunctions = schemaComposer.createObjectTC({
|
|
239
|
+
name: 'count_functions',
|
|
240
|
+
fields: {
|
|
241
|
+
count: {
|
|
242
|
+
type: graphql_1.GraphQLInt,
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
});
|
|
238
246
|
const DateFunctions = schemaComposer.createObjectTC({
|
|
239
247
|
name: 'date_functions',
|
|
240
248
|
fields: {
|
|
@@ -328,6 +336,15 @@ class GraphQLService {
|
|
|
328
336
|
},
|
|
329
337
|
};
|
|
330
338
|
}
|
|
339
|
+
if (field.type === 'json' || field.type === 'alias') {
|
|
340
|
+
acc[`${field.field}_func`] = {
|
|
341
|
+
type: CountFunctions,
|
|
342
|
+
resolve: (obj) => {
|
|
343
|
+
const funcFields = Object.keys(CountFunctions.getFields()).map((key) => `${field.field}_${key}`);
|
|
344
|
+
return (0, lodash_1.mapKeys)((0, lodash_1.pick)(obj, funcFields), (_value, key) => key.substring(field.field.length + 1));
|
|
345
|
+
},
|
|
346
|
+
};
|
|
347
|
+
}
|
|
331
348
|
return acc;
|
|
332
349
|
}, {}),
|
|
333
350
|
});
|
|
@@ -550,6 +567,14 @@ class GraphQLService {
|
|
|
550
567
|
},
|
|
551
568
|
},
|
|
552
569
|
});
|
|
570
|
+
const CountFunctionFilterOperators = schemaComposer.createInputTC({
|
|
571
|
+
name: 'count_function_filter_operators',
|
|
572
|
+
fields: {
|
|
573
|
+
count: {
|
|
574
|
+
type: NumberFilterOperators,
|
|
575
|
+
},
|
|
576
|
+
},
|
|
577
|
+
});
|
|
553
578
|
const DateFunctionFilterOperators = schemaComposer.createInputTC({
|
|
554
579
|
name: 'date_function_filter_operators',
|
|
555
580
|
fields: {
|
|
@@ -634,6 +659,11 @@ class GraphQLService {
|
|
|
634
659
|
type: DateTimeFunctionFilterOperators,
|
|
635
660
|
};
|
|
636
661
|
}
|
|
662
|
+
if (field.type === 'json' || field.type === 'alias') {
|
|
663
|
+
acc[`${field.field}_func`] = {
|
|
664
|
+
type: CountFunctionFilterOperators,
|
|
665
|
+
};
|
|
666
|
+
}
|
|
637
667
|
return acc;
|
|
638
668
|
}, {}),
|
|
639
669
|
});
|
|
@@ -1145,6 +1175,7 @@ class GraphQLService {
|
|
|
1145
1175
|
continue;
|
|
1146
1176
|
selection = selection;
|
|
1147
1177
|
let current;
|
|
1178
|
+
let currentAlias = null;
|
|
1148
1179
|
// Union type (Many-to-Any)
|
|
1149
1180
|
if (selection.kind === 'InlineFragment') {
|
|
1150
1181
|
if (selection.typeCondition.name.value.startsWith('__'))
|
|
@@ -1157,8 +1188,20 @@ class GraphQLService {
|
|
|
1157
1188
|
if (selection.name.value.startsWith('__'))
|
|
1158
1189
|
continue;
|
|
1159
1190
|
current = selection.name.value;
|
|
1191
|
+
if (selection.alias) {
|
|
1192
|
+
currentAlias = selection.alias.value;
|
|
1193
|
+
}
|
|
1160
1194
|
if (parent) {
|
|
1161
1195
|
current = `${parent}.${current}`;
|
|
1196
|
+
if (currentAlias) {
|
|
1197
|
+
currentAlias = `${parent}.${currentAlias}`;
|
|
1198
|
+
// add nested aliases into deep query
|
|
1199
|
+
if (selection.selectionSet) {
|
|
1200
|
+
if (!query.deep)
|
|
1201
|
+
query.deep = {};
|
|
1202
|
+
(0, lodash_1.set)(query.deep, parent, (0, lodash_1.merge)((0, lodash_1.get)(query.deep, parent), { _alias: { [selection.alias.value]: selection.name.value } }));
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1162
1205
|
}
|
|
1163
1206
|
}
|
|
1164
1207
|
if (selection.selectionSet) {
|
|
@@ -1173,7 +1216,7 @@ class GraphQLService {
|
|
|
1173
1216
|
}
|
|
1174
1217
|
}
|
|
1175
1218
|
else {
|
|
1176
|
-
children = parseFields(selection.selectionSet.selections, current);
|
|
1219
|
+
children = parseFields(selection.selectionSet.selections, currentAlias !== null && currentAlias !== void 0 ? currentAlias : current);
|
|
1177
1220
|
}
|
|
1178
1221
|
fields.push(...children);
|
|
1179
1222
|
}
|
|
@@ -1308,7 +1351,7 @@ class GraphQLService {
|
|
|
1308
1351
|
return result;
|
|
1309
1352
|
}
|
|
1310
1353
|
injectSystemResolvers(schemaComposer, { CreateCollectionTypes, ReadCollectionTypes, UpdateCollectionTypes, DeleteCollectionTypes, }, schema) {
|
|
1311
|
-
var _a, _b, _c, _d, _e;
|
|
1354
|
+
var _a, _b, _c, _d, _e, _f;
|
|
1312
1355
|
const AuthTokens = schemaComposer.createObjectTC({
|
|
1313
1356
|
name: 'auth_tokens',
|
|
1314
1357
|
fields: {
|
|
@@ -2131,7 +2174,7 @@ class GraphQLService {
|
|
|
2131
2174
|
},
|
|
2132
2175
|
});
|
|
2133
2176
|
}
|
|
2134
|
-
if ('directus_users' in schema.update.collections) {
|
|
2177
|
+
if ('directus_users' in schema.update.collections && ((_c = this.accountability) === null || _c === void 0 ? void 0 : _c.user)) {
|
|
2135
2178
|
schemaComposer.Mutation.addFields({
|
|
2136
2179
|
update_users_me: {
|
|
2137
2180
|
type: ReadCollectionTypes['directus_users'],
|
|
@@ -2160,7 +2203,7 @@ class GraphQLService {
|
|
|
2160
2203
|
if ('directus_activity' in schema.create.collections) {
|
|
2161
2204
|
schemaComposer.Mutation.addFields({
|
|
2162
2205
|
create_comment: {
|
|
2163
|
-
type: (
|
|
2206
|
+
type: (_d = ReadCollectionTypes['directus_activity']) !== null && _d !== void 0 ? _d : graphql_1.GraphQLBoolean,
|
|
2164
2207
|
args: {
|
|
2165
2208
|
collection: (0, graphql_1.GraphQLNonNull)(graphql_1.GraphQLString),
|
|
2166
2209
|
item: (0, graphql_1.GraphQLNonNull)(graphql_1.GraphQLID),
|
|
@@ -2192,7 +2235,7 @@ class GraphQLService {
|
|
|
2192
2235
|
if ('directus_activity' in schema.update.collections) {
|
|
2193
2236
|
schemaComposer.Mutation.addFields({
|
|
2194
2237
|
update_comment: {
|
|
2195
|
-
type: (
|
|
2238
|
+
type: (_e = ReadCollectionTypes['directus_activity']) !== null && _e !== void 0 ? _e : graphql_1.GraphQLBoolean,
|
|
2196
2239
|
args: {
|
|
2197
2240
|
id: (0, graphql_1.GraphQLNonNull)(graphql_1.GraphQLID),
|
|
2198
2241
|
comment: (0, graphql_1.GraphQLNonNull)(graphql_1.GraphQLString),
|
|
@@ -2235,7 +2278,7 @@ class GraphQLService {
|
|
|
2235
2278
|
if ('directus_files' in schema.create.collections) {
|
|
2236
2279
|
schemaComposer.Mutation.addFields({
|
|
2237
2280
|
import_file: {
|
|
2238
|
-
type: (
|
|
2281
|
+
type: (_f = ReadCollectionTypes['directus_files']) !== null && _f !== void 0 ? _f : graphql_1.GraphQLBoolean,
|
|
2239
2282
|
args: {
|
|
2240
2283
|
url: (0, graphql_1.GraphQLNonNull)(graphql_1.GraphQLString),
|
|
2241
2284
|
data: (0, graphql_compose_1.toInputObjectType)(CreateCollectionTypes['directus_files']).setTypeName('create_directus_files_input'),
|
package/dist/services/items.js
CHANGED
|
@@ -354,13 +354,14 @@ class ItemsService {
|
|
|
354
354
|
knex: trx,
|
|
355
355
|
schema: this.schema,
|
|
356
356
|
});
|
|
357
|
-
const
|
|
357
|
+
const revisions = (await Promise.all(activity.map(async (activity, index) => ({
|
|
358
358
|
activity: activity,
|
|
359
359
|
collection: this.collection,
|
|
360
360
|
item: keys[index],
|
|
361
361
|
data: snapshots && Array.isArray(snapshots) ? JSON.stringify(snapshots[index]) : JSON.stringify(snapshots),
|
|
362
362
|
delta: await payloadService.prepareDelta(payloadWithTypeCasting),
|
|
363
|
-
}))));
|
|
363
|
+
})))).filter((revision) => revision.delta);
|
|
364
|
+
const revisionIDs = await revisionsService.createMany(revisions);
|
|
364
365
|
for (let i = 0; i < revisionIDs.length; i++) {
|
|
365
366
|
const revisionID = revisionIDs[i];
|
|
366
367
|
if (opts === null || opts === void 0 ? void 0 : opts.onRevisionCreate) {
|
|
@@ -529,7 +530,8 @@ class ItemsService {
|
|
|
529
530
|
defaults[name] = null;
|
|
530
531
|
continue;
|
|
531
532
|
}
|
|
532
|
-
|
|
533
|
+
if (field.defaultValue)
|
|
534
|
+
defaults[name] = field.defaultValue;
|
|
533
535
|
}
|
|
534
536
|
return defaults;
|
|
535
537
|
}
|
|
@@ -10,6 +10,7 @@ declare type Transformers = {
|
|
|
10
10
|
payload: Partial<Item>;
|
|
11
11
|
accountability: Accountability | null;
|
|
12
12
|
specials: string[];
|
|
13
|
+
helpers: Helpers;
|
|
13
14
|
}) => Promise<any>;
|
|
14
15
|
};
|
|
15
16
|
/**
|
|
@@ -64,6 +65,6 @@ export declare class PayloadService {
|
|
|
64
65
|
* Transforms the input partial payload to match the output structure, to have consistency
|
|
65
66
|
* between delta and data
|
|
66
67
|
*/
|
|
67
|
-
prepareDelta(data: Partial<Item>): Promise<string>;
|
|
68
|
+
prepareDelta(data: Partial<Item>): Promise<string | null>;
|
|
68
69
|
}
|
|
69
70
|
export {};
|
package/dist/services/payload.js
CHANGED
|
@@ -89,14 +89,14 @@ class PayloadService {
|
|
|
89
89
|
return (accountability === null || accountability === void 0 ? void 0 : accountability.role) || null;
|
|
90
90
|
return value;
|
|
91
91
|
},
|
|
92
|
-
async 'date-created'({ action, value }) {
|
|
92
|
+
async 'date-created'({ action, value, helpers }) {
|
|
93
93
|
if (action === 'create')
|
|
94
|
-
return new Date();
|
|
94
|
+
return new Date(helpers.date.writeTimestamp(new Date().toISOString()));
|
|
95
95
|
return value;
|
|
96
96
|
},
|
|
97
|
-
async 'date-updated'({ action, value }) {
|
|
97
|
+
async 'date-updated'({ action, value, helpers }) {
|
|
98
98
|
if (action === 'update')
|
|
99
|
-
return new Date();
|
|
99
|
+
return new Date(helpers.date.writeTimestamp(new Date().toISOString()));
|
|
100
100
|
return value;
|
|
101
101
|
},
|
|
102
102
|
async 'cast-csv'({ action, value }) {
|
|
@@ -181,6 +181,7 @@ class PayloadService {
|
|
|
181
181
|
payload,
|
|
182
182
|
accountability,
|
|
183
183
|
specials: fieldSpecials,
|
|
184
|
+
helpers: this.helpers,
|
|
184
185
|
});
|
|
185
186
|
}
|
|
186
187
|
}
|
|
@@ -233,21 +234,23 @@ class PayloadService {
|
|
|
233
234
|
value = new Date(value);
|
|
234
235
|
}
|
|
235
236
|
if (dateColumn.type === 'timestamp') {
|
|
236
|
-
const newValue = value.toISOString();
|
|
237
|
+
const newValue = this.helpers.date.readTimestampString(value.toISOString());
|
|
237
238
|
payload[name] = newValue;
|
|
238
239
|
}
|
|
239
240
|
if (dateColumn.type === 'dateTime') {
|
|
240
|
-
const year = String(value.
|
|
241
|
-
const month = String(value.
|
|
242
|
-
const
|
|
243
|
-
const hours = String(value.
|
|
244
|
-
const minutes = String(value.
|
|
245
|
-
const seconds = String(value.
|
|
246
|
-
const newValue = `${year}-${month}-${
|
|
241
|
+
const year = String(value.getFullYear());
|
|
242
|
+
const month = String(value.getMonth() + 1).padStart(2, '0');
|
|
243
|
+
const day = String(value.getDate()).padStart(2, '0');
|
|
244
|
+
const hours = String(value.getHours()).padStart(2, '0');
|
|
245
|
+
const minutes = String(value.getMinutes()).padStart(2, '0');
|
|
246
|
+
const seconds = String(value.getSeconds()).padStart(2, '0');
|
|
247
|
+
const newValue = `${year}-${month}-${day}T${hours}:${minutes}:${seconds}`;
|
|
247
248
|
payload[name] = newValue;
|
|
248
249
|
}
|
|
249
250
|
if (dateColumn.type === 'date') {
|
|
250
|
-
const
|
|
251
|
+
const year = String(value.getFullYear());
|
|
252
|
+
const month = String(value.getMonth() + 1).padStart(2, '0');
|
|
253
|
+
const day = String(value.getDate()).padStart(2, '0');
|
|
251
254
|
// Strip off the time / timezone information from a date-only value
|
|
252
255
|
const newValue = `${year}-${month}-${day}`;
|
|
253
256
|
payload[name] = newValue;
|
|
@@ -258,16 +261,16 @@ class PayloadService {
|
|
|
258
261
|
if (dateColumn.type === 'date') {
|
|
259
262
|
const [date] = value.split('T');
|
|
260
263
|
const [year, month, day] = date.split('-');
|
|
261
|
-
payload[name] = new Date(
|
|
264
|
+
payload[name] = new Date(Number(year), Number(month) - 1, Number(day));
|
|
262
265
|
}
|
|
263
266
|
if (dateColumn.type === 'dateTime') {
|
|
264
267
|
const [date, time] = value.split('T');
|
|
265
268
|
const [year, month, day] = date.split('-');
|
|
266
269
|
const [hours, minutes, seconds] = time.substring(0, 8).split(':');
|
|
267
|
-
payload[name] = new Date(
|
|
270
|
+
payload[name] = new Date(Number(year), Number(month) - 1, Number(day), Number(hours), Number(minutes), Number(seconds));
|
|
268
271
|
}
|
|
269
272
|
if (dateColumn.type === 'timestamp') {
|
|
270
|
-
const newValue =
|
|
273
|
+
const newValue = this.helpers.date.writeTimestamp(value);
|
|
271
274
|
payload[name] = newValue;
|
|
272
275
|
}
|
|
273
276
|
}
|
|
@@ -429,7 +432,7 @@ class PayloadService {
|
|
|
429
432
|
delete: joi_1.default.array().items(joi_1.default.string(), joi_1.default.number()),
|
|
430
433
|
});
|
|
431
434
|
for (const relation of relationsToProcess) {
|
|
432
|
-
if (!relation.meta
|
|
435
|
+
if (!relation.meta)
|
|
433
436
|
continue;
|
|
434
437
|
const currentPrimaryKeyField = this.schema.collections[relation.related_collection].primary;
|
|
435
438
|
const relatedPrimaryKeyField = this.schema.collections[relation.collection].primary;
|
|
@@ -441,9 +444,11 @@ class PayloadService {
|
|
|
441
444
|
const recordsToUpsert = [];
|
|
442
445
|
const savedPrimaryKeys = [];
|
|
443
446
|
// Nested array of individual items
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
+
const field = payload[relation.meta.one_field];
|
|
448
|
+
if (!field || Array.isArray(field)) {
|
|
449
|
+
const updates = field || []; // treat falsey values as removing all children
|
|
450
|
+
for (let i = 0; i < updates.length; i++) {
|
|
451
|
+
const relatedRecord = updates[i];
|
|
447
452
|
let record = (0, lodash_1.cloneDeep)(relatedRecord);
|
|
448
453
|
if (typeof relatedRecord === 'string' || typeof relatedRecord === 'number') {
|
|
449
454
|
const existingRecord = await this.knex
|
|
@@ -507,7 +512,7 @@ class PayloadService {
|
|
|
507
512
|
}
|
|
508
513
|
// "Updates" object w/ create/update/delete
|
|
509
514
|
else {
|
|
510
|
-
const alterations =
|
|
515
|
+
const alterations = field;
|
|
511
516
|
const { error } = nestedUpdateSchema.validate(alterations);
|
|
512
517
|
if (error)
|
|
513
518
|
throw new exceptions_1.InvalidPayloadException(`Invalid one-to-many update structure: ${error.message}`);
|
|
@@ -573,6 +578,8 @@ class PayloadService {
|
|
|
573
578
|
}
|
|
574
579
|
}
|
|
575
580
|
payload = await this.processValues('read', payload);
|
|
581
|
+
if (Object.keys(payload).length === 0)
|
|
582
|
+
return null;
|
|
576
583
|
return JSON.stringify(payload);
|
|
577
584
|
}
|
|
578
585
|
}
|
|
@@ -146,36 +146,40 @@ class RelationsService {
|
|
|
146
146
|
if (existingRelation) {
|
|
147
147
|
throw new exceptions_1.InvalidPayloadException(`Field "${relation.field}" in collection "${relation.collection}" already has an associated relationship`);
|
|
148
148
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
149
|
+
try {
|
|
150
|
+
const metaRow = {
|
|
151
|
+
...(relation.meta || {}),
|
|
152
|
+
many_collection: relation.collection,
|
|
153
|
+
many_field: relation.field,
|
|
154
|
+
one_collection: relation.related_collection || null,
|
|
155
|
+
};
|
|
156
|
+
await this.knex.transaction(async (trx) => {
|
|
157
|
+
if (relation.related_collection) {
|
|
158
|
+
await trx.schema.alterTable(relation.collection, async (table) => {
|
|
159
|
+
var _a;
|
|
160
|
+
this.alterType(table, relation);
|
|
161
|
+
const constraintName = (0, get_default_index_name_1.getDefaultIndexName)('foreign', relation.collection, relation.field);
|
|
162
|
+
const builder = table
|
|
163
|
+
.foreign(relation.field, constraintName)
|
|
164
|
+
.references(`${relation.related_collection}.${this.schema.collections[relation.related_collection].primary}`);
|
|
165
|
+
if ((_a = relation.schema) === null || _a === void 0 ? void 0 : _a.on_delete) {
|
|
166
|
+
builder.onDelete(relation.schema.on_delete);
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
const relationsItemService = new items_1.ItemsService('directus_relations', {
|
|
171
|
+
knex: trx,
|
|
172
|
+
schema: this.schema,
|
|
173
|
+
// We don't set accountability here. If you have read access to certain fields, you are
|
|
174
|
+
// allowed to extract the relations regardless of permissions to directus_relations. This
|
|
175
|
+
// happens in `filterForbidden` down below
|
|
167
176
|
});
|
|
168
|
-
|
|
169
|
-
const relationsItemService = new items_1.ItemsService('directus_relations', {
|
|
170
|
-
knex: trx,
|
|
171
|
-
schema: this.schema,
|
|
172
|
-
// We don't set accountability here. If you have read access to certain fields, you are
|
|
173
|
-
// allowed to extract the relations regardless of permissions to directus_relations. This
|
|
174
|
-
// happens in `filterForbidden` down below
|
|
177
|
+
await relationsItemService.createOne(metaRow);
|
|
175
178
|
});
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
+
}
|
|
180
|
+
finally {
|
|
181
|
+
await (0, cache_1.clearSystemCache)();
|
|
182
|
+
}
|
|
179
183
|
}
|
|
180
184
|
/**
|
|
181
185
|
* Update an existing foreign key constraint
|
|
@@ -196,47 +200,51 @@ class RelationsService {
|
|
|
196
200
|
if (!existingRelation) {
|
|
197
201
|
throw new exceptions_1.InvalidPayloadException(`Field "${field}" in collection "${collection}" doesn't have a relationship.`);
|
|
198
202
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
203
|
+
try {
|
|
204
|
+
await this.knex.transaction(async (trx) => {
|
|
205
|
+
if (existingRelation.related_collection) {
|
|
206
|
+
await trx.schema.alterTable(collection, async (table) => {
|
|
207
|
+
var _a;
|
|
208
|
+
let constraintName = (0, get_default_index_name_1.getDefaultIndexName)('foreign', collection, field);
|
|
209
|
+
// If the FK already exists in the DB, drop it first
|
|
210
|
+
if (existingRelation === null || existingRelation === void 0 ? void 0 : existingRelation.schema) {
|
|
211
|
+
constraintName = existingRelation.schema.constraint_name || constraintName;
|
|
212
|
+
table.dropForeign(field, constraintName);
|
|
213
|
+
}
|
|
214
|
+
this.alterType(table, relation);
|
|
215
|
+
const builder = table
|
|
216
|
+
.foreign(field, constraintName || undefined)
|
|
217
|
+
.references(`${existingRelation.related_collection}.${this.schema.collections[existingRelation.related_collection].primary}`);
|
|
218
|
+
if ((_a = relation.schema) === null || _a === void 0 ? void 0 : _a.on_delete) {
|
|
219
|
+
builder.onDelete(relation.schema.on_delete);
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
const relationsItemService = new items_1.ItemsService('directus_relations', {
|
|
224
|
+
knex: trx,
|
|
225
|
+
schema: this.schema,
|
|
226
|
+
// We don't set accountability here. If you have read access to certain fields, you are
|
|
227
|
+
// allowed to extract the relations regardless of permissions to directus_relations. This
|
|
228
|
+
// happens in `filterForbidden` down below
|
|
229
|
+
});
|
|
230
|
+
if (relation.meta) {
|
|
231
|
+
if (existingRelation === null || existingRelation === void 0 ? void 0 : existingRelation.meta) {
|
|
232
|
+
await relationsItemService.updateOne(existingRelation.meta.id, relation.meta);
|
|
208
233
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
234
|
+
else {
|
|
235
|
+
await relationsItemService.createOne({
|
|
236
|
+
...(relation.meta || {}),
|
|
237
|
+
many_collection: relation.collection,
|
|
238
|
+
many_field: relation.field,
|
|
239
|
+
one_collection: existingRelation.related_collection || null,
|
|
240
|
+
});
|
|
215
241
|
}
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
|
-
const relationsItemService = new items_1.ItemsService('directus_relations', {
|
|
219
|
-
knex: trx,
|
|
220
|
-
schema: this.schema,
|
|
221
|
-
// We don't set accountability here. If you have read access to certain fields, you are
|
|
222
|
-
// allowed to extract the relations regardless of permissions to directus_relations. This
|
|
223
|
-
// happens in `filterForbidden` down below
|
|
224
|
-
});
|
|
225
|
-
if (relation.meta) {
|
|
226
|
-
if (existingRelation === null || existingRelation === void 0 ? void 0 : existingRelation.meta) {
|
|
227
|
-
await relationsItemService.updateOne(existingRelation.meta.id, relation.meta);
|
|
228
|
-
}
|
|
229
|
-
else {
|
|
230
|
-
await relationsItemService.createOne({
|
|
231
|
-
...(relation.meta || {}),
|
|
232
|
-
many_collection: relation.collection,
|
|
233
|
-
many_field: relation.field,
|
|
234
|
-
one_collection: existingRelation.related_collection || null,
|
|
235
|
-
});
|
|
236
242
|
}
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
finally {
|
|
246
|
+
await (0, cache_1.clearSystemCache)();
|
|
247
|
+
}
|
|
240
248
|
}
|
|
241
249
|
/**
|
|
242
250
|
* Delete an existing relationship
|
|
@@ -255,21 +263,25 @@ class RelationsService {
|
|
|
255
263
|
if (!existingRelation) {
|
|
256
264
|
throw new exceptions_1.InvalidPayloadException(`Field "${field}" in collection "${collection}" doesn't have a relationship.`);
|
|
257
265
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
266
|
+
try {
|
|
267
|
+
await this.knex.transaction(async (trx) => {
|
|
268
|
+
var _a;
|
|
269
|
+
const existingConstraints = await this.schemaInspector.foreignKeys();
|
|
270
|
+
const constraintNames = existingConstraints.map((key) => key.constraint_name);
|
|
271
|
+
if (((_a = existingRelation.schema) === null || _a === void 0 ? void 0 : _a.constraint_name) &&
|
|
272
|
+
constraintNames.includes(existingRelation.schema.constraint_name)) {
|
|
273
|
+
await trx.schema.alterTable(existingRelation.collection, (table) => {
|
|
274
|
+
table.dropForeign(existingRelation.field, existingRelation.schema.constraint_name);
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
if (existingRelation.meta) {
|
|
278
|
+
await trx('directus_relations').delete().where({ many_collection: collection, many_field: field });
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
finally {
|
|
283
|
+
await (0, cache_1.clearSystemCache)();
|
|
284
|
+
}
|
|
273
285
|
}
|
|
274
286
|
/**
|
|
275
287
|
* Whether or not the current user has read access to relations
|
package/dist/services/server.js
CHANGED
package/dist/services/shares.js
CHANGED
|
@@ -16,6 +16,7 @@ const users_1 = require("./users");
|
|
|
16
16
|
const mail_1 = require("./mail");
|
|
17
17
|
const user_name_1 = require("../utils/user-name");
|
|
18
18
|
const md_1 = require("../utils/md");
|
|
19
|
+
const url_1 = require("../utils/url");
|
|
19
20
|
class SharesService extends items_1.ItemsService {
|
|
20
21
|
constructor(options) {
|
|
21
22
|
super('directus_shares', options);
|
|
@@ -116,7 +117,7 @@ Hello!
|
|
|
116
117
|
|
|
117
118
|
${(0, user_name_1.userName)(userInfo)} has invited you to view an item in ${share.collection}.
|
|
118
119
|
|
|
119
|
-
[Open](${env_1.default.PUBLIC_URL
|
|
120
|
+
[Open](${new url_1.Url(env_1.default.PUBLIC_URL).addPath('admin', 'shared', payload.share).toString()})
|
|
120
121
|
`;
|
|
121
122
|
for (const email of payload.emails) {
|
|
122
123
|
await mailService.send({
|
package/dist/services/users.js
CHANGED
|
@@ -140,8 +140,11 @@ class UsersService extends items_1.ItemsService {
|
|
|
140
140
|
* Update many users by primary key
|
|
141
141
|
*/
|
|
142
142
|
async updateMany(keys, data, opts) {
|
|
143
|
+
var _a, _b;
|
|
143
144
|
if (data.role) {
|
|
144
|
-
|
|
145
|
+
// data.role will be an object with id with GraphQL mutations
|
|
146
|
+
const roleId = (_b = (_a = data.role) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : data.role;
|
|
147
|
+
const newRole = await this.knex.select('admin_access').from('directus_roles').where('id', roleId).first();
|
|
145
148
|
if (!(newRole === null || newRole === void 0 ? void 0 : newRole.admin_access)) {
|
|
146
149
|
await this.checkRemainingAdminExistence(keys);
|
|
147
150
|
}
|
|
@@ -264,7 +267,9 @@ class UsersService extends items_1.ItemsService {
|
|
|
264
267
|
});
|
|
265
268
|
const payload = { email, scope: 'password-reset', hash: (0, utils_2.getSimpleHash)('' + user.password) };
|
|
266
269
|
const token = jsonwebtoken_1.default.sign(payload, env_1.default.SECRET, { expiresIn: '1d', issuer: 'directus' });
|
|
267
|
-
const acceptURL = url
|
|
270
|
+
const acceptURL = url
|
|
271
|
+
? new url_1.Url(url).setQuery('token', token).toString()
|
|
272
|
+
: new url_1.Url(env_1.default.PUBLIC_URL).addPath('admin', 'reset-password').setQuery('token', token);
|
|
268
273
|
const subjectLine = subject ? subject : 'Password Reset Request';
|
|
269
274
|
await mailService.send({
|
|
270
275
|
to: email,
|
package/dist/types/files.d.ts
CHANGED
|
@@ -19,3 +19,11 @@ export declare type File = {
|
|
|
19
19
|
tags: string | null;
|
|
20
20
|
metadata: Record<string, any> | null;
|
|
21
21
|
};
|
|
22
|
+
export declare type Metadata = {
|
|
23
|
+
height?: number | undefined;
|
|
24
|
+
width?: number | undefined;
|
|
25
|
+
description?: string | undefined;
|
|
26
|
+
title?: string | undefined;
|
|
27
|
+
tags?: any | undefined;
|
|
28
|
+
metadata?: any | undefined;
|
|
29
|
+
};
|