directus 9.14.3 → 9.15.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/app.js +1 -1
- package/dist/auth/drivers/ldap.js +18 -8
- package/dist/auth/drivers/oauth2.js +19 -9
- package/dist/auth/drivers/openid.js +19 -9
- package/dist/cache.d.ts +3 -0
- package/dist/cache.js +21 -2
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +2 -1
- package/dist/controllers/assets.js +23 -1
- package/dist/controllers/extensions.js +3 -2
- package/dist/database/helpers/schema/dialects/cockroachdb.d.ts +3 -14
- package/dist/database/helpers/schema/dialects/cockroachdb.js +2 -8
- package/dist/database/helpers/schema/dialects/oracle.d.ts +3 -10
- package/dist/database/helpers/schema/dialects/oracle.js +2 -5
- package/dist/database/helpers/schema/types.d.ts +8 -18
- package/dist/database/helpers/schema/types.js +7 -36
- package/dist/database/migrations/20201105B-change-webhook-url-type.js +3 -2
- package/dist/database/migrations/20210312A-webhooks-collections-text.js +3 -2
- package/dist/database/migrations/20210415A-make-filesize-nullable.js +2 -2
- package/dist/database/migrations/20210510A-restructure-relations.js +3 -3
- package/dist/database/migrations/20210903A-add-auth-provider.js +2 -2
- package/dist/database/migrations/20210907A-webhooks-collections-not-null.js +4 -2
- package/dist/database/migrations/20210920A-webhooks-url-not-null.js +2 -2
- package/dist/database/migrations/20220303A-remove-default-project-color.js +2 -2
- package/dist/database/migrations/20220325B-add-default-language.js +2 -2
- package/dist/database/migrations/20220402A-remove-default-value-panel-icon.js +2 -2
- package/dist/database/migrations/20220801A-update-notifications-timestamp-column.d.ts +3 -0
- package/dist/database/migrations/20220801A-update-notifications-timestamp-column.js +19 -0
- package/dist/database/migrations/20220802A-add-custom-aspect-ratios.d.ts +3 -0
- package/dist/database/migrations/20220802A-add-custom-aspect-ratios.js +15 -0
- package/dist/database/system-data/fields/settings.yaml +33 -0
- package/dist/extensions.d.ts +2 -2
- package/dist/extensions.js +10 -9
- package/dist/logger.js +0 -1
- package/dist/middleware/cache.js +3 -3
- package/dist/middleware/respond.js +2 -2
- package/dist/operations/item-create/index.js +1 -1
- package/dist/operations/item-delete/index.js +2 -2
- package/dist/operations/item-read/index.js +2 -2
- package/dist/operations/item-update/index.js +3 -3
- package/dist/services/assets.d.ts +1 -1
- package/dist/services/assets.js +7 -2
- package/dist/services/authorization.js +2 -1
- package/dist/services/graphql/index.js +37 -11
- package/dist/services/graphql/types/hash.d.ts +2 -0
- package/dist/services/graphql/types/hash.js +9 -0
- package/dist/services/specifications.js +1 -6
- package/dist/utils/apply-query.js +11 -0
- package/dist/utils/compress.d.ts +3 -0
- package/dist/utils/compress.js +17 -0
- package/dist/utils/get-ast-from-query.js +1 -1
- package/dist/utils/get-graphql-type.js +3 -0
- package/dist/utils/get-permissions.js +2 -2
- package/dist/utils/get-schema.js +1 -2
- package/package.json +10 -9
|
@@ -4,7 +4,7 @@ exports.down = exports.up = void 0;
|
|
|
4
4
|
const helpers_1 = require("../helpers");
|
|
5
5
|
async function up(knex) {
|
|
6
6
|
const helper = (0, helpers_1.getHelpers)(knex).schema;
|
|
7
|
-
await helper.
|
|
7
|
+
await helper.changeToType('directus_panels', 'icon', 'string', {
|
|
8
8
|
nullable: true,
|
|
9
9
|
default: null,
|
|
10
10
|
length: 30,
|
|
@@ -13,7 +13,7 @@ async function up(knex) {
|
|
|
13
13
|
exports.up = up;
|
|
14
14
|
async function down(knex) {
|
|
15
15
|
const helper = (0, helpers_1.getHelpers)(knex).schema;
|
|
16
|
-
await helper.
|
|
16
|
+
await helper.changeToType('directus_panels', 'icon', 'string', {
|
|
17
17
|
nullable: true,
|
|
18
18
|
default: 'insert_chart',
|
|
19
19
|
length: 30,
|
|
@@ -0,0 +1,19 @@
|
|
|
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.changeToType('directus_notifications', 'timestamp', 'timestamp', {
|
|
8
|
+
nullable: true,
|
|
9
|
+
default: knex.fn.now(),
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
exports.up = up;
|
|
13
|
+
async function down(knex) {
|
|
14
|
+
const helper = (0, helpers_1.getHelpers)(knex).schema;
|
|
15
|
+
await helper.changeToType('directus_notifications', 'timestamp', 'timestamp', {
|
|
16
|
+
nullable: false,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
exports.down = down;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.down = exports.up = void 0;
|
|
4
|
+
async function up(knex) {
|
|
5
|
+
await knex.schema.alterTable('directus_settings', (table) => {
|
|
6
|
+
table.json('custom_aspect_ratios');
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
exports.up = up;
|
|
10
|
+
async function down(knex) {
|
|
11
|
+
await knex.schema.alterTable('directus_settings', (table) => {
|
|
12
|
+
table.dropColumn('custom_aspect_ratios');
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
exports.down = down;
|
|
@@ -392,3 +392,36 @@ fields:
|
|
|
392
392
|
- field: translation_strings
|
|
393
393
|
special: cast-json
|
|
394
394
|
hidden: true
|
|
395
|
+
|
|
396
|
+
- field: image_editor
|
|
397
|
+
interface: presentation-divider
|
|
398
|
+
options:
|
|
399
|
+
icon: image
|
|
400
|
+
title: $t:fields.directus_settings.image_editor
|
|
401
|
+
special:
|
|
402
|
+
- alias
|
|
403
|
+
- no-data
|
|
404
|
+
width: full
|
|
405
|
+
|
|
406
|
+
- field: custom_aspect_ratios
|
|
407
|
+
interface: list
|
|
408
|
+
special: cast-json
|
|
409
|
+
options:
|
|
410
|
+
template: '{{text}}'
|
|
411
|
+
fields:
|
|
412
|
+
- field: text
|
|
413
|
+
name: $t:text
|
|
414
|
+
type: string
|
|
415
|
+
meta:
|
|
416
|
+
interface: text-input
|
|
417
|
+
width: half
|
|
418
|
+
options:
|
|
419
|
+
placeholder: $t:text
|
|
420
|
+
- field: value
|
|
421
|
+
name: $t:value
|
|
422
|
+
type: float
|
|
423
|
+
meta:
|
|
424
|
+
interface: input
|
|
425
|
+
width: half
|
|
426
|
+
options:
|
|
427
|
+
placeholder: $t:value
|
package/dist/extensions.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Router } from 'express';
|
|
2
|
-
import { AppExtensionType, ExtensionType } from '@directus/shared/types';
|
|
2
|
+
import { AppExtensionType, ExtensionType, HybridExtensionType } from '@directus/shared/types';
|
|
3
3
|
export declare function getExtensionManager(): ExtensionManager;
|
|
4
4
|
declare type Options = {
|
|
5
5
|
schedule: boolean;
|
|
@@ -19,7 +19,7 @@ declare class ExtensionManager {
|
|
|
19
19
|
initialize(options?: Partial<Options>): Promise<void>;
|
|
20
20
|
reload(): void;
|
|
21
21
|
getExtensionsList(type?: ExtensionType): string[];
|
|
22
|
-
getAppExtensions(type: AppExtensionType): string | undefined;
|
|
22
|
+
getAppExtensions(type: AppExtensionType | HybridExtensionType): string | undefined;
|
|
23
23
|
getEndpointRouter(): Router;
|
|
24
24
|
private load;
|
|
25
25
|
private unload;
|
package/dist/extensions.js
CHANGED
|
@@ -132,7 +132,7 @@ class ExtensionManager {
|
|
|
132
132
|
}
|
|
133
133
|
async load() {
|
|
134
134
|
try {
|
|
135
|
-
await (0, node_1.ensureExtensionDirs)(env_1.default.EXTENSIONS_PATH, env_1.default.SERVE_APP ? constants_1.EXTENSION_TYPES : constants_1.
|
|
135
|
+
await (0, node_1.ensureExtensionDirs)(env_1.default.EXTENSIONS_PATH, env_1.default.SERVE_APP ? constants_1.EXTENSION_TYPES : constants_1.API_OR_HYBRID_EXTENSION_TYPES);
|
|
136
136
|
this.extensions = await this.getExtensions();
|
|
137
137
|
}
|
|
138
138
|
catch (err) {
|
|
@@ -160,9 +160,9 @@ class ExtensionManager {
|
|
|
160
160
|
initializeWatcher() {
|
|
161
161
|
if (this.options.watch && !this.watcher) {
|
|
162
162
|
logger_1.default.info('Watching extensions for changes...');
|
|
163
|
-
const localExtensionPaths = (env_1.default.SERVE_APP ? constants_1.EXTENSION_TYPES : constants_1.
|
|
163
|
+
const localExtensionPaths = (env_1.default.SERVE_APP ? constants_1.EXTENSION_TYPES : constants_1.API_OR_HYBRID_EXTENSION_TYPES).flatMap((type) => {
|
|
164
164
|
const typeDir = path_1.default.posix.join(path_1.default.relative('.', env_1.default.EXTENSIONS_PATH).split(path_1.default.sep).join(path_1.default.posix.sep), (0, utils_1.pluralize)(type));
|
|
165
|
-
return (0, utils_1.
|
|
165
|
+
return (0, utils_1.isIn)(type, constants_1.HYBRID_EXTENSION_TYPES)
|
|
166
166
|
? [path_1.default.posix.join(typeDir, '*', 'app.js'), path_1.default.posix.join(typeDir, '*', 'api.js')]
|
|
167
167
|
: path_1.default.posix.join(typeDir, '*', 'index.js');
|
|
168
168
|
});
|
|
@@ -179,9 +179,9 @@ class ExtensionManager {
|
|
|
179
179
|
if (this.watcher) {
|
|
180
180
|
const toPackageExtensionPaths = (extensions) => extensions
|
|
181
181
|
.filter((extension) => !extension.local)
|
|
182
|
-
.flatMap((extension) => extension
|
|
182
|
+
.flatMap((extension) => (0, utils_1.isTypeIn)(extension, constants_1.PACKAGE_EXTENSION_TYPES)
|
|
183
183
|
? path_1.default.resolve(extension.path, 'package.json')
|
|
184
|
-
: (0, utils_1.
|
|
184
|
+
: (0, utils_1.isTypeIn)(extension, constants_1.HYBRID_EXTENSION_TYPES)
|
|
185
185
|
? [
|
|
186
186
|
path_1.default.resolve(extension.path, extension.entrypoint.app),
|
|
187
187
|
path_1.default.resolve(extension.path, extension.entrypoint.api),
|
|
@@ -194,8 +194,8 @@ class ExtensionManager {
|
|
|
194
194
|
}
|
|
195
195
|
}
|
|
196
196
|
async getExtensions() {
|
|
197
|
-
const packageExtensions = await (0, node_1.getPackageExtensions)('.', env_1.default.SERVE_APP ? constants_1.EXTENSION_PACKAGE_TYPES : constants_1.
|
|
198
|
-
const localExtensions = await (0, node_1.getLocalExtensions)(env_1.default.EXTENSIONS_PATH, env_1.default.SERVE_APP ? constants_1.EXTENSION_TYPES : constants_1.
|
|
197
|
+
const packageExtensions = await (0, node_1.getPackageExtensions)('.', env_1.default.SERVE_APP ? constants_1.EXTENSION_PACKAGE_TYPES : constants_1.API_OR_HYBRID_EXTENSION_PACKAGE_TYPES);
|
|
198
|
+
const localExtensions = await (0, node_1.getLocalExtensions)(env_1.default.EXTENSIONS_PATH, env_1.default.SERVE_APP ? constants_1.EXTENSION_TYPES : constants_1.API_OR_HYBRID_EXTENSION_TYPES);
|
|
199
199
|
return [...packageExtensions, ...localExtensions];
|
|
200
200
|
}
|
|
201
201
|
async generateExtensionBundles() {
|
|
@@ -205,7 +205,7 @@ class ExtensionManager {
|
|
|
205
205
|
replacement: path,
|
|
206
206
|
}));
|
|
207
207
|
const bundles = {};
|
|
208
|
-
for (const extensionType of constants_1.
|
|
208
|
+
for (const extensionType of constants_1.APP_OR_HYBRID_EXTENSION_TYPES) {
|
|
209
209
|
const entry = (0, node_1.generateExtensionsEntry)(extensionType, this.extensions);
|
|
210
210
|
try {
|
|
211
211
|
const bundle = await (0, rollup_1.rollup)({
|
|
@@ -226,7 +226,8 @@ class ExtensionManager {
|
|
|
226
226
|
return bundles;
|
|
227
227
|
}
|
|
228
228
|
async getSharedDepsMapping(deps) {
|
|
229
|
-
|
|
229
|
+
var _a;
|
|
230
|
+
const appDir = await fs_extra_1.default.readdir(path_1.default.join((0, node_1.resolvePackage)('@directus/app', (_a = require.main) === null || _a === void 0 ? void 0 : _a.filename), 'dist', 'assets'));
|
|
230
231
|
const depsMapping = {};
|
|
231
232
|
for (const dep of deps) {
|
|
232
233
|
const depRegex = new RegExp(`${(0, lodash_1.escapeRegExp)(dep.replace(/\//g, '_'))}\\.[0-9a-f]{8}\\.entry\\.js`);
|
package/dist/logger.js
CHANGED
|
@@ -67,7 +67,6 @@ const httpLoggerEnvConfig = (0, get_config_from_env_1.getConfigFromEnv)('LOGGER_
|
|
|
67
67
|
exports.expressLogger = (0, pino_http_1.default)({
|
|
68
68
|
logger,
|
|
69
69
|
...httpLoggerEnvConfig,
|
|
70
|
-
}, {
|
|
71
70
|
serializers: {
|
|
72
71
|
req(request) {
|
|
73
72
|
const output = pino_http_1.stdSerializers.req(request);
|
package/dist/middleware/cache.js
CHANGED
|
@@ -10,7 +10,7 @@ const get_cache_headers_1 = require("../utils/get-cache-headers");
|
|
|
10
10
|
const get_cache_key_1 = require("../utils/get-cache-key");
|
|
11
11
|
const logger_1 = __importDefault(require("../logger"));
|
|
12
12
|
const checkCacheMiddleware = (0, async_handler_1.default)(async (req, res, next) => {
|
|
13
|
-
var _a, _b, _c;
|
|
13
|
+
var _a, _b, _c, _d;
|
|
14
14
|
const { cache } = (0, cache_1.getCache)();
|
|
15
15
|
if (req.method.toLowerCase() !== 'get' && ((_a = req.path) === null || _a === void 0 ? void 0 : _a.startsWith('/graphql')) === false)
|
|
16
16
|
return next();
|
|
@@ -26,7 +26,7 @@ const checkCacheMiddleware = (0, async_handler_1.default)(async (req, res, next)
|
|
|
26
26
|
const key = (0, get_cache_key_1.getCacheKey)(req);
|
|
27
27
|
let cachedData;
|
|
28
28
|
try {
|
|
29
|
-
cachedData = await
|
|
29
|
+
cachedData = await (0, cache_1.getCacheValue)(cache, key);
|
|
30
30
|
}
|
|
31
31
|
catch (err) {
|
|
32
32
|
logger_1.default.warn(err, `[cache] Couldn't read key ${key}. ${err.message}`);
|
|
@@ -37,7 +37,7 @@ const checkCacheMiddleware = (0, async_handler_1.default)(async (req, res, next)
|
|
|
37
37
|
if (cachedData) {
|
|
38
38
|
let cacheExpiryDate;
|
|
39
39
|
try {
|
|
40
|
-
cacheExpiryDate = (await
|
|
40
|
+
cacheExpiryDate = (_d = (await (0, cache_1.getCacheValue)(cache, `${key}__expires_at`))) === null || _d === void 0 ? void 0 : _d.exp;
|
|
41
41
|
}
|
|
42
42
|
catch (err) {
|
|
43
43
|
logger_1.default.warn(err, `[cache] Couldn't read key ${`${key}__expires_at`}. ${err.message}`);
|
|
@@ -32,8 +32,8 @@ exports.respond = (0, async_handler_1.default)(async (req, res) => {
|
|
|
32
32
|
exceedsMaxSize === false) {
|
|
33
33
|
const key = (0, get_cache_key_1.getCacheKey)(req);
|
|
34
34
|
try {
|
|
35
|
-
await
|
|
36
|
-
await
|
|
35
|
+
await (0, cache_1.setCacheValue)(cache, key, res.locals.payload, (0, ms_1.default)(env_1.default.CACHE_TTL));
|
|
36
|
+
await (0, cache_1.setCacheValue)(cache, `${key}__expires_at`, { exp: Date.now() + (0, ms_1.default)(env_1.default.CACHE_TTL) });
|
|
37
37
|
}
|
|
38
38
|
catch (err) {
|
|
39
39
|
logger_1.default.warn(err, `[cache] Couldn't set key ${key}. ${err}`);
|
|
@@ -32,7 +32,7 @@ exports.default = (0, utils_1.defineOperationApi)({
|
|
|
32
32
|
result = null;
|
|
33
33
|
}
|
|
34
34
|
else {
|
|
35
|
-
result = await itemsService.createMany((0, utils_1.toArray)(payloadObject), { emitEvents });
|
|
35
|
+
result = await itemsService.createMany((0, utils_1.toArray)(payloadObject), { emitEvents: !!emitEvents });
|
|
36
36
|
}
|
|
37
37
|
return result;
|
|
38
38
|
},
|
|
@@ -35,10 +35,10 @@ exports.default = (0, utils_1.defineOperationApi)({
|
|
|
35
35
|
else {
|
|
36
36
|
const keys = (0, utils_1.toArray)(key);
|
|
37
37
|
if (keys.length === 1) {
|
|
38
|
-
result = await itemsService.deleteOne(keys[0], { emitEvents });
|
|
38
|
+
result = await itemsService.deleteOne(keys[0], { emitEvents: !!emitEvents });
|
|
39
39
|
}
|
|
40
40
|
else {
|
|
41
|
-
result = await itemsService.deleteMany(keys, { emitEvents });
|
|
41
|
+
result = await itemsService.deleteMany(keys, { emitEvents: !!emitEvents });
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
return result;
|
|
@@ -35,10 +35,10 @@ exports.default = (0, utils_1.defineOperationApi)({
|
|
|
35
35
|
else {
|
|
36
36
|
const keys = (0, utils_1.toArray)(key);
|
|
37
37
|
if (keys.length === 1) {
|
|
38
|
-
result = await itemsService.readOne(keys[0], sanitizedQueryObject, { emitEvents });
|
|
38
|
+
result = await itemsService.readOne(keys[0], sanitizedQueryObject, { emitEvents: !!emitEvents });
|
|
39
39
|
}
|
|
40
40
|
else {
|
|
41
|
-
result = await itemsService.readMany(keys, sanitizedQueryObject, { emitEvents });
|
|
41
|
+
result = await itemsService.readMany(keys, sanitizedQueryObject, { emitEvents: !!emitEvents });
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
return result;
|
|
@@ -35,15 +35,15 @@ exports.default = (0, utils_1.defineOperationApi)({
|
|
|
35
35
|
}
|
|
36
36
|
let result;
|
|
37
37
|
if (!key || (Array.isArray(key) && key.length === 0)) {
|
|
38
|
-
result = await itemsService.updateByQuery(sanitizedQueryObject, payloadObject, { emitEvents });
|
|
38
|
+
result = await itemsService.updateByQuery(sanitizedQueryObject, payloadObject, { emitEvents: !!emitEvents });
|
|
39
39
|
}
|
|
40
40
|
else {
|
|
41
41
|
const keys = (0, utils_1.toArray)(key);
|
|
42
42
|
if (keys.length === 1) {
|
|
43
|
-
result = await itemsService.updateOne(keys[0], payloadObject, { emitEvents });
|
|
43
|
+
result = await itemsService.updateOne(keys[0], payloadObject, { emitEvents: !!emitEvents });
|
|
44
44
|
}
|
|
45
45
|
else {
|
|
46
|
-
result = await itemsService.updateMany(keys, payloadObject, { emitEvents });
|
|
46
|
+
result = await itemsService.updateMany(keys, payloadObject, { emitEvents: !!emitEvents });
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
return result;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import { Range, StatResponse } from '@directus/drive';
|
|
3
|
+
import { Accountability } from '@directus/shared/types';
|
|
3
4
|
import { Knex } from 'knex';
|
|
4
5
|
import { AbstractServiceOptions, TransformationParams, TransformationPreset } from '../types';
|
|
5
|
-
import { Accountability } from '@directus/shared/types';
|
|
6
6
|
import { AuthorizationService } from './authorization';
|
|
7
7
|
export declare class AssetsService {
|
|
8
8
|
knex: Knex;
|
package/dist/services/assets.js
CHANGED
|
@@ -32,13 +32,14 @@ const mime_types_1 = require("mime-types");
|
|
|
32
32
|
const object_hash_1 = __importDefault(require("object-hash"));
|
|
33
33
|
const path_1 = __importDefault(require("path"));
|
|
34
34
|
const sharp_1 = __importDefault(require("sharp"));
|
|
35
|
+
const uuid_validate_1 = __importDefault(require("uuid-validate"));
|
|
35
36
|
const database_1 = __importDefault(require("../database"));
|
|
36
37
|
const env_1 = __importDefault(require("../env"));
|
|
37
38
|
const exceptions_1 = require("../exceptions");
|
|
39
|
+
const logger_1 = __importDefault(require("../logger"));
|
|
38
40
|
const storage_1 = __importDefault(require("../storage"));
|
|
39
|
-
const authorization_1 = require("./authorization");
|
|
40
41
|
const TransformationUtils = __importStar(require("../utils/transformations"));
|
|
41
|
-
const
|
|
42
|
+
const authorization_1 = require("./authorization");
|
|
42
43
|
sharp_1.default.concurrency(1);
|
|
43
44
|
// Note: don't put this in the service. The service can be initialized in multiple places, but they
|
|
44
45
|
// should all share the same semaphore instance.
|
|
@@ -140,6 +141,10 @@ class AssetsService {
|
|
|
140
141
|
sequentialRead: true,
|
|
141
142
|
}).rotate();
|
|
142
143
|
transforms.forEach(([method, ...args]) => transformer[method].apply(transformer, args));
|
|
144
|
+
readStream.on('error', (e) => {
|
|
145
|
+
logger_1.default.error(e, `Couldn't transform file ${file.id}`);
|
|
146
|
+
readStream.unpipe(transformer);
|
|
147
|
+
});
|
|
143
148
|
await storage_1.default.disk(file.storage).put(assetFilename, readStream.pipe(transformer), type);
|
|
144
149
|
return {
|
|
145
150
|
stream: storage_1.default.disk(file.storage).getStream(assetFilename, range),
|
|
@@ -13,6 +13,7 @@ const strip_function_1 = require("../utils/strip-function");
|
|
|
13
13
|
const items_1 = require("./items");
|
|
14
14
|
const payload_1 = require("./payload");
|
|
15
15
|
const get_relation_info_1 = require("../utils/get-relation-info");
|
|
16
|
+
const constants_1 = require("../constants");
|
|
16
17
|
class AuthorizationService {
|
|
17
18
|
constructor(options) {
|
|
18
19
|
this.knex = options.knex || (0, database_1.default)();
|
|
@@ -379,7 +380,7 @@ class AuthorizationService {
|
|
|
379
380
|
const requiredColumns = [];
|
|
380
381
|
for (const field of Object.values(this.schema.collections[collection].fields)) {
|
|
381
382
|
const specials = (_g = field === null || field === void 0 ? void 0 : field.special) !== null && _g !== void 0 ? _g : [];
|
|
382
|
-
const hasGenerateSpecial =
|
|
383
|
+
const hasGenerateSpecial = constants_1.GENERATE_SPECIAL.some((name) => specials.includes(name));
|
|
383
384
|
const nullable = field.nullable || hasGenerateSpecial || field.generated;
|
|
384
385
|
if (!nullable) {
|
|
385
386
|
requiredColumns.push(field);
|
|
@@ -50,6 +50,7 @@ const geojson_1 = require("./types/geojson");
|
|
|
50
50
|
const string_or_float_1 = require("./types/string-or-float");
|
|
51
51
|
const void_1 = require("./types/void");
|
|
52
52
|
const add_path_to_validation_error_1 = require("./utils/add-path-to-validation-error");
|
|
53
|
+
const hash_1 = require("./types/hash");
|
|
53
54
|
const validationRules = Array.from(graphql_1.specifiedRules);
|
|
54
55
|
if (env_1.default.GRAPHQL_INTROSPECTION === false) {
|
|
55
56
|
validationRules.push(graphql_1.NoSchemaIntrospectionCustomRule);
|
|
@@ -277,7 +278,9 @@ class GraphQLService {
|
|
|
277
278
|
// GraphQL doesn't differentiate between not-null and has-to-be-submitted. We
|
|
278
279
|
// can't non-null in update, as that would require every not-nullable field to be
|
|
279
280
|
// submitted on updates
|
|
280
|
-
if (field.nullable === false &&
|
|
281
|
+
if (field.nullable === false &&
|
|
282
|
+
!constants_1.GENERATE_SPECIAL.some((flag) => field.special.includes(flag)) &&
|
|
283
|
+
action !== 'update') {
|
|
281
284
|
type = (0, graphql_1.GraphQLNonNull)(type);
|
|
282
285
|
}
|
|
283
286
|
if (collection.primary === field.field) {
|
|
@@ -562,6 +565,23 @@ class GraphQLService {
|
|
|
562
565
|
},
|
|
563
566
|
},
|
|
564
567
|
});
|
|
568
|
+
const HashFilterOperators = schemaComposer.createInputTC({
|
|
569
|
+
name: 'hash_filter_operators',
|
|
570
|
+
fields: {
|
|
571
|
+
_null: {
|
|
572
|
+
type: graphql_1.GraphQLBoolean,
|
|
573
|
+
},
|
|
574
|
+
_nnull: {
|
|
575
|
+
type: graphql_1.GraphQLBoolean,
|
|
576
|
+
},
|
|
577
|
+
_empty: {
|
|
578
|
+
type: graphql_1.GraphQLBoolean,
|
|
579
|
+
},
|
|
580
|
+
_nempty: {
|
|
581
|
+
type: graphql_1.GraphQLBoolean,
|
|
582
|
+
},
|
|
583
|
+
},
|
|
584
|
+
});
|
|
565
585
|
const CountFunctionFilterOperators = schemaComposer.createInputTC({
|
|
566
586
|
name: 'count_function_filter_operators',
|
|
567
587
|
fields: {
|
|
@@ -635,6 +655,9 @@ class GraphQLService {
|
|
|
635
655
|
case geojson_1.GraphQLGeoJSON:
|
|
636
656
|
filterOperatorType = GeometryFilterOperators;
|
|
637
657
|
break;
|
|
658
|
+
case hash_1.GraphQLHash:
|
|
659
|
+
filterOperatorType = HashFilterOperators;
|
|
660
|
+
break;
|
|
638
661
|
default:
|
|
639
662
|
filterOperatorType = StringFilterOperators;
|
|
640
663
|
}
|
|
@@ -1023,7 +1046,7 @@ class GraphQLService {
|
|
|
1023
1046
|
* Directus' query structure which is then executed by the services.
|
|
1024
1047
|
*/
|
|
1025
1048
|
async resolveQuery(info) {
|
|
1026
|
-
var _a, _b, _c;
|
|
1049
|
+
var _a, _b, _c, _d;
|
|
1027
1050
|
let collection = info.fieldName;
|
|
1028
1051
|
if (this.scope === 'system')
|
|
1029
1052
|
collection = `directus_${collection}`;
|
|
@@ -1057,13 +1080,15 @@ class GraphQLService {
|
|
|
1057
1080
|
query.limit = 1;
|
|
1058
1081
|
}
|
|
1059
1082
|
// Transform count(a.b.c) into a.b.count(c)
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1083
|
+
if ((_c = query.fields) === null || _c === void 0 ? void 0 : _c.length) {
|
|
1084
|
+
for (let fieldIndex = 0; fieldIndex < query.fields.length; fieldIndex++) {
|
|
1085
|
+
if (query.fields[fieldIndex].includes('(') && query.fields[fieldIndex].includes(')')) {
|
|
1086
|
+
const functionName = query.fields[fieldIndex].split('(')[0];
|
|
1087
|
+
const columnNames = query.fields[fieldIndex].match(constants_2.REGEX_BETWEEN_PARENS)[1].split('.');
|
|
1088
|
+
if (columnNames.length > 1) {
|
|
1089
|
+
const column = columnNames.pop();
|
|
1090
|
+
query.fields[fieldIndex] = columnNames.join('.') + '.' + functionName + '(' + column + ')';
|
|
1091
|
+
}
|
|
1067
1092
|
}
|
|
1068
1093
|
}
|
|
1069
1094
|
}
|
|
@@ -1073,7 +1098,7 @@ class GraphQLService {
|
|
|
1073
1098
|
}
|
|
1074
1099
|
if (query.group) {
|
|
1075
1100
|
// for every entry in result add a group field based on query.group;
|
|
1076
|
-
const aggregateKeys = Object.keys((
|
|
1101
|
+
const aggregateKeys = Object.keys((_d = query.aggregate) !== null && _d !== void 0 ? _d : {});
|
|
1077
1102
|
result.map((field) => {
|
|
1078
1103
|
field.group = (0, lodash_1.omit)(field, aggregateKeys);
|
|
1079
1104
|
});
|
|
@@ -1313,7 +1338,8 @@ class GraphQLService {
|
|
|
1313
1338
|
result[currentKey] = Object.values(value)[0];
|
|
1314
1339
|
}
|
|
1315
1340
|
else {
|
|
1316
|
-
result[currentKey] =
|
|
1341
|
+
result[currentKey] =
|
|
1342
|
+
(value === null || value === void 0 ? void 0 : value.constructor) === Object || (value === null || value === void 0 ? void 0 : value.constructor) === Array ? replaceFuncDeep(value) : value;
|
|
1317
1343
|
}
|
|
1318
1344
|
});
|
|
1319
1345
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GraphQLHash = void 0;
|
|
4
|
+
const graphql_1 = require("graphql");
|
|
5
|
+
exports.GraphQLHash = new graphql_1.GraphQLScalarType({
|
|
6
|
+
...graphql_1.GraphQLString,
|
|
7
|
+
name: 'Hash',
|
|
8
|
+
description: 'Hashed string values',
|
|
9
|
+
});
|
|
@@ -13,6 +13,7 @@ const get_column_1 = require("./get-column");
|
|
|
13
13
|
const get_column_path_1 = require("./get-column-path");
|
|
14
14
|
const get_relation_info_1 = require("./get-relation-info");
|
|
15
15
|
const utils_1 = require("@directus/shared/utils");
|
|
16
|
+
const strip_function_1 = require("./strip-function");
|
|
16
17
|
const generateAlias = (0, nanoid_1.customAlphabet)('abcdefghijklmnopqrstuvwxyz', 5);
|
|
17
18
|
/**
|
|
18
19
|
* Apply the Query to a given Knex query builder instance
|
|
@@ -211,9 +212,11 @@ function applyFilter(knex, schema, rootQuery, rootFilter, collection, subQuery =
|
|
|
211
212
|
const { columnPath, targetCollection } = (0, get_column_path_1.getColumnPath)({ path: filterPath, collection, relations, aliasMap });
|
|
212
213
|
if (!columnPath)
|
|
213
214
|
continue;
|
|
215
|
+
validateFilterOperator(schema.collections[targetCollection].fields[(0, strip_function_1.stripFunction)(filterPath[filterPath.length - 1])].type, filterOperator);
|
|
214
216
|
applyFilterToQuery(columnPath, filterOperator, filterValue, logical, targetCollection);
|
|
215
217
|
}
|
|
216
218
|
else {
|
|
219
|
+
validateFilterOperator(schema.collections[collection].fields[(0, strip_function_1.stripFunction)(filterPath[0])].type, filterOperator);
|
|
217
220
|
applyFilterToQuery(`${collection}.${filterPath[0]}`, filterOperator, filterValue, logical);
|
|
218
221
|
}
|
|
219
222
|
}
|
|
@@ -245,6 +248,14 @@ function applyFilter(knex, schema, rootQuery, rootFilter, collection, subQuery =
|
|
|
245
248
|
}
|
|
246
249
|
}
|
|
247
250
|
}
|
|
251
|
+
function validateFilterOperator(type, filterOperator) {
|
|
252
|
+
if (filterOperator.startsWith('_')) {
|
|
253
|
+
filterOperator = filterOperator.slice(1);
|
|
254
|
+
}
|
|
255
|
+
if (!(0, utils_1.getFilterOperatorsForType)(type).includes(filterOperator)) {
|
|
256
|
+
throw new invalid_query_1.InvalidQueryException(`"${type}" field type does not contain the "_${filterOperator}" filter operator`);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
248
259
|
function applyFilterToQuery(key, operator, compareValue, logical = 'and', originalCollectionName) {
|
|
249
260
|
const [table, column] = key.split('.');
|
|
250
261
|
// Is processed through Knex.Raw, so should be safe to string-inject into these where queries
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.decompress = exports.compress = void 0;
|
|
4
|
+
const snappy_1 = require("snappy");
|
|
5
|
+
const utils_1 = require("@directus/shared/utils");
|
|
6
|
+
async function compress(raw) {
|
|
7
|
+
if (!raw)
|
|
8
|
+
return raw;
|
|
9
|
+
return await (0, snappy_1.compress)((0, utils_1.compress)(raw));
|
|
10
|
+
}
|
|
11
|
+
exports.compress = compress;
|
|
12
|
+
async function decompress(compressed) {
|
|
13
|
+
if (!compressed)
|
|
14
|
+
return compressed;
|
|
15
|
+
return (0, utils_1.decompress)((await (0, snappy_1.uncompress)(compressed, { asBuffer: false })));
|
|
16
|
+
}
|
|
17
|
+
exports.decompress = decompress;
|
|
@@ -70,7 +70,7 @@ async function getASTFromQuery(collection, query, schema, options) {
|
|
|
70
70
|
if (!fields)
|
|
71
71
|
return [];
|
|
72
72
|
fields = await convertWildcards(parentCollection, fields);
|
|
73
|
-
if (!fields)
|
|
73
|
+
if (!fields || !Array.isArray(fields))
|
|
74
74
|
return [];
|
|
75
75
|
const children = [];
|
|
76
76
|
const relationalStructure = {};
|
|
@@ -5,6 +5,7 @@ const graphql_1 = require("graphql");
|
|
|
5
5
|
const graphql_compose_1 = require("graphql-compose");
|
|
6
6
|
const date_1 = require("../services/graphql/types/date");
|
|
7
7
|
const geojson_1 = require("../services/graphql/types/geojson");
|
|
8
|
+
const hash_1 = require("../services/graphql/types/hash");
|
|
8
9
|
function getGraphQLType(localType) {
|
|
9
10
|
switch (localType) {
|
|
10
11
|
case 'boolean':
|
|
@@ -26,6 +27,8 @@ function getGraphQLType(localType) {
|
|
|
26
27
|
case 'dateTime':
|
|
27
28
|
case 'date':
|
|
28
29
|
return date_1.GraphQLDate;
|
|
30
|
+
case 'hash':
|
|
31
|
+
return hash_1.GraphQLHash;
|
|
29
32
|
default:
|
|
30
33
|
return graphql_1.GraphQLString;
|
|
31
34
|
}
|
|
@@ -17,12 +17,12 @@ const merge_permissions_1 = require("../utils/merge-permissions");
|
|
|
17
17
|
const merge_permissions_for_share_1 = require("./merge-permissions-for-share");
|
|
18
18
|
async function getPermissions(accountability, schema) {
|
|
19
19
|
const database = (0, database_1.default)();
|
|
20
|
-
const {
|
|
20
|
+
const { cache } = (0, cache_1.getCache)();
|
|
21
21
|
let permissions = [];
|
|
22
22
|
const { user, role, app, admin, share_scope } = accountability;
|
|
23
23
|
const cacheKey = `permissions-${(0, object_hash_1.default)({ user, role, app, admin, share_scope })}`;
|
|
24
24
|
if (env_1.default.CACHE_PERMISSIONS !== false) {
|
|
25
|
-
const cachedPermissions = await
|
|
25
|
+
const cachedPermissions = await (0, cache_1.getSystemCache)(cacheKey);
|
|
26
26
|
if (cachedPermissions) {
|
|
27
27
|
if (!cachedPermissions.containDynamicData) {
|
|
28
28
|
return processPermissions(accountability, cachedPermissions.permissions, {});
|
package/dist/utils/get-schema.js
CHANGED
|
@@ -20,12 +20,11 @@ const get_local_type_1 = __importDefault(require("./get-local-type"));
|
|
|
20
20
|
async function getSchema(options) {
|
|
21
21
|
const database = (options === null || options === void 0 ? void 0 : options.database) || (0, database_1.default)();
|
|
22
22
|
const schemaInspector = (0, schema_1.default)(database);
|
|
23
|
-
const { systemCache } = (0, cache_1.getCache)();
|
|
24
23
|
let result;
|
|
25
24
|
if (env_1.default.CACHE_SCHEMA !== false) {
|
|
26
25
|
let cachedSchema;
|
|
27
26
|
try {
|
|
28
|
-
cachedSchema = (await
|
|
27
|
+
cachedSchema = (await (0, cache_1.getSystemCache)('schema'));
|
|
29
28
|
}
|
|
30
29
|
catch (err) {
|
|
31
30
|
logger_1.default.warn(err, `[schema-cache] Couldn't retrieve cache. ${err}`);
|