directus 9.9.1 → 9.11.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/README.md +1 -1
- package/dist/app.js +3 -0
- package/dist/auth/drivers/oauth2.d.ts +1 -1
- package/dist/auth/drivers/oauth2.js +14 -11
- package/dist/auth/drivers/openid.d.ts +1 -1
- package/dist/auth/drivers/openid.js +14 -11
- package/dist/cli/commands/schema/apply.js +4 -3
- package/dist/controllers/assets.js +8 -9
- package/dist/database/helpers/date/dialects/sqlite.js +6 -2
- package/dist/database/index.js +5 -0
- package/dist/database/migrations/20210225A-add-relations-sort-field.js +2 -1
- package/dist/database/migrations/20210506A-rename-interfaces.js +2 -1
- package/dist/database/migrations/20210802A-replace-groups.js +2 -1
- package/dist/database/migrations/20210805A-update-groups.js +2 -1
- package/dist/database/migrations/20210805B-change-image-metadata-structure.js +3 -2
- package/dist/database/migrations/20211007A-update-presets.js +5 -4
- package/dist/database/run-ast.js +10 -14
- package/dist/database/system-data/fields/activity.yaml +3 -0
- package/dist/database/system-data/fields/dashboards.yaml +3 -1
- package/dist/database/system-data/fields/notifications.yaml +3 -1
- package/dist/database/system-data/fields/panels.yaml +3 -1
- package/dist/database/system-data/fields/shares.yaml +3 -1
- package/dist/env.js +193 -10
- package/dist/exceptions/index.d.ts +1 -0
- package/dist/exceptions/index.js +1 -0
- package/dist/exceptions/invalid-provider.d.ts +4 -0
- package/dist/exceptions/invalid-provider.js +10 -0
- package/dist/exceptions/range-not-satisfiable.d.ts +2 -2
- package/dist/exceptions/range-not-satisfiable.js +5 -1
- package/dist/middleware/graphql.js +2 -1
- package/dist/services/assets.js +27 -1
- package/dist/services/authentication.js +4 -1
- package/dist/services/fields.js +15 -8
- package/dist/services/graphql.js +61 -33
- package/dist/services/import-export.d.ts +1 -1
- package/dist/services/import-export.js +14 -11
- package/dist/services/items.d.ts +3 -3
- package/dist/services/items.js +31 -3
- package/dist/services/payload.d.ts +2 -2
- package/dist/services/payload.js +9 -8
- package/dist/services/users.d.ts +4 -0
- package/dist/services/users.js +20 -0
- package/dist/utils/apply-query.d.ts +2 -1
- package/dist/utils/apply-query.js +144 -149
- package/dist/utils/apply-snapshot.d.ts +3 -3
- package/dist/utils/apply-snapshot.js +64 -49
- package/dist/utils/get-ast-from-query.js +1 -7
- package/dist/utils/get-column-path.d.ts +16 -0
- package/dist/utils/get-column-path.js +46 -0
- package/dist/utils/get-default-value.js +4 -3
- package/dist/utils/get-permissions.d.ts +1 -1
- package/dist/utils/get-permissions.js +9 -8
- package/dist/utils/get-relation-info.d.ts +7 -0
- package/dist/utils/get-relation-info.js +45 -0
- package/dist/utils/get-relation-type.d.ts +1 -1
- package/dist/utils/get-schema.js +2 -1
- package/dist/utils/get-snapshot.js +22 -4
- package/dist/utils/merge-permissions-for-share.js +1 -1
- package/dist/utils/parse-json.d.ts +5 -0
- package/dist/utils/parse-json.js +19 -0
- package/dist/utils/reduce-schema.js +4 -5
- package/dist/utils/sanitize-query.d.ts +1 -2
- package/dist/utils/sanitize-query.js +6 -5
- package/dist/utils/validate-keys.d.ts +6 -0
- package/dist/utils/validate-keys.js +28 -0
- package/dist/utils/validate-query.js +1 -1
- package/package.json +16 -18
package/dist/env.js
CHANGED
|
@@ -14,6 +14,180 @@ const lodash_1 = require("lodash");
|
|
|
14
14
|
const path_1 = __importDefault(require("path"));
|
|
15
15
|
const require_yaml_1 = require("./utils/require-yaml");
|
|
16
16
|
const utils_1 = require("@directus/shared/utils");
|
|
17
|
+
const parse_json_1 = require("./utils/parse-json");
|
|
18
|
+
// keeping this here for now to prevent a circular import to constants.ts
|
|
19
|
+
const allowedEnvironmentVars = [
|
|
20
|
+
// general
|
|
21
|
+
'CONFIG_PATH',
|
|
22
|
+
'HOST',
|
|
23
|
+
'PORT',
|
|
24
|
+
'PUBLIC_URL',
|
|
25
|
+
'LOG_LEVEL',
|
|
26
|
+
'LOG_STYLE',
|
|
27
|
+
'MAX_PAYLOAD_SIZE',
|
|
28
|
+
'ROOT_REDIRECT',
|
|
29
|
+
'SERVE_APP',
|
|
30
|
+
'GRAPHQL_INTROSPECTION',
|
|
31
|
+
// server
|
|
32
|
+
'SERVER_KEEP_ALIVE_TIMEOUT',
|
|
33
|
+
'SERVER_HEADERS_TIMEOUT',
|
|
34
|
+
// database
|
|
35
|
+
'DB_CLIENT',
|
|
36
|
+
'DB_HOST',
|
|
37
|
+
'DB_PORT',
|
|
38
|
+
'DB_DATABASE',
|
|
39
|
+
'DB_USER',
|
|
40
|
+
'DB_PASSWORD',
|
|
41
|
+
'DB_FILENAME',
|
|
42
|
+
'DB_CONNECTION_STRING',
|
|
43
|
+
'DB_POOL_.+',
|
|
44
|
+
'DB_EXCLUDE_TABLES',
|
|
45
|
+
'DB_CHARSET',
|
|
46
|
+
'DB_VERSION',
|
|
47
|
+
// security
|
|
48
|
+
'KEY',
|
|
49
|
+
'SECRET',
|
|
50
|
+
'ACCESS_TOKEN_TTL',
|
|
51
|
+
'REFRESH_TOKEN_TTL',
|
|
52
|
+
'REFRESH_TOKEN_COOKIE_DOMAIN',
|
|
53
|
+
'REFRESH_TOKEN_COOKIE_SECURE',
|
|
54
|
+
'REFRESH_TOKEN_COOKIE_SAME_SITE',
|
|
55
|
+
'REFRESH_TOKEN_COOKIE_NAME',
|
|
56
|
+
'PASSWORD_RESET_URL_ALLOW_LIST',
|
|
57
|
+
'USER_INVITE_URL_ALLOW_LIST',
|
|
58
|
+
'IP_TRUST_PROXY',
|
|
59
|
+
'IP_CUSTOM_HEADER',
|
|
60
|
+
'ASSETS_CONTENT_SECURITY_POLICY',
|
|
61
|
+
'IMPORT_IP_DENY_LIST',
|
|
62
|
+
'CONTENT_SECURITY_POLICY_.+',
|
|
63
|
+
'HSTS_.+',
|
|
64
|
+
// hashing
|
|
65
|
+
'HASH_MEMORY_COST',
|
|
66
|
+
'HASH_LENGTH',
|
|
67
|
+
'HASH_TIME_COST',
|
|
68
|
+
'HASH_PARALLELISM',
|
|
69
|
+
'HASH_TYPE',
|
|
70
|
+
'HASH_ASSOCIATED_DATA',
|
|
71
|
+
// cors
|
|
72
|
+
'CORS_ENABLED',
|
|
73
|
+
'CORS_ORIGIN',
|
|
74
|
+
'CORS_METHODS',
|
|
75
|
+
'CORS_ALLOWED_HEADERS',
|
|
76
|
+
'CORS_EXPOSED_HEADERS',
|
|
77
|
+
'CORS_CREDENTIALS',
|
|
78
|
+
'CORS_MAX_AGE',
|
|
79
|
+
// rate limiting
|
|
80
|
+
'RATE_LIMITER_ENABLED',
|
|
81
|
+
'RATE_LIMITER_POINTS',
|
|
82
|
+
'RATE_LIMITER_DURATION',
|
|
83
|
+
'RATE_LIMITER_STORE',
|
|
84
|
+
'RATE_LIMITER_REDIS',
|
|
85
|
+
'RATE_LIMITER_REDIS_HOST',
|
|
86
|
+
'RATE_LIMITER_REDIS_PORT',
|
|
87
|
+
'RATE_LIMITER_REDIS_PASSWORD',
|
|
88
|
+
'RATE_LIMITER_MEMCACHE',
|
|
89
|
+
// cache
|
|
90
|
+
'CACHE_ENABLED',
|
|
91
|
+
'CACHE_TTL',
|
|
92
|
+
'CACHE_CONTROL_S_MAXAGE',
|
|
93
|
+
'CACHE_AUTO_PURGE',
|
|
94
|
+
'CACHE_SYSTEM_TTL',
|
|
95
|
+
'CACHE_SCHEMA',
|
|
96
|
+
'CACHE_PERMISSIONS',
|
|
97
|
+
'CACHE_NAMESPACE',
|
|
98
|
+
'CACHE_STORE',
|
|
99
|
+
'CACHE_STATUS_HEADER',
|
|
100
|
+
'CACHE_REDIS',
|
|
101
|
+
'CACHE_REDIS_HOST',
|
|
102
|
+
'CACHE_REDIS_PORT',
|
|
103
|
+
'CACHE_REDIS_PASSWORD',
|
|
104
|
+
'CACHE_MEMCACHE',
|
|
105
|
+
// storage
|
|
106
|
+
'STORAGE_LOCATIONS',
|
|
107
|
+
'STORAGE_.+_DRIVER',
|
|
108
|
+
'STORAGE_.+_ROOT',
|
|
109
|
+
'STORAGE_.+_KEY',
|
|
110
|
+
'STORAGE_.+_SECRET',
|
|
111
|
+
'STORAGE_.+_BUCKET',
|
|
112
|
+
'STORAGE_.+_REGION',
|
|
113
|
+
'STORAGE_.+_ENDPOINT',
|
|
114
|
+
'STORAGE_.+_ACL',
|
|
115
|
+
'STORAGE_.+_CONTAINER_NAME',
|
|
116
|
+
'STORAGE_.+_ACCOUNT_NAME',
|
|
117
|
+
'STORAGE_.+_ACCOUNT_KEY',
|
|
118
|
+
'STORAGE_.+_ENDPOINT',
|
|
119
|
+
'STORAGE_.+_KEY_FILENAME',
|
|
120
|
+
'STORAGE_.+_BUCKET',
|
|
121
|
+
// metadata
|
|
122
|
+
'FILE_METADATA_ALLOW_LIST',
|
|
123
|
+
// assets
|
|
124
|
+
'ASSETS_CACHE_TTL',
|
|
125
|
+
'ASSETS_TRANSFORM_MAX_CONCURRENT',
|
|
126
|
+
'ASSETS_TRANSFORM_IMAGE_MAX_DIMENSION',
|
|
127
|
+
'ASSETS_TRANSFORM_MAX_OPERATIONS',
|
|
128
|
+
'ASSETS_CONTENT_SECURITY_POLICY',
|
|
129
|
+
// auth
|
|
130
|
+
'AUTH_PROVIDERS',
|
|
131
|
+
'AUTH_DISABLE_DEFAULT',
|
|
132
|
+
'AUTH_.+_DRIVER',
|
|
133
|
+
'AUTH_.+_CLIENT_ID',
|
|
134
|
+
'AUTH_.+_CLIENT_SECRET',
|
|
135
|
+
'AUTH_.+_SCOPE',
|
|
136
|
+
'AUTH_.+_AUTHORIZE_URL',
|
|
137
|
+
'AUTH_.+_ACCESS_URL',
|
|
138
|
+
'AUTH_.+_PROFILE_URL',
|
|
139
|
+
'AUTH_.+_IDENTIFIER_KEY',
|
|
140
|
+
'AUTH_.+_EMAIL_KEY',
|
|
141
|
+
'AUTH_.+_FIRST_NAME_KEY',
|
|
142
|
+
'AUTH_.+_LAST_NAME_KEY',
|
|
143
|
+
'AUTH_.+_ALLOW_PUBLIC_REGISTRATION',
|
|
144
|
+
'AUTH_.+_DEFAULT_ROLE_ID',
|
|
145
|
+
'AUTH_.+_ICON',
|
|
146
|
+
'AUTH_.+_PARAMS',
|
|
147
|
+
'AUTH_.+_ISSUER_URL',
|
|
148
|
+
'AUTH_.+_AUTH_REQUIRE_VERIFIED_EMAIL',
|
|
149
|
+
'AUTH_.+_CLIENT_URL',
|
|
150
|
+
'AUTH_.+_BIND_DN',
|
|
151
|
+
'AUTH_.+_BIND_PASSWORD',
|
|
152
|
+
'AUTH_.+_USER_DN',
|
|
153
|
+
'AUTH_.+_USER_ATTRIBUTE',
|
|
154
|
+
'AUTH_.+_USER_SCOPE',
|
|
155
|
+
'AUTH_.+_MAIL_ATTRIBUTE',
|
|
156
|
+
'AUTH_.+_FIRST_NAME_ATTRIBUTE',
|
|
157
|
+
'AUTH_.+_LAST_NAME_ATTRIBUTE',
|
|
158
|
+
'AUTH_.+_GROUP_DN',
|
|
159
|
+
'AUTH_.+_GROUP_ATTRIBUTE',
|
|
160
|
+
'AUTH_.+_GROUP_SCOPE',
|
|
161
|
+
// extensions
|
|
162
|
+
'EXTENSIONS_PATH',
|
|
163
|
+
'EXTENSIONS_AUTO_RELOAD',
|
|
164
|
+
// emails
|
|
165
|
+
'EMAIL_FROM',
|
|
166
|
+
'EMAIL_TRANSPORT',
|
|
167
|
+
'EMAIL_SENDMAIL_NEW_LINE',
|
|
168
|
+
'EMAIL_SENDMAIL_PATH',
|
|
169
|
+
'EMAIL_SMTP_HOST',
|
|
170
|
+
'EMAIL_SMTP_PORT',
|
|
171
|
+
'EMAIL_SMTP_USER',
|
|
172
|
+
'EMAIL_SMTP_PASSWORD',
|
|
173
|
+
'EMAIL_SMTP_POOL',
|
|
174
|
+
'EMAIL_SMTP_SECURE',
|
|
175
|
+
'EMAIL_SMTP_IGNORE_TLS',
|
|
176
|
+
'EMAIL_MAILGUN_API_KEY',
|
|
177
|
+
'EMAIL_MAILGUN_DOMAIN',
|
|
178
|
+
'EMAIL_MAILGUN_HOST',
|
|
179
|
+
'EMAIL_SES_CREDENTIALS__ACCESS_KEY_ID',
|
|
180
|
+
'EMAIL_SES_CREDENTIALS__SECRET_ACCESS_KEY',
|
|
181
|
+
'EMAIL_SES_REGION',
|
|
182
|
+
// admin account
|
|
183
|
+
'ADMIN_EMAIL',
|
|
184
|
+
'ADMIN_PASSWORD',
|
|
185
|
+
// telemetry
|
|
186
|
+
'TELEMETRY',
|
|
187
|
+
// limits & optimization
|
|
188
|
+
'RELATIONAL_BATCH_SIZE',
|
|
189
|
+
'EXPORT_BATCH_SIZE',
|
|
190
|
+
].map((name) => new RegExp(`^${name}$`));
|
|
17
191
|
const acceptedEnvTypes = ['string', 'number', 'regex', 'array', 'json'];
|
|
18
192
|
const defaults = {
|
|
19
193
|
CONFIG_PATH: path_1.default.resolve(process.cwd(), '.env'),
|
|
@@ -70,6 +244,7 @@ const defaults = {
|
|
|
70
244
|
RELATIONAL_BATCH_SIZE: 25000,
|
|
71
245
|
EXPORT_BATCH_SIZE: 5000,
|
|
72
246
|
FILE_METADATA_ALLOW_LIST: 'ifd0.Make,ifd0.Model,exif.FNumber,exif.ExposureTime,exif.FocalLength,exif.ISO',
|
|
247
|
+
GRAPHQL_INTROSPECTION: true,
|
|
73
248
|
};
|
|
74
249
|
// Allows us to force certain environment variable into a type, instead of relying
|
|
75
250
|
// on the auto-parsed type in processValues. ref #3705
|
|
@@ -84,6 +259,7 @@ const typeMap = {
|
|
|
84
259
|
DB_EXCLUDE_TABLES: 'array',
|
|
85
260
|
IMPORT_IP_DENY_LIST: 'array',
|
|
86
261
|
FILE_METADATA_ALLOW_LIST: 'array',
|
|
262
|
+
GRAPHQL_INTROSPECTION: 'boolean',
|
|
87
263
|
};
|
|
88
264
|
let env = {
|
|
89
265
|
...defaults,
|
|
@@ -177,15 +353,17 @@ function processValues(env) {
|
|
|
177
353
|
let newKey;
|
|
178
354
|
if (key.length > 5 && key.endsWith('_FILE')) {
|
|
179
355
|
newKey = key.slice(0, -5);
|
|
180
|
-
if (
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
356
|
+
if (allowedEnvironmentVars.some((pattern) => pattern.test(newKey))) {
|
|
357
|
+
if (newKey in env) {
|
|
358
|
+
throw new Error(`Duplicate environment variable encountered: you can't use "${newKey}" and "${key}" simultaneously.`);
|
|
359
|
+
}
|
|
360
|
+
try {
|
|
361
|
+
value = fs_1.default.readFileSync(value, { encoding: 'utf8' });
|
|
362
|
+
key = newKey;
|
|
363
|
+
}
|
|
364
|
+
catch {
|
|
365
|
+
throw new Error(`Failed to read value from file "${value}", defined in environment variable "${key}".`);
|
|
366
|
+
}
|
|
189
367
|
}
|
|
190
368
|
}
|
|
191
369
|
// Convert values with a type prefix
|
|
@@ -209,6 +387,8 @@ function processValues(env) {
|
|
|
209
387
|
case 'json':
|
|
210
388
|
env[key] = tryJSON(value);
|
|
211
389
|
break;
|
|
390
|
+
case 'boolean':
|
|
391
|
+
env[key] = toBoolean(value);
|
|
212
392
|
}
|
|
213
393
|
continue;
|
|
214
394
|
}
|
|
@@ -251,9 +431,12 @@ function processValues(env) {
|
|
|
251
431
|
}
|
|
252
432
|
function tryJSON(value) {
|
|
253
433
|
try {
|
|
254
|
-
return
|
|
434
|
+
return (0, parse_json_1.parseJSON)(value);
|
|
255
435
|
}
|
|
256
436
|
catch {
|
|
257
437
|
return value;
|
|
258
438
|
}
|
|
259
439
|
}
|
|
440
|
+
function toBoolean(value) {
|
|
441
|
+
return value === 'true' || value === true || value === '1' || value === 1;
|
|
442
|
+
}
|
|
@@ -7,6 +7,7 @@ export * from './invalid-credentials';
|
|
|
7
7
|
export * from './invalid-ip';
|
|
8
8
|
export * from './invalid-otp';
|
|
9
9
|
export * from './invalid-payload';
|
|
10
|
+
export * from './invalid-provider';
|
|
10
11
|
export * from './invalid-query';
|
|
11
12
|
export * from './invalid-token';
|
|
12
13
|
export * from './method-not-allowed';
|
package/dist/exceptions/index.js
CHANGED
|
@@ -19,6 +19,7 @@ __exportStar(require("./invalid-credentials"), exports);
|
|
|
19
19
|
__exportStar(require("./invalid-ip"), exports);
|
|
20
20
|
__exportStar(require("./invalid-otp"), exports);
|
|
21
21
|
__exportStar(require("./invalid-payload"), exports);
|
|
22
|
+
__exportStar(require("./invalid-provider"), exports);
|
|
22
23
|
__exportStar(require("./invalid-query"), exports);
|
|
23
24
|
__exportStar(require("./invalid-token"), exports);
|
|
24
25
|
__exportStar(require("./method-not-allowed"), exports);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.InvalidProviderException = void 0;
|
|
4
|
+
const exceptions_1 = require("@directus/shared/exceptions");
|
|
5
|
+
class InvalidProviderException extends exceptions_1.BaseException {
|
|
6
|
+
constructor(message = 'Invalid provider.') {
|
|
7
|
+
super(message, 403, 'INVALID_PROVIDER');
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
exports.InvalidProviderException = InvalidProviderException;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Range } from '@directus/drive';
|
|
2
1
|
import { BaseException } from '@directus/shared/exceptions';
|
|
2
|
+
import { Range } from '@directus/drive';
|
|
3
3
|
export declare class RangeNotSatisfiableException extends BaseException {
|
|
4
|
-
constructor(range
|
|
4
|
+
constructor(range?: Range);
|
|
5
5
|
}
|
|
@@ -4,7 +4,11 @@ exports.RangeNotSatisfiableException = void 0;
|
|
|
4
4
|
const exceptions_1 = require("@directus/shared/exceptions");
|
|
5
5
|
class RangeNotSatisfiableException extends exceptions_1.BaseException {
|
|
6
6
|
constructor(range) {
|
|
7
|
-
|
|
7
|
+
var _a, _b;
|
|
8
|
+
const rangeString = range && ((range === null || range === void 0 ? void 0 : range.start) !== undefined || (range === null || range === void 0 ? void 0 : range.end) !== undefined)
|
|
9
|
+
? `"${(_a = range.start) !== null && _a !== void 0 ? _a : ''}-${(_b = range.end) !== null && _b !== void 0 ? _b : ''}" `
|
|
10
|
+
: '';
|
|
11
|
+
super(`Range ${rangeString}is invalid or the file's size doesn't match the requested range.`, 416, 'RANGE_NOT_SATISFIABLE');
|
|
8
12
|
}
|
|
9
13
|
}
|
|
10
14
|
exports.RangeNotSatisfiableException = RangeNotSatisfiableException;
|
|
@@ -7,6 +7,7 @@ exports.parseGraphQL = void 0;
|
|
|
7
7
|
const graphql_1 = require("graphql");
|
|
8
8
|
const exceptions_1 = require("../exceptions");
|
|
9
9
|
const async_handler_1 = __importDefault(require("../utils/async-handler"));
|
|
10
|
+
const parse_json_1 = require("../utils/parse-json");
|
|
10
11
|
exports.parseGraphQL = (0, async_handler_1.default)(async (req, res, next) => {
|
|
11
12
|
if (req.method !== 'GET' && req.method !== 'POST') {
|
|
12
13
|
throw new exceptions_1.MethodNotAllowedException('GraphQL only supports GET and POST requests.', { allow: ['GET', 'POST'] });
|
|
@@ -19,7 +20,7 @@ exports.parseGraphQL = (0, async_handler_1.default)(async (req, res, next) => {
|
|
|
19
20
|
query = req.query.query || null;
|
|
20
21
|
if (req.query.variables) {
|
|
21
22
|
try {
|
|
22
|
-
variables =
|
|
23
|
+
variables = (0, parse_json_1.parseJSON)(req.query.variables);
|
|
23
24
|
}
|
|
24
25
|
catch {
|
|
25
26
|
throw new exceptions_1.InvalidQueryException(`Variables are invalid JSON.`);
|
package/dist/services/assets.js
CHANGED
|
@@ -70,9 +70,35 @@ class AssetsService {
|
|
|
70
70
|
if (!exists)
|
|
71
71
|
throw new exceptions_1.ForbiddenException();
|
|
72
72
|
if (range) {
|
|
73
|
-
|
|
73
|
+
const missingRangeLimits = range.start === undefined && range.end === undefined;
|
|
74
|
+
const endBeforeStart = range.start !== undefined && range.end !== undefined && range.end <= range.start;
|
|
75
|
+
const startOverflow = range.start !== undefined && range.start >= file.filesize;
|
|
76
|
+
const endUnderflow = range.end !== undefined && range.end <= 0;
|
|
77
|
+
if (missingRangeLimits || endBeforeStart || startOverflow || endUnderflow) {
|
|
74
78
|
throw new exceptions_1.RangeNotSatisfiableException(range);
|
|
75
79
|
}
|
|
80
|
+
const lastByte = file.filesize - 1;
|
|
81
|
+
if (range.end) {
|
|
82
|
+
if (range.start === undefined) {
|
|
83
|
+
// fetch chunk from tail
|
|
84
|
+
range.start = file.filesize - range.end;
|
|
85
|
+
range.end = lastByte;
|
|
86
|
+
}
|
|
87
|
+
if (range.end >= file.filesize) {
|
|
88
|
+
// fetch entire file
|
|
89
|
+
range.end = lastByte;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (range.start) {
|
|
93
|
+
if (range.end === undefined) {
|
|
94
|
+
// fetch entire file
|
|
95
|
+
range.end = lastByte;
|
|
96
|
+
}
|
|
97
|
+
if (range.start < 0) {
|
|
98
|
+
// fetch file from head
|
|
99
|
+
range.start = 0;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
76
102
|
}
|
|
77
103
|
const type = file.type;
|
|
78
104
|
const transforms = TransformationUtils.resolvePreset(transformation, file);
|
|
@@ -45,7 +45,6 @@ class AuthenticationService {
|
|
|
45
45
|
.from('directus_users as u')
|
|
46
46
|
.leftJoin('directus_roles as r', 'u.role', 'r.id')
|
|
47
47
|
.where('u.id', await provider.getUserID((0, lodash_1.cloneDeep)(payload)))
|
|
48
|
-
.andWhere('u.provider', providerName)
|
|
49
48
|
.first();
|
|
50
49
|
const updatedPayload = await emitter_1.default.emitFilter('auth.login', payload, {
|
|
51
50
|
status: 'pending',
|
|
@@ -79,6 +78,10 @@ class AuthenticationService {
|
|
|
79
78
|
throw new exceptions_1.InvalidCredentialsException();
|
|
80
79
|
}
|
|
81
80
|
}
|
|
81
|
+
else if (user.provider !== providerName) {
|
|
82
|
+
await (0, stall_1.stall)(STALL_TIME, timeStart);
|
|
83
|
+
throw new exceptions_1.InvalidProviderException();
|
|
84
|
+
}
|
|
82
85
|
const settingsService = new settings_1.SettingsService({
|
|
83
86
|
knex: this.knex,
|
|
84
87
|
schema: this.schema,
|
package/dist/services/fields.js
CHANGED
|
@@ -351,13 +351,6 @@ class FieldsService {
|
|
|
351
351
|
});
|
|
352
352
|
await this.knex.transaction(async (trx) => {
|
|
353
353
|
var _a, _b;
|
|
354
|
-
if (this.schema.collections[collection] &&
|
|
355
|
-
field in this.schema.collections[collection].fields &&
|
|
356
|
-
this.schema.collections[collection].fields[field].alias === false) {
|
|
357
|
-
await trx.schema.table(collection, (table) => {
|
|
358
|
-
table.dropColumn(field);
|
|
359
|
-
});
|
|
360
|
-
}
|
|
361
354
|
const relations = this.schema.relations.filter((relation) => {
|
|
362
355
|
var _a;
|
|
363
356
|
return ((relation.collection === collection && relation.field === field) ||
|
|
@@ -389,6 +382,14 @@ class FieldsService {
|
|
|
389
382
|
.where({ many_collection: relation.collection, many_field: relation.field });
|
|
390
383
|
}
|
|
391
384
|
}
|
|
385
|
+
// Delete field only after foreign key constraints are removed
|
|
386
|
+
if (this.schema.collections[collection] &&
|
|
387
|
+
field in this.schema.collections[collection].fields &&
|
|
388
|
+
this.schema.collections[collection].fields[field].alias === false) {
|
|
389
|
+
await trx.schema.table(collection, (table) => {
|
|
390
|
+
table.dropColumn(field);
|
|
391
|
+
});
|
|
392
|
+
}
|
|
392
393
|
const collectionMeta = await trx
|
|
393
394
|
.select('archive_field', 'sort_field')
|
|
394
395
|
.from('directus_collections')
|
|
@@ -441,7 +442,13 @@ class FieldsService {
|
|
|
441
442
|
if (field.type === 'alias' || field.type === 'unknown')
|
|
442
443
|
return;
|
|
443
444
|
if ((_a = field.schema) === null || _a === void 0 ? void 0 : _a.has_auto_increment) {
|
|
444
|
-
|
|
445
|
+
if (field.type === 'bigInteger') {
|
|
446
|
+
// Create an auto-incremented big integer (MySQL, PostgreSQL) or an auto-incremented integer (other DBs)
|
|
447
|
+
column = table.bigIncrements(field.field);
|
|
448
|
+
}
|
|
449
|
+
else {
|
|
450
|
+
column = table.increments(field.field);
|
|
451
|
+
}
|
|
445
452
|
}
|
|
446
453
|
else if (field.type === 'string') {
|
|
447
454
|
column = table.string(field.field, (_c = (_b = field.schema) === null || _b === void 0 ? void 0 : _b.max_length) !== null && _c !== void 0 ? _c : undefined);
|
package/dist/services/graphql.js
CHANGED
|
@@ -89,7 +89,16 @@ class GraphQLService {
|
|
|
89
89
|
async execute({ document, variables, operationName, contextValue, }) {
|
|
90
90
|
var _a;
|
|
91
91
|
const schema = this.getSchema();
|
|
92
|
-
const validationErrors = (0, graphql_1.validate)(schema, document,
|
|
92
|
+
const validationErrors = (0, graphql_1.validate)(schema, document, [
|
|
93
|
+
...graphql_1.specifiedRules,
|
|
94
|
+
(context) => ({
|
|
95
|
+
Field(node) {
|
|
96
|
+
if (env_1.default.GRAPHQL_INTROSPECTION === false && (node.name.value === '__schema' || node.name.value === '__type')) {
|
|
97
|
+
context.reportError(new graphql_1.GraphQLError('GraphQL introspection is not allowed. The query contained __schema or __type.', [node]));
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
}),
|
|
101
|
+
]);
|
|
93
102
|
if (validationErrors.length > 0) {
|
|
94
103
|
throw new exceptions_1.GraphQLValidationException({ graphqlErrors: validationErrors });
|
|
95
104
|
}
|
|
@@ -161,16 +170,7 @@ class GraphQLService {
|
|
|
161
170
|
acc[collectionName] = ReadCollectionTypes[collection.collection].getResolver(collection.collection);
|
|
162
171
|
if (this.schema.collections[collection.collection].singleton === false) {
|
|
163
172
|
acc[`${collectionName}_by_id`] = ReadCollectionTypes[collection.collection].getResolver(`${collection.collection}_by_id`);
|
|
164
|
-
|
|
165
|
-
const graphqlType = (0, get_graphql_type_1.getGraphQLType)(field.type);
|
|
166
|
-
if (graphqlType === graphql_1.GraphQLInt || graphqlType === graphql_1.GraphQLFloat) {
|
|
167
|
-
return true;
|
|
168
|
-
}
|
|
169
|
-
return false;
|
|
170
|
-
});
|
|
171
|
-
if (hasAggregate) {
|
|
172
|
-
acc[`${collectionName}_aggregated`] = ReadCollectionTypes[collection.collection].getResolver(`${collection.collection}_aggregated`);
|
|
173
|
-
}
|
|
173
|
+
acc[`${collectionName}_aggregated`] = ReadCollectionTypes[collection.collection].getResolver(`${collection.collection}_aggregated`);
|
|
174
174
|
}
|
|
175
175
|
return acc;
|
|
176
176
|
}, {}));
|
|
@@ -415,7 +415,8 @@ class GraphQLService {
|
|
|
415
415
|
const { CollectionTypes: ReadCollectionTypes } = getTypes('read');
|
|
416
416
|
const ReadableCollectionFilterTypes = {};
|
|
417
417
|
const AggregatedFunctions = {};
|
|
418
|
-
const
|
|
418
|
+
const AggregatedFields = {};
|
|
419
|
+
const AggregateMethods = {};
|
|
419
420
|
const StringFilterOperators = schemaComposer.createInputTC({
|
|
420
421
|
name: 'string_filter_operators',
|
|
421
422
|
fields: {
|
|
@@ -671,7 +672,7 @@ class GraphQLService {
|
|
|
671
672
|
_and: [ReadableCollectionFilterTypes[collection.collection]],
|
|
672
673
|
_or: [ReadableCollectionFilterTypes[collection.collection]],
|
|
673
674
|
});
|
|
674
|
-
|
|
675
|
+
AggregatedFields[collection.collection] = schemaComposer.createObjectTC({
|
|
675
676
|
name: `${collection.collection}_aggregated_fields`,
|
|
676
677
|
fields: Object.values(collection.fields).reduce((acc, field) => {
|
|
677
678
|
const graphqlType = (0, get_graphql_type_1.getGraphQLType)(field.type);
|
|
@@ -689,46 +690,71 @@ class GraphQLService {
|
|
|
689
690
|
return acc;
|
|
690
691
|
}, {}),
|
|
691
692
|
});
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
693
|
+
AggregateMethods[collection.collection] = {
|
|
694
|
+
group: {
|
|
695
|
+
name: 'group',
|
|
696
|
+
type: graphql_compose_1.GraphQLJSON,
|
|
697
|
+
},
|
|
698
|
+
countAll: {
|
|
699
|
+
name: 'countAll',
|
|
700
|
+
type: graphql_1.GraphQLInt,
|
|
701
|
+
},
|
|
702
|
+
count: {
|
|
703
|
+
name: 'count',
|
|
704
|
+
type: schemaComposer.createObjectTC({
|
|
705
|
+
name: `${collection.collection}_aggregated_count`,
|
|
706
|
+
fields: Object.values(collection.fields).reduce((acc, field) => {
|
|
707
|
+
acc[field.field] = {
|
|
708
|
+
type: graphql_1.GraphQLInt,
|
|
709
|
+
description: field.note,
|
|
710
|
+
};
|
|
711
|
+
return acc;
|
|
712
|
+
}, {}),
|
|
713
|
+
}),
|
|
714
|
+
},
|
|
715
|
+
};
|
|
716
|
+
const hasNumericAggregates = Object.values(collection.fields).some((field) => {
|
|
717
|
+
const graphqlType = (0, get_graphql_type_1.getGraphQLType)(field.type);
|
|
718
|
+
if (graphqlType === graphql_1.GraphQLInt || graphqlType === graphql_1.GraphQLFloat) {
|
|
719
|
+
return true;
|
|
720
|
+
}
|
|
721
|
+
return false;
|
|
722
|
+
});
|
|
723
|
+
if (hasNumericAggregates) {
|
|
724
|
+
Object.assign(AggregateMethods[collection.collection], {
|
|
699
725
|
avg: {
|
|
700
726
|
name: 'avg',
|
|
701
|
-
type:
|
|
727
|
+
type: AggregatedFields[collection.collection],
|
|
702
728
|
},
|
|
703
729
|
sum: {
|
|
704
730
|
name: 'sum',
|
|
705
|
-
type:
|
|
706
|
-
},
|
|
707
|
-
count: {
|
|
708
|
-
name: 'count',
|
|
709
|
-
type: AggregatedFilters[collection.collection],
|
|
731
|
+
type: AggregatedFields[collection.collection],
|
|
710
732
|
},
|
|
711
733
|
countDistinct: {
|
|
712
734
|
name: 'countDistinct',
|
|
713
|
-
type:
|
|
735
|
+
type: AggregatedFields[collection.collection],
|
|
714
736
|
},
|
|
715
737
|
avgDistinct: {
|
|
716
738
|
name: 'avgDistinct',
|
|
717
|
-
type:
|
|
739
|
+
type: AggregatedFields[collection.collection],
|
|
718
740
|
},
|
|
719
741
|
sumDistinct: {
|
|
720
742
|
name: 'sumDistinct',
|
|
721
|
-
type:
|
|
743
|
+
type: AggregatedFields[collection.collection],
|
|
722
744
|
},
|
|
723
745
|
min: {
|
|
724
746
|
name: 'min',
|
|
725
|
-
type:
|
|
747
|
+
type: AggregatedFields[collection.collection],
|
|
726
748
|
},
|
|
727
749
|
max: {
|
|
728
750
|
name: 'max',
|
|
729
|
-
type:
|
|
751
|
+
type: AggregatedFields[collection.collection],
|
|
730
752
|
},
|
|
731
|
-
}
|
|
753
|
+
});
|
|
754
|
+
}
|
|
755
|
+
AggregatedFunctions[collection.collection] = schemaComposer.createObjectTC({
|
|
756
|
+
name: `${collection.collection}_aggregated`,
|
|
757
|
+
fields: AggregateMethods[collection.collection],
|
|
732
758
|
});
|
|
733
759
|
ReadCollectionTypes[collection.collection].addResolver({
|
|
734
760
|
name: collection.collection,
|
|
@@ -1228,7 +1254,7 @@ class GraphQLService {
|
|
|
1228
1254
|
if (!query.deep)
|
|
1229
1255
|
query.deep = {};
|
|
1230
1256
|
const args = this.parseArgs(selection.arguments, variableValues);
|
|
1231
|
-
(0, lodash_1.set)(query.deep, current, (0, lodash_1.merge)((0, lodash_1.get)(query.deep, current), (0, lodash_1.mapKeys)((0, sanitize_query_1.sanitizeQuery)(args, this.accountability), (value, key) => `_${key}`)));
|
|
1257
|
+
(0, lodash_1.set)(query.deep, currentAlias !== null && currentAlias !== void 0 ? currentAlias : current, (0, lodash_1.merge)((0, lodash_1.get)(query.deep, currentAlias !== null && currentAlias !== void 0 ? currentAlias : current), (0, lodash_1.mapKeys)((0, sanitize_query_1.sanitizeQuery)(args, this.accountability), (value, key) => `_${key}`)));
|
|
1232
1258
|
}
|
|
1233
1259
|
}
|
|
1234
1260
|
}
|
|
@@ -1287,8 +1313,10 @@ class GraphQLService {
|
|
|
1287
1313
|
*/
|
|
1288
1314
|
formatError(error) {
|
|
1289
1315
|
if (Array.isArray(error)) {
|
|
1316
|
+
error[0].extensions.code = error[0].code;
|
|
1290
1317
|
return new graphql_1.GraphQLError(error[0].message, undefined, undefined, undefined, undefined, error[0]);
|
|
1291
1318
|
}
|
|
1319
|
+
error.extensions.code = error.code;
|
|
1292
1320
|
return new graphql_1.GraphQLError(error.message, undefined, undefined, undefined, undefined, error);
|
|
1293
1321
|
}
|
|
1294
1322
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
+
import { Accountability, Query, SchemaOverview } from '@directus/shared/types';
|
|
2
3
|
import { Knex } from 'knex';
|
|
3
4
|
import { AbstractServiceOptions, File } from '../types';
|
|
4
|
-
import { Accountability, Query, SchemaOverview } from '@directus/shared/types';
|
|
5
5
|
export declare class ImportService {
|
|
6
6
|
knex: Knex;
|
|
7
7
|
accountability: Accountability | null;
|
|
@@ -4,24 +4,26 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.ExportService = exports.ImportService = void 0;
|
|
7
|
-
const
|
|
8
|
-
const exceptions_1 = require("../exceptions");
|
|
9
|
-
const StreamArray_1 = __importDefault(require("stream-json/streamers/StreamArray"));
|
|
10
|
-
const items_1 = require("./items");
|
|
7
|
+
const utils_1 = require("@directus/shared/utils");
|
|
11
8
|
const async_1 = require("async");
|
|
12
|
-
const destroy_1 = __importDefault(require("destroy"));
|
|
13
9
|
const csv_parser_1 = __importDefault(require("csv-parser"));
|
|
14
|
-
const
|
|
10
|
+
const destroy_1 = __importDefault(require("destroy"));
|
|
11
|
+
const fs_extra_1 = require("fs-extra");
|
|
15
12
|
const js2xmlparser_1 = require("js2xmlparser");
|
|
16
13
|
const json2csv_1 = require("json2csv");
|
|
17
|
-
const
|
|
14
|
+
const lodash_1 = require("lodash");
|
|
15
|
+
const StreamArray_1 = __importDefault(require("stream-json/streamers/StreamArray"));
|
|
16
|
+
const strip_bom_stream_1 = __importDefault(require("strip-bom-stream"));
|
|
18
17
|
const tmp_promise_1 = require("tmp-promise");
|
|
18
|
+
const database_1 = __importDefault(require("../database"));
|
|
19
19
|
const env_1 = __importDefault(require("../env"));
|
|
20
|
-
const
|
|
20
|
+
const exceptions_1 = require("../exceptions");
|
|
21
|
+
const logger_1 = __importDefault(require("../logger"));
|
|
21
22
|
const get_date_formatted_1 = require("../utils/get-date-formatted");
|
|
22
|
-
const
|
|
23
|
+
const parse_json_1 = require("../utils/parse-json");
|
|
24
|
+
const files_1 = require("./files");
|
|
25
|
+
const items_1 = require("./items");
|
|
23
26
|
const notifications_1 = require("./notifications");
|
|
24
|
-
const logger_1 = __importDefault(require("../logger"));
|
|
25
27
|
class ImportService {
|
|
26
28
|
constructor(options) {
|
|
27
29
|
this.knex = options.knex || (0, database_1.default)();
|
|
@@ -91,6 +93,7 @@ class ImportService {
|
|
|
91
93
|
});
|
|
92
94
|
return new Promise((resolve, reject) => {
|
|
93
95
|
stream
|
|
96
|
+
.pipe((0, strip_bom_stream_1.default)())
|
|
94
97
|
.pipe((0, csv_parser_1.default)())
|
|
95
98
|
.on('data', (value) => {
|
|
96
99
|
const obj = (0, lodash_1.transform)(value, (result, value, key) => {
|
|
@@ -99,7 +102,7 @@ class ImportService {
|
|
|
99
102
|
}
|
|
100
103
|
else {
|
|
101
104
|
try {
|
|
102
|
-
const parsedJson =
|
|
105
|
+
const parsedJson = (0, parse_json_1.parseJSON)(value);
|
|
103
106
|
(0, lodash_1.set)(result, key, parsedJson);
|
|
104
107
|
}
|
|
105
108
|
catch {
|
package/dist/services/items.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Accountability, PermissionsAction, Query, SchemaOverview } from '@directus/shared/types';
|
|
2
2
|
import Keyv from 'keyv';
|
|
3
|
-
import {
|
|
4
|
-
import { AbstractService, AbstractServiceOptions, Item as AnyItem,
|
|
3
|
+
import { Knex } from 'knex';
|
|
4
|
+
import { AbstractService, AbstractServiceOptions, Item as AnyItem, MutationOptions, PrimaryKey } from '../types';
|
|
5
5
|
export declare type QueryOptions = {
|
|
6
6
|
stripNonRequested?: boolean;
|
|
7
7
|
permissionsAction?: PermissionsAction;
|