directus 9.21.2 → 9.22.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 +9 -5
- package/dist/app.test.d.ts +1 -0
- package/dist/cli/commands/bootstrap/index.js +2 -2
- package/dist/cli/commands/security/secret.js +2 -2
- package/dist/cli/utils/create-env/env-stub.liquid +3 -3
- package/dist/cli/utils/create-env/index.js +5 -8
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +2 -1
- package/dist/controllers/assets.js +9 -7
- package/dist/controllers/files.js +2 -1
- package/dist/controllers/utils.js +2 -2
- package/dist/database/helpers/fn/dialects/mssql.js +2 -1
- package/dist/database/helpers/fn/dialects/mysql.js +2 -1
- package/dist/database/helpers/fn/dialects/oracle.js +2 -1
- package/dist/database/helpers/fn/dialects/postgres.js +2 -1
- package/dist/database/helpers/fn/dialects/sqlite.js +2 -1
- package/dist/database/helpers/fn/types.d.ts +1 -0
- package/dist/database/helpers/fn/types.js +5 -4
- package/dist/database/helpers/index.d.ts +1 -1
- package/dist/database/helpers/schema/dialects/mssql.d.ts +6 -0
- package/dist/database/helpers/schema/dialects/mssql.js +14 -0
- package/dist/database/helpers/schema/dialects/mysql.d.ts +5 -0
- package/dist/database/helpers/schema/dialects/mysql.js +19 -0
- package/dist/database/helpers/schema/dialects/oracle.d.ts +1 -0
- package/dist/database/helpers/schema/dialects/oracle.js +3 -0
- package/dist/database/helpers/schema/index.d.ts +2 -2
- package/dist/database/helpers/schema/index.js +4 -4
- package/dist/database/helpers/schema/types.d.ts +5 -0
- package/dist/database/helpers/schema/types.js +13 -0
- package/dist/database/index.d.ts +6 -0
- package/dist/database/index.js +20 -1
- package/dist/database/migrations/20211007A-update-presets.js +2 -2
- package/dist/database/migrations/run.js +7 -31
- package/dist/database/run-ast.js +132 -6
- package/dist/database/system-data/fields/index.js +2 -1
- package/dist/env.js +3 -2
- package/dist/exceptions/range-not-satisfiable.d.ts +1 -1
- package/dist/extensions.d.ts +7 -1
- package/dist/extensions.js +41 -15
- package/dist/logger.js +27 -1
- package/dist/operations/request/index.d.ts +1 -2
- package/dist/operations/request/index.js +2 -2
- package/dist/services/assets.d.ts +4 -3
- package/dist/services/assets.js +13 -11
- package/dist/services/authentication.js +4 -3
- package/dist/services/authorization.js +1 -1
- package/dist/services/files.d.ts +4 -3
- package/dist/services/files.js +92 -68
- package/dist/services/flows.d.ts +0 -2
- package/dist/services/flows.js +0 -14
- package/dist/services/flows.test.d.ts +1 -0
- package/dist/services/graphql/index.d.ts +5 -1
- package/dist/services/graphql/index.js +29 -31
- package/dist/services/import-export.d.ts +4 -3
- package/dist/services/items.js +7 -1
- package/dist/services/meta.js +2 -2
- package/dist/services/operations.d.ts +0 -2
- package/dist/services/operations.js +0 -12
- package/dist/services/operations.test.d.ts +1 -0
- package/dist/services/permissions.d.ts +0 -5
- package/dist/services/permissions.js +0 -25
- package/dist/services/permissions.test.d.ts +1 -0
- package/dist/services/roles.js +0 -3
- package/dist/services/roles.test.d.ts +1 -0
- package/dist/services/server.js +8 -6
- package/dist/services/shares.js +2 -2
- package/dist/services/specifications.js +12 -1
- package/dist/services/webhooks.d.ts +0 -2
- package/dist/services/webhooks.js +0 -10
- package/dist/services/webhooks.test.d.ts +1 -0
- package/dist/storage/get-storage-driver.d.ts +3 -0
- package/dist/storage/get-storage-driver.js +20 -0
- package/dist/storage/get-storage-driver.test.d.ts +1 -0
- package/dist/storage/index.d.ts +5 -0
- package/dist/storage/index.js +20 -0
- package/dist/storage/index.test.d.ts +1 -0
- package/dist/storage/register-drivers.d.ts +2 -0
- package/dist/storage/register-drivers.js +22 -0
- package/dist/storage/register-drivers.test.d.ts +1 -0
- package/dist/storage/register-locations.d.ts +2 -0
- package/dist/storage/register-locations.js +17 -0
- package/dist/storage/register-locations.test.d.ts +1 -0
- package/dist/utils/apply-query.d.ts +27 -3
- package/dist/utils/apply-query.js +180 -127
- package/dist/utils/dynamic-import.d.ts +1 -0
- package/dist/utils/dynamic-import.js +7 -0
- package/dist/utils/get-collection-from-alias.d.ts +6 -0
- package/dist/utils/get-collection-from-alias.js +15 -0
- package/dist/utils/get-collection-from-alias.test.d.ts +1 -0
- package/dist/utils/get-column-path.d.ts +14 -8
- package/dist/utils/get-column-path.js +24 -7
- package/dist/utils/get-column.d.ts +8 -1
- package/dist/utils/get-column.js +10 -3
- package/dist/utils/get-config-from-env.js +3 -2
- package/dist/utils/get-default-value.d.ts +1 -1
- package/dist/utils/parse-image-metadata.d.ts +3 -0
- package/dist/utils/parse-image-metadata.js +73 -0
- package/dist/utils/track.js +2 -2
- package/dist/utils/validate-env.js +3 -2
- package/dist/webhooks.js +2 -2
- package/package.json +17 -11
- package/dist/storage.d.ts +0 -3
- package/dist/storage.js +0 -61
|
@@ -8,7 +8,6 @@ const types_1 = require("@directus/shared/types");
|
|
|
8
8
|
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
|
9
9
|
const lodash_1 = require("lodash");
|
|
10
10
|
const ms_1 = __importDefault(require("ms"));
|
|
11
|
-
const nanoid_1 = require("nanoid");
|
|
12
11
|
const perf_hooks_1 = require("perf_hooks");
|
|
13
12
|
const auth_1 = require("../auth");
|
|
14
13
|
const constants_1 = require("../constants");
|
|
@@ -37,6 +36,7 @@ class AuthenticationService {
|
|
|
37
36
|
*/
|
|
38
37
|
async login(providerName = constants_1.DEFAULT_AUTH_PROVIDER, payload, otp) {
|
|
39
38
|
var _a, _b, _c;
|
|
39
|
+
const { nanoid } = await import('nanoid');
|
|
40
40
|
const STALL_TIME = env_1.default.LOGIN_STALL_TIME;
|
|
41
41
|
const timeStart = perf_hooks_1.performance.now();
|
|
42
42
|
const provider = (0, auth_1.getAuthProvider)(providerName);
|
|
@@ -151,7 +151,7 @@ class AuthenticationService {
|
|
|
151
151
|
expiresIn: env_1.default.ACCESS_TOKEN_TTL,
|
|
152
152
|
issuer: 'directus',
|
|
153
153
|
});
|
|
154
|
-
const refreshToken =
|
|
154
|
+
const refreshToken = nanoid(64);
|
|
155
155
|
const refreshTokenExpiration = new Date(Date.now() + (0, ms_1.default)(env_1.default.REFRESH_TOKEN_TTL));
|
|
156
156
|
await this.knex('directus_sessions').insert({
|
|
157
157
|
token: refreshToken,
|
|
@@ -187,6 +187,7 @@ class AuthenticationService {
|
|
|
187
187
|
};
|
|
188
188
|
}
|
|
189
189
|
async refresh(refreshToken) {
|
|
190
|
+
const { nanoid } = await import('nanoid');
|
|
190
191
|
if (!refreshToken) {
|
|
191
192
|
throw new exceptions_1.InvalidCredentialsException();
|
|
192
193
|
}
|
|
@@ -280,7 +281,7 @@ class AuthenticationService {
|
|
|
280
281
|
expiresIn: env_1.default.ACCESS_TOKEN_TTL,
|
|
281
282
|
issuer: 'directus',
|
|
282
283
|
});
|
|
283
|
-
const newRefreshToken =
|
|
284
|
+
const newRefreshToken = nanoid(64);
|
|
284
285
|
const refreshTokenExpiration = new Date(Date.now() + (0, ms_1.default)(env_1.default.REFRESH_TOKEN_TTL));
|
|
285
286
|
await this.knex('directus_sessions')
|
|
286
287
|
.update({
|
|
@@ -178,7 +178,7 @@ class AuthorizationService {
|
|
|
178
178
|
(result[relation.collection] || (result[relation.collection] = new Set())).add(relation.field);
|
|
179
179
|
}
|
|
180
180
|
}
|
|
181
|
-
//
|
|
181
|
+
// a2o filter in the form of `item:collection`
|
|
182
182
|
else if (filterKey.includes(':')) {
|
|
183
183
|
const [field, collectionScope] = filterKey.split(':');
|
|
184
184
|
if (collection) {
|
package/dist/services/files.d.ts
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
-
import {
|
|
2
|
+
import type { Readable } from 'node:stream';
|
|
3
|
+
import { AbstractServiceOptions, File, Metadata, MutationOptions, PrimaryKey } from '../types';
|
|
3
4
|
import { ItemsService } from './items';
|
|
4
5
|
export declare class FilesService extends ItemsService {
|
|
5
6
|
constructor(options: AbstractServiceOptions);
|
|
6
7
|
/**
|
|
7
8
|
* Upload a single new file to the configured storage adapter
|
|
8
9
|
*/
|
|
9
|
-
uploadOne(stream:
|
|
10
|
+
uploadOne(stream: Readable, data: Partial<File> & {
|
|
10
11
|
filename_download: string;
|
|
11
12
|
storage: string;
|
|
12
13
|
}, primaryKey?: PrimaryKey, opts?: MutationOptions): Promise<PrimaryKey>;
|
|
13
14
|
/**
|
|
14
15
|
* Extract metadata from a buffer's content
|
|
15
16
|
*/
|
|
16
|
-
getMetadata(
|
|
17
|
+
getMetadata(stream: Readable, allowList?: any): Promise<Metadata>;
|
|
17
18
|
/**
|
|
18
19
|
* Import a single file from an external URL
|
|
19
20
|
*/
|
package/dist/services/files.js
CHANGED
|
@@ -27,26 +27,29 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
27
27
|
};
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
29
|
exports.FilesService = void 0;
|
|
30
|
-
const
|
|
31
|
-
const
|
|
32
|
-
const
|
|
30
|
+
const utils_1 = require("@directus/shared/utils");
|
|
31
|
+
const dns_1 = require("dns");
|
|
32
|
+
const encodeurl_1 = __importDefault(require("encodeurl"));
|
|
33
|
+
const exif_reader_1 = __importDefault(require("exif-reader"));
|
|
34
|
+
const icc_1 = require("icc");
|
|
33
35
|
const lodash_1 = require("lodash");
|
|
34
36
|
const mime_types_1 = require("mime-types");
|
|
37
|
+
const net_1 = __importDefault(require("net"));
|
|
38
|
+
const promises_1 = require("node:stream/promises");
|
|
39
|
+
const os_1 = __importDefault(require("os"));
|
|
35
40
|
const path_1 = __importDefault(require("path"));
|
|
36
41
|
const sharp_1 = __importDefault(require("sharp"));
|
|
37
42
|
const url_1 = __importStar(require("url"));
|
|
38
43
|
const util_1 = require("util");
|
|
39
|
-
const dns_1 = require("dns");
|
|
40
44
|
const emitter_1 = __importDefault(require("../emitter"));
|
|
41
45
|
const env_1 = __importDefault(require("../env"));
|
|
42
46
|
const exceptions_1 = require("../exceptions");
|
|
43
47
|
const logger_1 = __importDefault(require("../logger"));
|
|
44
|
-
const storage_1 =
|
|
45
|
-
const
|
|
48
|
+
const storage_1 = require("../storage");
|
|
49
|
+
const parse_image_metadata_1 = require("../utils/parse-image-metadata");
|
|
46
50
|
const items_1 = require("./items");
|
|
47
|
-
|
|
48
|
-
const
|
|
49
|
-
const encodeurl_1 = __importDefault(require("encodeurl"));
|
|
51
|
+
// @ts-ignore
|
|
52
|
+
const format_title_1 = __importDefault(require("@directus/format-title"));
|
|
50
53
|
const lookupDNS = (0, util_1.promisify)(dns_1.lookup);
|
|
51
54
|
class FilesService extends items_1.ItemsService {
|
|
52
55
|
constructor(options) {
|
|
@@ -57,6 +60,7 @@ class FilesService extends items_1.ItemsService {
|
|
|
57
60
|
*/
|
|
58
61
|
async uploadOne(stream, data, primaryKey, opts) {
|
|
59
62
|
var _a, _b, _c, _d, _e, _f;
|
|
63
|
+
const storage = await (0, storage_1.getStorage)();
|
|
60
64
|
const payload = (0, lodash_1.clone)(data);
|
|
61
65
|
if ('folder' in payload === false) {
|
|
62
66
|
const settings = await this.knex.select('storage_default_folder').from('directus_settings').first();
|
|
@@ -68,9 +72,9 @@ class FilesService extends items_1.ItemsService {
|
|
|
68
72
|
await this.updateOne(primaryKey, payload, { emitEvents: false });
|
|
69
73
|
// If the file you're uploading already exists, we'll consider this upload a replace. In that case, we'll
|
|
70
74
|
// delete the previously saved file and thumbnails to ensure they're generated fresh
|
|
71
|
-
const disk =
|
|
72
|
-
for await (const
|
|
73
|
-
await disk.delete(
|
|
75
|
+
const disk = storage.location(payload.storage);
|
|
76
|
+
for await (const filepath of disk.list(String(primaryKey))) {
|
|
77
|
+
await disk.delete(filepath);
|
|
74
78
|
}
|
|
75
79
|
}
|
|
76
80
|
else {
|
|
@@ -82,18 +86,18 @@ class FilesService extends items_1.ItemsService {
|
|
|
82
86
|
payload.type = 'application/octet-stream';
|
|
83
87
|
}
|
|
84
88
|
try {
|
|
85
|
-
await
|
|
89
|
+
await storage.location(data.storage).write(payload.filename_disk, stream, payload.type);
|
|
86
90
|
}
|
|
87
91
|
catch (err) {
|
|
88
92
|
logger_1.default.warn(`Couldn't save file ${payload.filename_disk}`);
|
|
89
93
|
logger_1.default.warn(err);
|
|
90
94
|
throw new exceptions_1.ServiceUnavailableException(`Couldn't save file ${payload.filename_disk}`, { service: 'files' });
|
|
91
95
|
}
|
|
92
|
-
const { size } = await
|
|
96
|
+
const { size } = await storage.location(data.storage).stat(payload.filename_disk);
|
|
93
97
|
payload.filesize = size;
|
|
94
98
|
if (['image/jpeg', 'image/png', 'image/webp', 'image/gif', 'image/tiff'].includes(payload.type)) {
|
|
95
|
-
const
|
|
96
|
-
const { height, width, description, title, tags, metadata } = await this.getMetadata(
|
|
99
|
+
const stream = await storage.location(data.storage).read(payload.filename_disk);
|
|
100
|
+
const { height, width, description, title, tags, metadata } = await this.getMetadata(stream);
|
|
97
101
|
(_a = payload.height) !== null && _a !== void 0 ? _a : (payload.height = height);
|
|
98
102
|
(_b = payload.width) !== null && _b !== void 0 ? _b : (payload.width = width);
|
|
99
103
|
(_c = payload.description) !== null && _c !== void 0 ? _c : (payload.description = description);
|
|
@@ -127,60 +131,79 @@ class FilesService extends items_1.ItemsService {
|
|
|
127
131
|
/**
|
|
128
132
|
* Extract metadata from a buffer's content
|
|
129
133
|
*/
|
|
130
|
-
async getMetadata(
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
metadata
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
134
|
+
async getMetadata(stream, allowList = env_1.default.FILE_METADATA_ALLOW_LIST) {
|
|
135
|
+
return new Promise((resolve, reject) => {
|
|
136
|
+
(0, promises_1.pipeline)(stream, (0, sharp_1.default)().metadata(async (err, sharpMetadata) => {
|
|
137
|
+
var _a, _b, _c, _d;
|
|
138
|
+
if (err)
|
|
139
|
+
reject(err);
|
|
140
|
+
const metadata = {};
|
|
141
|
+
if (sharpMetadata.orientation && sharpMetadata.orientation >= 5) {
|
|
142
|
+
metadata.height = sharpMetadata.width;
|
|
143
|
+
metadata.width = sharpMetadata.height;
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
metadata.width = sharpMetadata.width;
|
|
147
|
+
metadata.height = sharpMetadata.height;
|
|
148
|
+
}
|
|
149
|
+
// Backward-compatible layout as it used to be with 'exifr'
|
|
150
|
+
const fullMetadata = {};
|
|
151
|
+
if (sharpMetadata.exif) {
|
|
152
|
+
const { image, thumbnail, interoperability, ...rest } = (0, exif_reader_1.default)(sharpMetadata.exif);
|
|
153
|
+
if (image) {
|
|
154
|
+
fullMetadata.ifd0 = image;
|
|
155
|
+
}
|
|
156
|
+
if (thumbnail) {
|
|
157
|
+
fullMetadata.ifd1 = thumbnail;
|
|
158
|
+
}
|
|
159
|
+
if (interoperability) {
|
|
160
|
+
fullMetadata.interop = interoperability;
|
|
161
|
+
}
|
|
162
|
+
Object.assign(fullMetadata, rest);
|
|
163
|
+
}
|
|
164
|
+
if (sharpMetadata.icc) {
|
|
165
|
+
fullMetadata.icc = (0, icc_1.parse)(sharpMetadata.icc);
|
|
166
|
+
}
|
|
167
|
+
if (sharpMetadata.iptc) {
|
|
168
|
+
fullMetadata.iptc = (0, parse_image_metadata_1.parseIptc)(sharpMetadata.iptc);
|
|
169
|
+
}
|
|
170
|
+
if (sharpMetadata.xmp) {
|
|
171
|
+
fullMetadata.xmp = (0, parse_image_metadata_1.parseXmp)(sharpMetadata.xmp);
|
|
172
|
+
}
|
|
173
|
+
if (((_a = fullMetadata === null || fullMetadata === void 0 ? void 0 : fullMetadata.iptc) === null || _a === void 0 ? void 0 : _a.Caption) && typeof fullMetadata.iptc.Caption === 'string') {
|
|
174
|
+
metadata.description = (_b = fullMetadata.iptc) === null || _b === void 0 ? void 0 : _b.Caption;
|
|
175
|
+
}
|
|
176
|
+
if (((_c = fullMetadata === null || fullMetadata === void 0 ? void 0 : fullMetadata.iptc) === null || _c === void 0 ? void 0 : _c.Headline) && typeof fullMetadata.iptc.Headline === 'string') {
|
|
177
|
+
metadata.title = fullMetadata.iptc.Headline;
|
|
178
|
+
}
|
|
179
|
+
if ((_d = fullMetadata === null || fullMetadata === void 0 ? void 0 : fullMetadata.iptc) === null || _d === void 0 ? void 0 : _d.Keywords) {
|
|
180
|
+
metadata.tags = fullMetadata.iptc.Keywords;
|
|
181
|
+
}
|
|
182
|
+
if (allowList === '*' || (allowList === null || allowList === void 0 ? void 0 : allowList[0]) === '*') {
|
|
183
|
+
metadata.metadata = fullMetadata;
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
metadata.metadata = (0, lodash_1.pick)(fullMetadata, allowList);
|
|
187
|
+
}
|
|
188
|
+
// Fix (incorrectly parsed?) values starting / ending with spaces,
|
|
189
|
+
// limited to one level and string values only
|
|
190
|
+
for (const section of Object.keys(metadata.metadata)) {
|
|
191
|
+
for (const [key, value] of Object.entries(metadata.metadata[section])) {
|
|
192
|
+
if (typeof value === 'string') {
|
|
193
|
+
metadata.metadata[section][key] = value.trim();
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
resolve(metadata);
|
|
198
|
+
}));
|
|
199
|
+
});
|
|
178
200
|
}
|
|
179
201
|
/**
|
|
180
202
|
* Import a single file from an external URL
|
|
181
203
|
*/
|
|
182
204
|
async importOne(importURL, body) {
|
|
183
205
|
var _a, _b, _c;
|
|
206
|
+
const axios = (await import('axios')).default;
|
|
184
207
|
const fileCreatePermissions = (_b = (_a = this.accountability) === null || _a === void 0 ? void 0 : _a.permissions) === null || _b === void 0 ? void 0 : _b.find((permission) => permission.collection === 'directus_files' && permission.action === 'create');
|
|
185
208
|
if (this.accountability && ((_c = this.accountability) === null || _c === void 0 ? void 0 : _c.admin) !== true && !fileCreatePermissions) {
|
|
186
209
|
throw new exceptions_1.ForbiddenException();
|
|
@@ -230,7 +253,7 @@ class FilesService extends items_1.ItemsService {
|
|
|
230
253
|
}
|
|
231
254
|
let fileResponse;
|
|
232
255
|
try {
|
|
233
|
-
fileResponse = await
|
|
256
|
+
fileResponse = await axios.get((0, encodeurl_1.default)(importURL), {
|
|
234
257
|
responseType: 'stream',
|
|
235
258
|
});
|
|
236
259
|
}
|
|
@@ -273,16 +296,17 @@ class FilesService extends items_1.ItemsService {
|
|
|
273
296
|
* Delete multiple files
|
|
274
297
|
*/
|
|
275
298
|
async deleteMany(keys, opts) {
|
|
299
|
+
const storage = await (0, storage_1.getStorage)();
|
|
276
300
|
const files = await super.readMany(keys, { fields: ['id', 'storage'], limit: -1 });
|
|
277
301
|
if (!files) {
|
|
278
302
|
throw new exceptions_1.ForbiddenException();
|
|
279
303
|
}
|
|
280
304
|
await super.deleteMany(keys);
|
|
281
305
|
for (const file of files) {
|
|
282
|
-
const disk =
|
|
306
|
+
const disk = storage.location(file.storage);
|
|
283
307
|
// Delete file + thumbnails
|
|
284
|
-
for await (const
|
|
285
|
-
await disk.delete(
|
|
308
|
+
for await (const filepath of disk.list(file.id)) {
|
|
309
|
+
await disk.delete(filepath);
|
|
286
310
|
}
|
|
287
311
|
}
|
|
288
312
|
if (this.cache && env_1.default.CACHE_AUTO_PURGE && (opts === null || opts === void 0 ? void 0 : opts.autoPurgeCache) !== false) {
|
package/dist/services/flows.d.ts
CHANGED
|
@@ -5,9 +5,7 @@ export declare class FlowsService extends ItemsService<FlowRaw> {
|
|
|
5
5
|
constructor(options: AbstractServiceOptions);
|
|
6
6
|
createOne(data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
|
|
7
7
|
createMany(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
8
|
-
updateOne(key: PrimaryKey, data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
|
|
9
8
|
updateBatch(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
10
9
|
updateMany(keys: PrimaryKey[], data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
11
|
-
deleteOne(key: PrimaryKey, opts?: MutationOptions): Promise<PrimaryKey>;
|
|
12
10
|
deleteMany(keys: PrimaryKey[], opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
13
11
|
}
|
package/dist/services/flows.js
CHANGED
|
@@ -19,12 +19,6 @@ class FlowsService extends items_1.ItemsService {
|
|
|
19
19
|
await flowManager.reload();
|
|
20
20
|
return result;
|
|
21
21
|
}
|
|
22
|
-
async updateOne(key, data, opts) {
|
|
23
|
-
const flowManager = (0, flows_1.getFlowManager)();
|
|
24
|
-
const result = await super.updateOne(key, data, opts);
|
|
25
|
-
await flowManager.reload();
|
|
26
|
-
return result;
|
|
27
|
-
}
|
|
28
22
|
async updateBatch(data, opts) {
|
|
29
23
|
const flowManager = (0, flows_1.getFlowManager)();
|
|
30
24
|
const result = await super.updateBatch(data, opts);
|
|
@@ -37,14 +31,6 @@ class FlowsService extends items_1.ItemsService {
|
|
|
37
31
|
await flowManager.reload();
|
|
38
32
|
return result;
|
|
39
33
|
}
|
|
40
|
-
async deleteOne(key, opts) {
|
|
41
|
-
const flowManager = (0, flows_1.getFlowManager)();
|
|
42
|
-
// this is to prevent foreign key constraint error on directus_operations resolve/reject during cascade deletion
|
|
43
|
-
await this.knex('directus_operations').update({ resolve: null, reject: null }).where('flow', key);
|
|
44
|
-
const result = await super.deleteOne(key, opts);
|
|
45
|
-
await flowManager.reload();
|
|
46
|
-
return result;
|
|
47
|
-
}
|
|
48
34
|
async deleteMany(keys, opts) {
|
|
49
35
|
const flowManager = (0, flows_1.getFlowManager)();
|
|
50
36
|
// this is to prevent foreign key constraint error on directus_operations resolve/reject during cascade deletion
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BaseException } from '@directus/shared/exceptions';
|
|
2
|
-
import { Accountability, Query, SchemaOverview } from '@directus/shared/types';
|
|
2
|
+
import { Accountability, Filter, Query, SchemaOverview } from '@directus/shared/types';
|
|
3
3
|
import { ArgumentNode, FormattedExecutionResult, FragmentDefinitionNode, GraphQLError, GraphQLResolveInfo, GraphQLSchema, SelectionNode } from 'graphql';
|
|
4
4
|
import { ObjectTypeComposer, SchemaComposer } from 'graphql-compose';
|
|
5
5
|
import { Knex } from 'knex';
|
|
@@ -54,6 +54,10 @@ export declare class GraphQLService {
|
|
|
54
54
|
* Resolve the aggregation query based on the requested aggregated fields
|
|
55
55
|
*/
|
|
56
56
|
getAggregateQuery(rawQuery: Query, selections: readonly SelectionNode[]): Query;
|
|
57
|
+
/**
|
|
58
|
+
* Replace functions from GraphQL format to Directus-Filter format
|
|
59
|
+
*/
|
|
60
|
+
replaceFuncs(filter?: Filter | null): null | undefined | Filter;
|
|
57
61
|
/**
|
|
58
62
|
* Convert Directus-Exception into a GraphQL format, so it can be returned by GraphQL properly.
|
|
59
63
|
*/
|
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.GraphQLService = void 0;
|
|
7
7
|
const types_1 = require("@directus/shared/types");
|
|
8
|
+
const utils_1 = require("@directus/shared/utils");
|
|
8
9
|
const argon2_1 = __importDefault(require("argon2"));
|
|
9
10
|
const graphql_1 = require("graphql");
|
|
10
11
|
const graphql_compose_1 = require("graphql-compose");
|
|
@@ -13,7 +14,6 @@ const lodash_1 = require("lodash");
|
|
|
13
14
|
const ms_1 = __importDefault(require("ms"));
|
|
14
15
|
const cache_1 = require("../../cache");
|
|
15
16
|
const constants_1 = require("../../constants");
|
|
16
|
-
const constants_2 = require("@directus/shared/constants");
|
|
17
17
|
const database_1 = __importDefault(require("../../database"));
|
|
18
18
|
const env_1 = __importDefault(require("../../env"));
|
|
19
19
|
const exceptions_1 = require("../../exceptions");
|
|
@@ -44,7 +44,7 @@ const shares_1 = require("../shares");
|
|
|
44
44
|
const specifications_1 = require("../specifications");
|
|
45
45
|
const tfa_1 = require("../tfa");
|
|
46
46
|
const users_1 = require("../users");
|
|
47
|
-
const
|
|
47
|
+
const utils_2 = require("../utils");
|
|
48
48
|
const webhooks_1 = require("../webhooks");
|
|
49
49
|
const date_1 = require("./types/date");
|
|
50
50
|
const geojson_1 = require("./types/geojson");
|
|
@@ -53,6 +53,7 @@ const void_1 = require("./types/void");
|
|
|
53
53
|
const add_path_to_validation_error_1 = require("./utils/add-path-to-validation-error");
|
|
54
54
|
const hash_1 = require("./types/hash");
|
|
55
55
|
const bigint_1 = require("./types/bigint");
|
|
56
|
+
const constants_2 = require("@directus/shared/constants");
|
|
56
57
|
const validationRules = Array.from(graphql_1.specifiedRules);
|
|
57
58
|
if (env_1.default.GRAPHQL_INTROSPECTION === false) {
|
|
58
59
|
validationRules.push(graphql_1.NoSchemaIntrospectionCustomRule);
|
|
@@ -1111,14 +1112,7 @@ class GraphQLService {
|
|
|
1111
1112
|
// Transform count(a.b.c) into a.b.count(c)
|
|
1112
1113
|
if ((_c = query.fields) === null || _c === void 0 ? void 0 : _c.length) {
|
|
1113
1114
|
for (let fieldIndex = 0; fieldIndex < query.fields.length; fieldIndex++) {
|
|
1114
|
-
|
|
1115
|
-
const functionName = query.fields[fieldIndex].split('(')[0];
|
|
1116
|
-
const columnNames = query.fields[fieldIndex].match(constants_2.REGEX_BETWEEN_PARENS)[1].split('.');
|
|
1117
|
-
if (columnNames.length > 1) {
|
|
1118
|
-
const column = columnNames.pop();
|
|
1119
|
-
query.fields[fieldIndex] = columnNames.join('.') + '.' + functionName + '(' + column + ')';
|
|
1120
|
-
}
|
|
1121
|
-
}
|
|
1115
|
+
query.fields[fieldIndex] = (0, utils_1.parseFilterFunctionPath)(query.fields[fieldIndex]);
|
|
1122
1116
|
}
|
|
1123
1117
|
}
|
|
1124
1118
|
const result = await this.read(collection, query);
|
|
@@ -1347,28 +1341,10 @@ class GraphQLService {
|
|
|
1347
1341
|
}
|
|
1348
1342
|
return (0, lodash_1.uniq)(fields);
|
|
1349
1343
|
};
|
|
1350
|
-
const replaceFuncs = (filter) => {
|
|
1351
|
-
if (!filter)
|
|
1352
|
-
return filter;
|
|
1353
|
-
return replaceFuncDeep(filter);
|
|
1354
|
-
function replaceFuncDeep(filter) {
|
|
1355
|
-
return (0, lodash_1.transform)(filter, (result, value, key) => {
|
|
1356
|
-
let currentKey = key;
|
|
1357
|
-
if (typeof key === 'string' && key.endsWith('_func')) {
|
|
1358
|
-
const functionName = Object.keys(value)[0];
|
|
1359
|
-
currentKey = `${functionName}(${currentKey.slice(0, -5)})`;
|
|
1360
|
-
result[currentKey] = Object.values(value)[0];
|
|
1361
|
-
}
|
|
1362
|
-
else {
|
|
1363
|
-
result[currentKey] =
|
|
1364
|
-
(value === null || value === void 0 ? void 0 : value.constructor) === Object || (value === null || value === void 0 ? void 0 : value.constructor) === Array ? replaceFuncDeep(value) : value;
|
|
1365
|
-
}
|
|
1366
|
-
});
|
|
1367
|
-
}
|
|
1368
|
-
};
|
|
1369
1344
|
query.alias = parseAliases(selections);
|
|
1370
1345
|
query.fields = parseFields(selections);
|
|
1371
|
-
query.filter = replaceFuncs(query.filter);
|
|
1346
|
+
query.filter = this.replaceFuncs(query.filter);
|
|
1347
|
+
query.deep = this.replaceFuncs(query.deep);
|
|
1372
1348
|
(0, validate_query_1.validateQuery)(query);
|
|
1373
1349
|
return query;
|
|
1374
1350
|
}
|
|
@@ -1393,9 +1369,31 @@ class GraphQLService {
|
|
|
1393
1369
|
return selectionNode.name.value;
|
|
1394
1370
|
})) !== null && _b !== void 0 ? _b : [];
|
|
1395
1371
|
}
|
|
1372
|
+
query.filter = this.replaceFuncs(query.filter);
|
|
1396
1373
|
(0, validate_query_1.validateQuery)(query);
|
|
1397
1374
|
return query;
|
|
1398
1375
|
}
|
|
1376
|
+
/**
|
|
1377
|
+
* Replace functions from GraphQL format to Directus-Filter format
|
|
1378
|
+
*/
|
|
1379
|
+
replaceFuncs(filter) {
|
|
1380
|
+
if (!filter)
|
|
1381
|
+
return filter;
|
|
1382
|
+
return replaceFuncDeep(filter);
|
|
1383
|
+
function replaceFuncDeep(filter) {
|
|
1384
|
+
return (0, lodash_1.transform)(filter, (result, value, key) => {
|
|
1385
|
+
const isFunctionKey = typeof key === 'string' && key.endsWith('_func') && constants_2.FUNCTIONS.includes(Object.keys(value)[0]);
|
|
1386
|
+
if (isFunctionKey) {
|
|
1387
|
+
const functionName = Object.keys(value)[0];
|
|
1388
|
+
const fieldName = key.slice(0, -5);
|
|
1389
|
+
result[`${functionName}(${fieldName})`] = Object.values(value)[0];
|
|
1390
|
+
}
|
|
1391
|
+
else {
|
|
1392
|
+
result[key] = (value === null || value === void 0 ? void 0 : value.constructor) === Object || (value === null || value === void 0 ? void 0 : value.constructor) === Array ? replaceFuncDeep(value) : value;
|
|
1393
|
+
}
|
|
1394
|
+
});
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1399
1397
|
/**
|
|
1400
1398
|
* Convert Directus-Exception into a GraphQL format, so it can be returned by GraphQL properly.
|
|
1401
1399
|
*/
|
|
@@ -1870,7 +1868,7 @@ class GraphQLService {
|
|
|
1870
1868
|
to: new graphql_1.GraphQLNonNull(graphql_1.GraphQLID),
|
|
1871
1869
|
},
|
|
1872
1870
|
resolve: async (_, args) => {
|
|
1873
|
-
const service = new
|
|
1871
|
+
const service = new utils_2.UtilsService({
|
|
1874
1872
|
accountability: this.accountability,
|
|
1875
1873
|
schema: this.schema,
|
|
1876
1874
|
});
|
|
@@ -2,14 +2,15 @@
|
|
|
2
2
|
import { Accountability, Query, SchemaOverview } from '@directus/shared/types';
|
|
3
3
|
import { Knex } from 'knex';
|
|
4
4
|
import { AbstractServiceOptions, File } from '../types';
|
|
5
|
+
import type { Readable } from 'node:stream';
|
|
5
6
|
export declare class ImportService {
|
|
6
7
|
knex: Knex;
|
|
7
8
|
accountability: Accountability | null;
|
|
8
9
|
schema: SchemaOverview;
|
|
9
10
|
constructor(options: AbstractServiceOptions);
|
|
10
|
-
import(collection: string, mimetype: string, stream:
|
|
11
|
-
importJSON(collection: string, stream:
|
|
12
|
-
importCSV(collection: string, stream:
|
|
11
|
+
import(collection: string, mimetype: string, stream: Readable): Promise<void>;
|
|
12
|
+
importJSON(collection: string, stream: Readable): Promise<void>;
|
|
13
|
+
importCSV(collection: string, stream: Readable): Promise<void>;
|
|
13
14
|
}
|
|
14
15
|
export declare class ExportService {
|
|
15
16
|
knex: Knex;
|
package/dist/services/items.js
CHANGED
|
@@ -8,6 +8,7 @@ const types_1 = require("@directus/shared/types");
|
|
|
8
8
|
const lodash_1 = require("lodash");
|
|
9
9
|
const cache_1 = require("../cache");
|
|
10
10
|
const database_1 = __importDefault(require("../database"));
|
|
11
|
+
const helpers_1 = require("../database/helpers");
|
|
11
12
|
const run_ast_1 = __importDefault(require("../database/run-ast"));
|
|
12
13
|
const emitter_1 = __importDefault(require("../emitter"));
|
|
13
14
|
const env_1 = __importDefault(require("../env"));
|
|
@@ -98,7 +99,12 @@ class ItemsService {
|
|
|
98
99
|
.returning(primaryKeyField)
|
|
99
100
|
.then((result) => result[0]);
|
|
100
101
|
const returnedKey = typeof result === 'object' ? result[primaryKeyField] : result;
|
|
101
|
-
|
|
102
|
+
if (this.schema.collections[this.collection].fields[primaryKeyField].type === 'uuid') {
|
|
103
|
+
primaryKey = (0, helpers_1.getHelpers)(trx).schema.formatUUID(primaryKey !== null && primaryKey !== void 0 ? primaryKey : returnedKey);
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
primaryKey = primaryKey !== null && primaryKey !== void 0 ? primaryKey : returnedKey;
|
|
107
|
+
}
|
|
102
108
|
}
|
|
103
109
|
catch (err) {
|
|
104
110
|
throw await (0, translate_1.translateDatabaseError)(err);
|
package/dist/services/meta.js
CHANGED
|
@@ -40,7 +40,7 @@ class MetaService {
|
|
|
40
40
|
if (!permissionsRecord)
|
|
41
41
|
throw new exceptions_1.ForbiddenException();
|
|
42
42
|
const permissions = (_d = permissionsRecord.permissions) !== null && _d !== void 0 ? _d : {};
|
|
43
|
-
(0, apply_query_1.applyFilter)(this.knex, this.schema, dbQuery, permissions, collection);
|
|
43
|
+
(0, apply_query_1.applyFilter)(this.knex, this.schema, dbQuery, permissions, collection, {});
|
|
44
44
|
}
|
|
45
45
|
const result = await dbQuery;
|
|
46
46
|
return Number((_e = result === null || result === void 0 ? void 0 : result.count) !== null && _e !== void 0 ? _e : 0);
|
|
@@ -64,7 +64,7 @@ class MetaService {
|
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
if (Object.keys(filter).length > 0) {
|
|
67
|
-
(0, apply_query_1.applyFilter)(this.knex, this.schema, dbQuery, filter, collection);
|
|
67
|
+
(0, apply_query_1.applyFilter)(this.knex, this.schema, dbQuery, filter, collection, {});
|
|
68
68
|
}
|
|
69
69
|
if (query.search) {
|
|
70
70
|
(0, apply_query_1.applySearch)(this.schema, dbQuery, query.search, collection);
|
|
@@ -5,9 +5,7 @@ export declare class OperationsService extends ItemsService<OperationRaw> {
|
|
|
5
5
|
constructor(options: AbstractServiceOptions);
|
|
6
6
|
createOne(data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
|
|
7
7
|
createMany(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
8
|
-
updateOne(key: PrimaryKey, data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
|
|
9
8
|
updateBatch(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
10
9
|
updateMany(keys: PrimaryKey[], data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
11
|
-
deleteOne(key: PrimaryKey, opts?: MutationOptions): Promise<PrimaryKey>;
|
|
12
10
|
deleteMany(keys: PrimaryKey[], opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
13
11
|
}
|
|
@@ -19,12 +19,6 @@ class OperationsService extends items_1.ItemsService {
|
|
|
19
19
|
await flowManager.reload();
|
|
20
20
|
return result;
|
|
21
21
|
}
|
|
22
|
-
async updateOne(key, data, opts) {
|
|
23
|
-
const flowManager = (0, flows_1.getFlowManager)();
|
|
24
|
-
const result = await super.updateOne(key, data, opts);
|
|
25
|
-
await flowManager.reload();
|
|
26
|
-
return result;
|
|
27
|
-
}
|
|
28
22
|
async updateBatch(data, opts) {
|
|
29
23
|
const flowManager = (0, flows_1.getFlowManager)();
|
|
30
24
|
const result = await super.updateBatch(data, opts);
|
|
@@ -37,12 +31,6 @@ class OperationsService extends items_1.ItemsService {
|
|
|
37
31
|
await flowManager.reload();
|
|
38
32
|
return result;
|
|
39
33
|
}
|
|
40
|
-
async deleteOne(key, opts) {
|
|
41
|
-
const flowManager = (0, flows_1.getFlowManager)();
|
|
42
|
-
const result = await super.deleteOne(key, opts);
|
|
43
|
-
await flowManager.reload();
|
|
44
|
-
return result;
|
|
45
|
-
}
|
|
46
34
|
async deleteMany(keys, opts) {
|
|
47
35
|
const flowManager = (0, flows_1.getFlowManager)();
|
|
48
36
|
const result = await super.deleteMany(keys, opts);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -10,13 +10,8 @@ export declare class PermissionsService extends ItemsService {
|
|
|
10
10
|
readMany(keys: PrimaryKey[], query?: Query, opts?: QueryOptions): Promise<Partial<Item>[]>;
|
|
11
11
|
createOne(data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
|
|
12
12
|
createMany(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
13
|
-
updateByQuery(query: Query, data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
14
|
-
updateOne(key: PrimaryKey, data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
|
|
15
13
|
updateBatch(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
16
14
|
updateMany(keys: PrimaryKey[], data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
17
|
-
upsertOne(payload: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
|
|
18
15
|
upsertMany(payloads: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
19
|
-
deleteByQuery(query: Query, opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
20
|
-
deleteOne(key: PrimaryKey, opts?: MutationOptions): Promise<PrimaryKey>;
|
|
21
16
|
deleteMany(keys: PrimaryKey[], opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
22
17
|
}
|