@directus/api 10.2.0 → 11.0.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 +4 -3
- package/dist/cli/utils/create-env/env-stub.liquid +3 -0
- package/dist/constants.d.ts +0 -1
- package/dist/constants.js +0 -1
- package/dist/controllers/files.js +19 -1
- package/dist/controllers/permissions.js +7 -4
- package/dist/controllers/translations.d.ts +2 -0
- package/dist/controllers/translations.js +149 -0
- package/dist/controllers/users.js +1 -1
- package/dist/database/migrations/20230525A-add-preview-settings.d.ts +3 -0
- package/dist/database/migrations/20230525A-add-preview-settings.js +10 -0
- package/dist/database/migrations/20230526A-migrate-translation-strings.d.ts +3 -0
- package/dist/database/migrations/20230526A-migrate-translation-strings.js +54 -0
- package/dist/database/system-data/app-access-permissions/app-access-permissions.yaml +3 -0
- package/dist/database/system-data/collections/collections.yaml +23 -0
- package/dist/database/system-data/fields/collections.yaml +16 -0
- package/dist/database/system-data/fields/settings.yaml +0 -5
- package/dist/database/system-data/fields/translations.yaml +27 -0
- package/dist/env.js +14 -0
- package/dist/exceptions/content-too-large.d.ts +4 -0
- package/dist/exceptions/content-too-large.js +6 -0
- package/dist/extensions.js +13 -11
- package/dist/flows.d.ts +1 -1
- package/dist/flows.js +20 -19
- package/dist/logger.d.ts +1 -1
- package/dist/logger.js +6 -6
- package/dist/server.js +0 -11
- package/dist/services/collections.js +8 -7
- package/dist/services/fields.js +4 -4
- package/dist/services/files.d.ts +2 -2
- package/dist/services/files.js +4 -9
- package/dist/services/graphql/index.js +0 -46
- package/dist/services/index.d.ts +1 -0
- package/dist/services/index.js +1 -0
- package/dist/services/items.js +10 -9
- package/dist/services/revisions.d.ts +6 -1
- package/dist/services/revisions.js +24 -0
- package/dist/services/server.js +0 -18
- package/dist/services/specifications.d.ts +2 -2
- package/dist/services/specifications.js +6 -5
- package/dist/services/translations.d.ts +10 -0
- package/dist/services/translations.js +36 -0
- package/dist/synchronization.d.ts +7 -0
- package/dist/synchronization.js +120 -0
- package/dist/types/events.d.ts +2 -2
- package/dist/utils/apply-query.d.ts +9 -2
- package/dist/utils/apply-query.js +41 -14
- package/dist/utils/md.js +1 -1
- package/dist/utils/redact.d.ts +11 -0
- package/dist/utils/redact.js +75 -0
- package/dist/utils/schedule.d.ts +5 -0
- package/dist/utils/schedule.js +27 -0
- package/dist/utils/should-clear-cache.d.ts +10 -0
- package/dist/utils/should-clear-cache.js +18 -0
- package/dist/utils/should-skip-cache.js +18 -2
- package/package.json +49 -53
- package/dist/utils/get-os-info.d.ts +0 -9
- package/dist/utils/get-os-info.js +0 -40
package/dist/extensions.js
CHANGED
|
@@ -8,7 +8,6 @@ import virtualDefault from '@rollup/plugin-virtual';
|
|
|
8
8
|
import chokidar, { FSWatcher } from 'chokidar';
|
|
9
9
|
import express, { Router } from 'express';
|
|
10
10
|
import { clone, escapeRegExp } from 'lodash-es';
|
|
11
|
-
import { schedule, validate } from 'node-cron';
|
|
12
11
|
import { readdir } from 'node:fs/promises';
|
|
13
12
|
import { createRequire } from 'node:module';
|
|
14
13
|
import { dirname } from 'node:path';
|
|
@@ -25,6 +24,7 @@ import * as services from './services/index.js';
|
|
|
25
24
|
import getModuleDefault from './utils/get-module-default.js';
|
|
26
25
|
import { getSchema } from './utils/get-schema.js';
|
|
27
26
|
import { JobQueue } from './utils/job-queue.js';
|
|
27
|
+
import { scheduleSynchronizedJob, validateCron } from './utils/schedule.js';
|
|
28
28
|
import { Url } from './utils/url.js';
|
|
29
29
|
// Workaround for https://github.com/rollup/plugins/issues/1329
|
|
30
30
|
const virtual = virtualDefault;
|
|
@@ -189,7 +189,7 @@ class ExtensionManager {
|
|
|
189
189
|
this.isLoaded = true;
|
|
190
190
|
}
|
|
191
191
|
async unload() {
|
|
192
|
-
this.unregisterApiExtensions();
|
|
192
|
+
await this.unregisterApiExtensions();
|
|
193
193
|
this.apiEmitter.offAll();
|
|
194
194
|
if (env['SERVE_APP']) {
|
|
195
195
|
this.appExtensions = null;
|
|
@@ -299,7 +299,7 @@ class ExtensionManager {
|
|
|
299
299
|
const hookPath = path.resolve(hook.path, hook.entrypoint);
|
|
300
300
|
const hookInstance = await import(`./${pathToRelativeUrl(hookPath, __dirname)}?t=${Date.now()}`);
|
|
301
301
|
const config = getModuleDefault(hookInstance);
|
|
302
|
-
this.registerHook(config);
|
|
302
|
+
this.registerHook(config, hook.name);
|
|
303
303
|
this.apiExtensions.push({ path: hookPath });
|
|
304
304
|
}
|
|
305
305
|
catch (error) {
|
|
@@ -353,8 +353,8 @@ class ExtensionManager {
|
|
|
353
353
|
const bundlePath = path.resolve(bundle.path, bundle.entrypoint.api);
|
|
354
354
|
const bundleInstances = await import(`./${pathToRelativeUrl(bundlePath, __dirname)}?t=${Date.now()}`);
|
|
355
355
|
const configs = getModuleDefault(bundleInstances);
|
|
356
|
-
for (const { config } of configs.hooks) {
|
|
357
|
-
this.registerHook(config);
|
|
356
|
+
for (const { config, name } of configs.hooks) {
|
|
357
|
+
this.registerHook(config, name);
|
|
358
358
|
}
|
|
359
359
|
for (const { config, name } of configs.endpoints) {
|
|
360
360
|
this.registerEndpoint(config, name);
|
|
@@ -370,7 +370,8 @@ class ExtensionManager {
|
|
|
370
370
|
}
|
|
371
371
|
}
|
|
372
372
|
}
|
|
373
|
-
registerHook(register) {
|
|
373
|
+
registerHook(register, name) {
|
|
374
|
+
let scheduleIndex = 0;
|
|
374
375
|
const registerFunctions = {
|
|
375
376
|
filter: (event, handler) => {
|
|
376
377
|
emitter.onFilter(event, handler);
|
|
@@ -397,8 +398,8 @@ class ExtensionManager {
|
|
|
397
398
|
});
|
|
398
399
|
},
|
|
399
400
|
schedule: (cron, handler) => {
|
|
400
|
-
if (
|
|
401
|
-
const
|
|
401
|
+
if (validateCron(cron)) {
|
|
402
|
+
const job = scheduleSynchronizedJob(`${name}:${scheduleIndex}`, cron, async () => {
|
|
402
403
|
if (this.options.schedule) {
|
|
403
404
|
try {
|
|
404
405
|
await handler();
|
|
@@ -408,9 +409,10 @@ class ExtensionManager {
|
|
|
408
409
|
}
|
|
409
410
|
}
|
|
410
411
|
});
|
|
412
|
+
scheduleIndex++;
|
|
411
413
|
this.hookEvents.push({
|
|
412
414
|
type: 'schedule',
|
|
413
|
-
|
|
415
|
+
job,
|
|
414
416
|
});
|
|
415
417
|
}
|
|
416
418
|
else {
|
|
@@ -460,7 +462,7 @@ class ExtensionManager {
|
|
|
460
462
|
const flowManager = getFlowManager();
|
|
461
463
|
flowManager.addOperation(config.id, config.handler);
|
|
462
464
|
}
|
|
463
|
-
unregisterApiExtensions() {
|
|
465
|
+
async unregisterApiExtensions() {
|
|
464
466
|
for (const event of this.hookEvents) {
|
|
465
467
|
switch (event.type) {
|
|
466
468
|
case 'filter':
|
|
@@ -473,7 +475,7 @@ class ExtensionManager {
|
|
|
473
475
|
emitter.offInit(event.name, event.handler);
|
|
474
476
|
break;
|
|
475
477
|
case 'schedule':
|
|
476
|
-
event.
|
|
478
|
+
await event.job.stop();
|
|
477
479
|
break;
|
|
478
480
|
}
|
|
479
481
|
}
|
package/dist/flows.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ declare class FlowManager {
|
|
|
15
15
|
runOperationFlow(id: string, data: unknown, context: Record<string, unknown>): Promise<unknown>;
|
|
16
16
|
runWebhookFlow(id: string, data: unknown, context: Record<string, unknown>): Promise<{
|
|
17
17
|
result: unknown;
|
|
18
|
-
cacheEnabled
|
|
18
|
+
cacheEnabled?: boolean;
|
|
19
19
|
}>;
|
|
20
20
|
private load;
|
|
21
21
|
private unload;
|
package/dist/flows.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
+
import { Action, REDACTED_TEXT } from '@directus/constants';
|
|
1
2
|
import * as sharedExceptions from '@directus/exceptions';
|
|
2
|
-
import { Action } from '@directus/constants';
|
|
3
3
|
import { applyOptionsData, isValidJSON, parseJSON, toArray } from '@directus/utils';
|
|
4
|
-
import fastRedact from 'fast-redact';
|
|
5
4
|
import { omit, pick } from 'lodash-es';
|
|
6
5
|
import { get } from 'micromustache';
|
|
7
|
-
import { schedule, validate } from 'node-cron';
|
|
8
6
|
import getDatabase from './database/index.js';
|
|
9
7
|
import emitter from './emitter.js';
|
|
10
8
|
import env from './env.js';
|
|
@@ -12,20 +10,17 @@ import * as exceptions from './exceptions/index.js';
|
|
|
12
10
|
import logger from './logger.js';
|
|
13
11
|
import { getMessenger } from './messenger.js';
|
|
14
12
|
import { ActivityService } from './services/activity.js';
|
|
15
|
-
import * as services from './services/index.js';
|
|
16
13
|
import { FlowsService } from './services/flows.js';
|
|
14
|
+
import * as services from './services/index.js';
|
|
17
15
|
import { RevisionsService } from './services/revisions.js';
|
|
18
16
|
import { constructFlowTree } from './utils/construct-flow-tree.js';
|
|
19
17
|
import { getSchema } from './utils/get-schema.js';
|
|
20
18
|
import { JobQueue } from './utils/job-queue.js';
|
|
21
19
|
import { mapValuesDeep } from './utils/map-values-deep.js';
|
|
20
|
+
import { redact } from './utils/redact.js';
|
|
22
21
|
import { sanitizeError } from './utils/sanitize-error.js';
|
|
22
|
+
import { scheduleSynchronizedJob, validateCron } from './utils/schedule.js';
|
|
23
23
|
let flowManager;
|
|
24
|
-
const redactLogs = fastRedact({
|
|
25
|
-
censor: '--redacted--',
|
|
26
|
-
paths: ['*.headers.authorization', '*.access_token', '*.headers.cookie'],
|
|
27
|
-
serialize: false,
|
|
28
|
-
});
|
|
29
24
|
export function getFlowManager() {
|
|
30
25
|
if (flowManager) {
|
|
31
26
|
return flowManager;
|
|
@@ -147,8 +142,8 @@ class FlowManager {
|
|
|
147
142
|
}
|
|
148
143
|
}
|
|
149
144
|
else if (flow.trigger === 'schedule') {
|
|
150
|
-
if (
|
|
151
|
-
const
|
|
145
|
+
if (validateCron(flow.options['cron'])) {
|
|
146
|
+
const job = scheduleSynchronizedJob(flow.id, flow.options['cron'], async () => {
|
|
152
147
|
try {
|
|
153
148
|
await this.executeFlow(flow);
|
|
154
149
|
}
|
|
@@ -156,7 +151,7 @@ class FlowManager {
|
|
|
156
151
|
logger.error(error);
|
|
157
152
|
}
|
|
158
153
|
});
|
|
159
|
-
this.triggerHandlers.push({ id: flow.id, events: [{ type: flow.trigger,
|
|
154
|
+
this.triggerHandlers.push({ id: flow.id, events: [{ type: flow.trigger, job }] });
|
|
160
155
|
}
|
|
161
156
|
else {
|
|
162
157
|
logger.warn(`Couldn't register cron trigger. Provided cron is invalid: ${flow.options['cron']}`);
|
|
@@ -186,7 +181,7 @@ class FlowManager {
|
|
|
186
181
|
this.webhookFlowHandlers[`${method}-${flow.id}`] = handler;
|
|
187
182
|
}
|
|
188
183
|
else if (flow.trigger === 'manual') {
|
|
189
|
-
const handler = (data, context) => {
|
|
184
|
+
const handler = async (data, context) => {
|
|
190
185
|
const enabledCollections = flow.options?.['collections'] ?? [];
|
|
191
186
|
const targetCollection = data?.['body'].collection;
|
|
192
187
|
if (!targetCollection) {
|
|
@@ -203,10 +198,10 @@ class FlowManager {
|
|
|
203
198
|
}
|
|
204
199
|
if (flow.options['async']) {
|
|
205
200
|
this.executeFlow(flow, data, context);
|
|
206
|
-
return undefined;
|
|
201
|
+
return { result: undefined };
|
|
207
202
|
}
|
|
208
203
|
else {
|
|
209
|
-
return this.executeFlow(flow, data, context);
|
|
204
|
+
return { result: await this.executeFlow(flow, data, context) };
|
|
210
205
|
}
|
|
211
206
|
};
|
|
212
207
|
// Default return to $last for manual
|
|
@@ -218,7 +213,7 @@ class FlowManager {
|
|
|
218
213
|
}
|
|
219
214
|
async unload() {
|
|
220
215
|
for (const trigger of this.triggerHandlers) {
|
|
221
|
-
trigger.events
|
|
216
|
+
for (const event of trigger.events) {
|
|
222
217
|
switch (event.type) {
|
|
223
218
|
case 'filter':
|
|
224
219
|
emitter.offFilter(event.name, event.handler);
|
|
@@ -227,10 +222,10 @@ class FlowManager {
|
|
|
227
222
|
emitter.offAction(event.name, event.handler);
|
|
228
223
|
break;
|
|
229
224
|
case 'schedule':
|
|
230
|
-
event.
|
|
225
|
+
await event.job.stop();
|
|
231
226
|
break;
|
|
232
227
|
}
|
|
233
|
-
}
|
|
228
|
+
}
|
|
234
229
|
}
|
|
235
230
|
this.triggerHandlers = [];
|
|
236
231
|
this.operationFlowHandlers = {};
|
|
@@ -283,7 +278,13 @@ class FlowManager {
|
|
|
283
278
|
item: flow.id,
|
|
284
279
|
data: {
|
|
285
280
|
steps: steps,
|
|
286
|
-
data:
|
|
281
|
+
data: redact(omit(keyedData, '$accountability.permissions'), // Permissions is a ton of data, and is just a copy of what's in the directus_permissions table
|
|
282
|
+
[
|
|
283
|
+
['**', 'headers', 'authorization'],
|
|
284
|
+
['**', 'headers', 'cookie'],
|
|
285
|
+
['**', 'query', 'access_token'],
|
|
286
|
+
['**', 'payload', 'password'],
|
|
287
|
+
], REDACTED_TEXT),
|
|
287
288
|
},
|
|
288
289
|
});
|
|
289
290
|
}
|
package/dist/logger.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference types="qs" />
|
|
2
|
-
import type { LoggerOptions } from 'pino';
|
|
3
2
|
import type { RequestHandler } from 'express';
|
|
3
|
+
import type { LoggerOptions } from 'pino';
|
|
4
4
|
export declare const httpLoggerOptions: LoggerOptions;
|
|
5
5
|
declare const logger: import("pino").Logger<LoggerOptions & Record<string, any>>;
|
|
6
6
|
export declare const expressLogger: RequestHandler<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>;
|
package/dist/logger.js
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
|
+
import { REDACTED_TEXT } from '@directus/constants';
|
|
1
2
|
import { toArray } from '@directus/utils';
|
|
2
3
|
import { merge } from 'lodash-es';
|
|
3
4
|
import { pino } from 'pino';
|
|
4
5
|
import { pinoHttp, stdSerializers } from 'pino-http';
|
|
5
6
|
import { URL } from 'url';
|
|
6
7
|
import env from './env.js';
|
|
7
|
-
import { REDACT_TEXT } from './constants.js';
|
|
8
8
|
import { getConfigFromEnv } from './utils/get-config-from-env.js';
|
|
9
9
|
const pinoOptions = {
|
|
10
10
|
level: env['LOG_LEVEL'] || 'info',
|
|
11
11
|
redact: {
|
|
12
12
|
paths: ['req.headers.authorization', 'req.headers.cookie'],
|
|
13
|
-
censor:
|
|
13
|
+
censor: REDACTED_TEXT,
|
|
14
14
|
},
|
|
15
15
|
};
|
|
16
16
|
export const httpLoggerOptions = {
|
|
17
17
|
level: env['LOG_LEVEL'] || 'info',
|
|
18
18
|
redact: {
|
|
19
19
|
paths: ['req.headers.authorization', 'req.headers.cookie'],
|
|
20
|
-
censor:
|
|
20
|
+
censor: REDACTED_TEXT,
|
|
21
21
|
},
|
|
22
22
|
};
|
|
23
23
|
if (env['LOG_STYLE'] !== 'raw') {
|
|
@@ -48,11 +48,11 @@ if (env['LOG_STYLE'] === 'raw') {
|
|
|
48
48
|
const path = pathParts.join('.');
|
|
49
49
|
if (path === 'res.headers') {
|
|
50
50
|
if ('set-cookie' in value) {
|
|
51
|
-
value['set-cookie'] =
|
|
51
|
+
value['set-cookie'] = REDACTED_TEXT;
|
|
52
52
|
}
|
|
53
53
|
return value;
|
|
54
54
|
}
|
|
55
|
-
return
|
|
55
|
+
return REDACTED_TEXT;
|
|
56
56
|
},
|
|
57
57
|
};
|
|
58
58
|
}
|
|
@@ -99,7 +99,7 @@ export default logger;
|
|
|
99
99
|
function redactQuery(originalPath) {
|
|
100
100
|
const url = new URL(originalPath, 'http://example.com/');
|
|
101
101
|
if (url.searchParams.has('access_token')) {
|
|
102
|
-
url.searchParams.set('access_token',
|
|
102
|
+
url.searchParams.set('access_token', REDACTED_TEXT);
|
|
103
103
|
}
|
|
104
104
|
return url.pathname + url.search;
|
|
105
105
|
}
|
package/dist/server.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { isUpToDate } from '@directus/update-check';
|
|
2
1
|
import { createTerminus } from '@godaddy/terminus';
|
|
3
2
|
import * as http from 'http';
|
|
4
3
|
import * as https from 'https';
|
|
@@ -11,7 +10,6 @@ import emitter from './emitter.js';
|
|
|
11
10
|
import env from './env.js';
|
|
12
11
|
import logger from './logger.js';
|
|
13
12
|
import { getConfigFromEnv } from './utils/get-config-from-env.js';
|
|
14
|
-
import * as pkg from './utils/package.js';
|
|
15
13
|
export let SERVER_ONLINE = true;
|
|
16
14
|
export async function createServer() {
|
|
17
15
|
const server = http.createServer(await createApp());
|
|
@@ -107,15 +105,6 @@ export async function startServer() {
|
|
|
107
105
|
const port = env['PORT'];
|
|
108
106
|
server
|
|
109
107
|
.listen(port, host, () => {
|
|
110
|
-
isUpToDate(pkg.name, pkg.version)
|
|
111
|
-
.then((update) => {
|
|
112
|
-
if (update) {
|
|
113
|
-
logger.warn(`Update available: ${pkg.version} -> ${update}`);
|
|
114
|
-
}
|
|
115
|
-
})
|
|
116
|
-
.catch(() => {
|
|
117
|
-
// No need to log/warn here. The update message is only an informative nice-to-have
|
|
118
|
-
});
|
|
119
108
|
logger.info(`Server started at http://${host}:${port}`);
|
|
120
109
|
emitter.emitAction('server.start', { server }, {
|
|
121
110
|
database: getDatabase(),
|
|
@@ -12,6 +12,7 @@ import { ForbiddenException, InvalidPayloadException } from '../exceptions/index
|
|
|
12
12
|
import { FieldsService } from '../services/fields.js';
|
|
13
13
|
import { ItemsService } from '../services/items.js';
|
|
14
14
|
import { getSchema } from '../utils/get-schema.js';
|
|
15
|
+
import { shouldClearCache } from '../utils/should-clear-cache.js';
|
|
15
16
|
export class CollectionsService {
|
|
16
17
|
knex;
|
|
17
18
|
helpers;
|
|
@@ -130,7 +131,7 @@ export class CollectionsService {
|
|
|
130
131
|
return payload.collection;
|
|
131
132
|
}
|
|
132
133
|
finally {
|
|
133
|
-
if (this.cache
|
|
134
|
+
if (shouldClearCache(this.cache, opts)) {
|
|
134
135
|
await this.cache.clear();
|
|
135
136
|
}
|
|
136
137
|
if (opts?.autoPurgeSystemCache !== false) {
|
|
@@ -171,7 +172,7 @@ export class CollectionsService {
|
|
|
171
172
|
return collections;
|
|
172
173
|
}
|
|
173
174
|
finally {
|
|
174
|
-
if (this.cache
|
|
175
|
+
if (shouldClearCache(this.cache, opts)) {
|
|
175
176
|
await this.cache.clear();
|
|
176
177
|
}
|
|
177
178
|
if (opts?.autoPurgeSystemCache !== false) {
|
|
@@ -315,7 +316,7 @@ export class CollectionsService {
|
|
|
315
316
|
return collectionKey;
|
|
316
317
|
}
|
|
317
318
|
finally {
|
|
318
|
-
if (this.cache
|
|
319
|
+
if (shouldClearCache(this.cache, opts)) {
|
|
319
320
|
await this.cache.clear();
|
|
320
321
|
}
|
|
321
322
|
if (opts?.autoPurgeSystemCache !== false) {
|
|
@@ -363,7 +364,7 @@ export class CollectionsService {
|
|
|
363
364
|
});
|
|
364
365
|
}
|
|
365
366
|
finally {
|
|
366
|
-
if (this.cache
|
|
367
|
+
if (shouldClearCache(this.cache, opts)) {
|
|
367
368
|
await this.cache.clear();
|
|
368
369
|
}
|
|
369
370
|
if (opts?.autoPurgeSystemCache !== false) {
|
|
@@ -405,7 +406,7 @@ export class CollectionsService {
|
|
|
405
406
|
return collectionKeys;
|
|
406
407
|
}
|
|
407
408
|
finally {
|
|
408
|
-
if (this.cache
|
|
409
|
+
if (shouldClearCache(this.cache, opts)) {
|
|
409
410
|
await this.cache.clear();
|
|
410
411
|
}
|
|
411
412
|
if (opts?.autoPurgeSystemCache !== false) {
|
|
@@ -510,7 +511,7 @@ export class CollectionsService {
|
|
|
510
511
|
return collectionKey;
|
|
511
512
|
}
|
|
512
513
|
finally {
|
|
513
|
-
if (this.cache
|
|
514
|
+
if (shouldClearCache(this.cache, opts)) {
|
|
514
515
|
await this.cache.clear();
|
|
515
516
|
}
|
|
516
517
|
if (opts?.autoPurgeSystemCache !== false) {
|
|
@@ -551,7 +552,7 @@ export class CollectionsService {
|
|
|
551
552
|
return collectionKeys;
|
|
552
553
|
}
|
|
553
554
|
finally {
|
|
554
|
-
if (this.cache
|
|
555
|
+
if (shouldClearCache(this.cache, opts)) {
|
|
555
556
|
await this.cache.clear();
|
|
556
557
|
}
|
|
557
558
|
if (opts?.autoPurgeSystemCache !== false) {
|
package/dist/services/fields.js
CHANGED
|
@@ -8,7 +8,6 @@ import { getHelpers } from '../database/helpers/index.js';
|
|
|
8
8
|
import getDatabase, { getSchemaInspector } from '../database/index.js';
|
|
9
9
|
import { systemFieldRows } from '../database/system-data/fields/index.js';
|
|
10
10
|
import emitter from '../emitter.js';
|
|
11
|
-
import env from '../env.js';
|
|
12
11
|
import { translateDatabaseError } from '../exceptions/database/translate.js';
|
|
13
12
|
import { ForbiddenException, InvalidPayloadException } from '../exceptions/index.js';
|
|
14
13
|
import { ItemsService } from '../services/items.js';
|
|
@@ -17,6 +16,7 @@ import getDefaultValue from '../utils/get-default-value.js';
|
|
|
17
16
|
import getLocalType from '../utils/get-local-type.js';
|
|
18
17
|
import { getSchema } from '../utils/get-schema.js';
|
|
19
18
|
import { sanitizeColumn } from '../utils/sanitize-schema.js';
|
|
19
|
+
import { shouldClearCache } from '../utils/should-clear-cache.js';
|
|
20
20
|
import { RelationsService } from './relations.js';
|
|
21
21
|
export class FieldsService {
|
|
22
22
|
knex;
|
|
@@ -270,7 +270,7 @@ export class FieldsService {
|
|
|
270
270
|
if (runPostColumnChange) {
|
|
271
271
|
await this.helpers.schema.postColumnChange();
|
|
272
272
|
}
|
|
273
|
-
if (this.cache
|
|
273
|
+
if (shouldClearCache(this.cache, opts)) {
|
|
274
274
|
await this.cache.clear();
|
|
275
275
|
}
|
|
276
276
|
if (opts?.autoPurgeSystemCache !== false) {
|
|
@@ -367,7 +367,7 @@ export class FieldsService {
|
|
|
367
367
|
if (runPostColumnChange) {
|
|
368
368
|
await this.helpers.schema.postColumnChange();
|
|
369
369
|
}
|
|
370
|
-
if (this.cache
|
|
370
|
+
if (shouldClearCache(this.cache, opts)) {
|
|
371
371
|
await this.cache.clear();
|
|
372
372
|
}
|
|
373
373
|
if (opts?.autoPurgeSystemCache !== false) {
|
|
@@ -497,7 +497,7 @@ export class FieldsService {
|
|
|
497
497
|
if (runPostColumnChange) {
|
|
498
498
|
await this.helpers.schema.postColumnChange();
|
|
499
499
|
}
|
|
500
|
-
if (this.cache
|
|
500
|
+
if (shouldClearCache(this.cache, opts)) {
|
|
501
501
|
await this.cache.clear();
|
|
502
502
|
}
|
|
503
503
|
if (opts?.autoPurgeSystemCache !== false) {
|
package/dist/services/files.d.ts
CHANGED
|
@@ -26,9 +26,9 @@ export declare class FilesService extends ItemsService {
|
|
|
26
26
|
/**
|
|
27
27
|
* Delete a file
|
|
28
28
|
*/
|
|
29
|
-
deleteOne(key: PrimaryKey
|
|
29
|
+
deleteOne(key: PrimaryKey): Promise<PrimaryKey>;
|
|
30
30
|
/**
|
|
31
31
|
* Delete multiple files
|
|
32
32
|
*/
|
|
33
|
-
deleteMany(keys: PrimaryKey[]
|
|
33
|
+
deleteMany(keys: PrimaryKey[]): Promise<PrimaryKey[]>;
|
|
34
34
|
}
|
package/dist/services/files.js
CHANGED
|
@@ -66,6 +66,7 @@ export class FilesService extends ItemsService {
|
|
|
66
66
|
catch (err) {
|
|
67
67
|
logger.warn(`Couldn't save file ${payload.filename_disk}`);
|
|
68
68
|
logger.warn(err);
|
|
69
|
+
await this.deleteOne(primaryKey);
|
|
69
70
|
throw new ServiceUnavailableException(`Couldn't save file ${payload.filename_disk}`, { service: 'files' });
|
|
70
71
|
}
|
|
71
72
|
const { size } = await storage.location(data.storage).stat(payload.filename_disk);
|
|
@@ -87,9 +88,6 @@ export class FilesService extends ItemsService {
|
|
|
87
88
|
schema: this.schema,
|
|
88
89
|
});
|
|
89
90
|
await sudoService.updateOne(primaryKey, payload, { emitEvents: false });
|
|
90
|
-
if (this.cache && env['CACHE_AUTO_PURGE'] && opts?.autoPurgeCache !== false) {
|
|
91
|
-
await this.cache.clear();
|
|
92
|
-
}
|
|
93
91
|
if (opts?.emitEvents !== false) {
|
|
94
92
|
emitter.emitAction('files.upload', {
|
|
95
93
|
payload,
|
|
@@ -244,14 +242,14 @@ export class FilesService extends ItemsService {
|
|
|
244
242
|
/**
|
|
245
243
|
* Delete a file
|
|
246
244
|
*/
|
|
247
|
-
async deleteOne(key
|
|
248
|
-
await this.deleteMany([key]
|
|
245
|
+
async deleteOne(key) {
|
|
246
|
+
await this.deleteMany([key]);
|
|
249
247
|
return key;
|
|
250
248
|
}
|
|
251
249
|
/**
|
|
252
250
|
* Delete multiple files
|
|
253
251
|
*/
|
|
254
|
-
async deleteMany(keys
|
|
252
|
+
async deleteMany(keys) {
|
|
255
253
|
const storage = await getStorage();
|
|
256
254
|
const files = await super.readMany(keys, { fields: ['id', 'storage'], limit: -1 });
|
|
257
255
|
if (!files) {
|
|
@@ -265,9 +263,6 @@ export class FilesService extends ItemsService {
|
|
|
265
263
|
await disk.delete(filepath);
|
|
266
264
|
}
|
|
267
265
|
}
|
|
268
|
-
if (this.cache && env['CACHE_AUTO_PURGE'] && opts?.autoPurgeCache !== false) {
|
|
269
|
-
await this.cache.clear();
|
|
270
|
-
}
|
|
271
266
|
return keys;
|
|
272
267
|
}
|
|
273
268
|
}
|
|
@@ -1550,52 +1550,6 @@ export class GraphQLService {
|
|
|
1550
1550
|
},
|
|
1551
1551
|
});
|
|
1552
1552
|
}
|
|
1553
|
-
if (this.accountability?.admin === true) {
|
|
1554
|
-
ServerInfo.addFields({
|
|
1555
|
-
directus: {
|
|
1556
|
-
type: new GraphQLObjectType({
|
|
1557
|
-
name: 'server_info_directus',
|
|
1558
|
-
fields: {
|
|
1559
|
-
version: {
|
|
1560
|
-
type: GraphQLString,
|
|
1561
|
-
},
|
|
1562
|
-
},
|
|
1563
|
-
}),
|
|
1564
|
-
},
|
|
1565
|
-
node: {
|
|
1566
|
-
type: new GraphQLObjectType({
|
|
1567
|
-
name: 'server_info_node',
|
|
1568
|
-
fields: {
|
|
1569
|
-
version: {
|
|
1570
|
-
type: GraphQLString,
|
|
1571
|
-
},
|
|
1572
|
-
uptime: {
|
|
1573
|
-
type: GraphQLInt,
|
|
1574
|
-
},
|
|
1575
|
-
},
|
|
1576
|
-
}),
|
|
1577
|
-
},
|
|
1578
|
-
os: {
|
|
1579
|
-
type: new GraphQLObjectType({
|
|
1580
|
-
name: 'server_info_os',
|
|
1581
|
-
fields: {
|
|
1582
|
-
type: {
|
|
1583
|
-
type: GraphQLString,
|
|
1584
|
-
},
|
|
1585
|
-
version: {
|
|
1586
|
-
type: GraphQLString,
|
|
1587
|
-
},
|
|
1588
|
-
uptime: {
|
|
1589
|
-
type: GraphQLInt,
|
|
1590
|
-
},
|
|
1591
|
-
totalmem: {
|
|
1592
|
-
type: GraphQLInt,
|
|
1593
|
-
},
|
|
1594
|
-
},
|
|
1595
|
-
}),
|
|
1596
|
-
},
|
|
1597
|
-
});
|
|
1598
|
-
}
|
|
1599
1553
|
/** Globally available query */
|
|
1600
1554
|
schemaComposer.Query.addFields({
|
|
1601
1555
|
extensions: {
|
package/dist/services/index.d.ts
CHANGED
|
@@ -28,6 +28,7 @@ export * from './settings.js';
|
|
|
28
28
|
export * from './shares.js';
|
|
29
29
|
export * from './specifications.js';
|
|
30
30
|
export * from './tfa.js';
|
|
31
|
+
export * from './translations.js';
|
|
31
32
|
export * from './users.js';
|
|
32
33
|
export * from './utils.js';
|
|
33
34
|
export * from './webhooks.js';
|
package/dist/services/index.js
CHANGED
|
@@ -28,6 +28,7 @@ export * from './settings.js';
|
|
|
28
28
|
export * from './shares.js';
|
|
29
29
|
export * from './specifications.js';
|
|
30
30
|
export * from './tfa.js';
|
|
31
|
+
export * from './translations.js';
|
|
31
32
|
export * from './users.js';
|
|
32
33
|
export * from './utils.js';
|
|
33
34
|
export * from './webhooks.js';
|
package/dist/services/items.js
CHANGED
|
@@ -9,6 +9,7 @@ import env from '../env.js';
|
|
|
9
9
|
import { translateDatabaseError } from '../exceptions/database/translate.js';
|
|
10
10
|
import { ForbiddenException, InvalidPayloadException } from '../exceptions/index.js';
|
|
11
11
|
import getASTFromQuery from '../utils/get-ast-from-query.js';
|
|
12
|
+
import { shouldClearCache } from '../utils/should-clear-cache.js';
|
|
12
13
|
import { validateKeys } from '../utils/validate-keys.js';
|
|
13
14
|
import { AuthorizationService } from './authorization.js';
|
|
14
15
|
import { PayloadService } from './payload.js';
|
|
@@ -180,7 +181,7 @@ export class ItemsService {
|
|
|
180
181
|
// Make sure to set the parent field of the child-revision rows
|
|
181
182
|
const childrenRevisions = [...revisionsM2O, ...revisionsA2O, ...revisionsO2M];
|
|
182
183
|
if (childrenRevisions.length > 0) {
|
|
183
|
-
await revisionsService.updateMany(childrenRevisions, { parent: revision }
|
|
184
|
+
await revisionsService.updateMany(childrenRevisions, { parent: revision });
|
|
184
185
|
}
|
|
185
186
|
if (opts.onRevisionCreate) {
|
|
186
187
|
opts.onRevisionCreate(revision);
|
|
@@ -220,7 +221,7 @@ export class ItemsService {
|
|
|
220
221
|
}
|
|
221
222
|
}
|
|
222
223
|
}
|
|
223
|
-
if (this.cache
|
|
224
|
+
if (shouldClearCache(this.cache, opts, this.collection)) {
|
|
224
225
|
await this.cache.clear();
|
|
225
226
|
}
|
|
226
227
|
return primaryKey;
|
|
@@ -260,7 +261,7 @@ export class ItemsService {
|
|
|
260
261
|
}
|
|
261
262
|
}
|
|
262
263
|
}
|
|
263
|
-
if (this.cache
|
|
264
|
+
if (shouldClearCache(this.cache, opts, this.collection)) {
|
|
264
265
|
await this.cache.clear();
|
|
265
266
|
}
|
|
266
267
|
return primaryKeys;
|
|
@@ -401,7 +402,7 @@ export class ItemsService {
|
|
|
401
402
|
});
|
|
402
403
|
}
|
|
403
404
|
finally {
|
|
404
|
-
if (this.cache
|
|
405
|
+
if (shouldClearCache(this.cache, opts, this.collection)) {
|
|
405
406
|
await this.cache.clear();
|
|
406
407
|
}
|
|
407
408
|
}
|
|
@@ -514,7 +515,7 @@ export class ItemsService {
|
|
|
514
515
|
data: snapshots && Array.isArray(snapshots) ? JSON.stringify(snapshots[index]) : JSON.stringify(snapshots),
|
|
515
516
|
delta: await payloadService.prepareDelta(payloadWithTypeCasting),
|
|
516
517
|
})))).filter((revision) => revision.delta);
|
|
517
|
-
const revisionIDs = await revisionsService.createMany(revisions
|
|
518
|
+
const revisionIDs = await revisionsService.createMany(revisions);
|
|
518
519
|
for (let i = 0; i < revisionIDs.length; i++) {
|
|
519
520
|
const revisionID = revisionIDs[i];
|
|
520
521
|
if (opts.onRevisionCreate) {
|
|
@@ -526,14 +527,14 @@ export class ItemsService {
|
|
|
526
527
|
// with all other revisions on the current level as regular "flat" updates, and
|
|
527
528
|
// nested revisions as children of this first "root" item.
|
|
528
529
|
if (childrenRevisions.length > 0) {
|
|
529
|
-
await revisionsService.updateMany(childrenRevisions, { parent: revisionID }
|
|
530
|
+
await revisionsService.updateMany(childrenRevisions, { parent: revisionID });
|
|
530
531
|
}
|
|
531
532
|
}
|
|
532
533
|
}
|
|
533
534
|
}
|
|
534
535
|
}
|
|
535
536
|
});
|
|
536
|
-
if (this.cache
|
|
537
|
+
if (shouldClearCache(this.cache, opts, this.collection)) {
|
|
537
538
|
await this.cache.clear();
|
|
538
539
|
}
|
|
539
540
|
if (opts.emitEvents !== false) {
|
|
@@ -610,7 +611,7 @@ export class ItemsService {
|
|
|
610
611
|
}
|
|
611
612
|
return primaryKeys;
|
|
612
613
|
});
|
|
613
|
-
if (this.cache
|
|
614
|
+
if (shouldClearCache(this.cache, opts, this.collection)) {
|
|
614
615
|
await this.cache.clear();
|
|
615
616
|
}
|
|
616
617
|
return primaryKeys;
|
|
@@ -683,7 +684,7 @@ export class ItemsService {
|
|
|
683
684
|
})), { bypassLimits: true });
|
|
684
685
|
}
|
|
685
686
|
});
|
|
686
|
-
if (this.cache
|
|
687
|
+
if (shouldClearCache(this.cache, opts, this.collection)) {
|
|
687
688
|
await this.cache.clear();
|
|
688
689
|
}
|
|
689
690
|
if (opts.emitEvents !== false) {
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
import type { AbstractServiceOptions, PrimaryKey } from '../types/index.js';
|
|
1
|
+
import type { AbstractServiceOptions, Item, MutationOptions, PrimaryKey } from '../types/index.js';
|
|
2
2
|
import { ItemsService } from './items.js';
|
|
3
3
|
export declare class RevisionsService extends ItemsService {
|
|
4
4
|
constructor(options: AbstractServiceOptions);
|
|
5
5
|
revert(pk: PrimaryKey): Promise<void>;
|
|
6
|
+
private setDefaultOptions;
|
|
7
|
+
createOne(data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
|
|
8
|
+
createMany(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
9
|
+
updateOne(key: PrimaryKey, data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
|
|
10
|
+
updateMany(keys: PrimaryKey[], data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
6
11
|
}
|