@directus/api 10.2.0 → 11.0.0

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.
Files changed (58) hide show
  1. package/dist/app.js +4 -3
  2. package/dist/cli/utils/create-env/env-stub.liquid +3 -0
  3. package/dist/constants.d.ts +0 -1
  4. package/dist/constants.js +0 -1
  5. package/dist/controllers/files.js +19 -1
  6. package/dist/controllers/permissions.js +7 -4
  7. package/dist/controllers/translations.d.ts +2 -0
  8. package/dist/controllers/translations.js +149 -0
  9. package/dist/controllers/users.js +1 -1
  10. package/dist/database/migrations/20230525A-add-preview-settings.d.ts +3 -0
  11. package/dist/database/migrations/20230525A-add-preview-settings.js +10 -0
  12. package/dist/database/migrations/20230526A-migrate-translation-strings.d.ts +3 -0
  13. package/dist/database/migrations/20230526A-migrate-translation-strings.js +54 -0
  14. package/dist/database/system-data/app-access-permissions/app-access-permissions.yaml +3 -0
  15. package/dist/database/system-data/collections/collections.yaml +23 -0
  16. package/dist/database/system-data/fields/collections.yaml +16 -0
  17. package/dist/database/system-data/fields/settings.yaml +0 -5
  18. package/dist/database/system-data/fields/translations.yaml +27 -0
  19. package/dist/env.js +14 -0
  20. package/dist/exceptions/content-too-large.d.ts +4 -0
  21. package/dist/exceptions/content-too-large.js +6 -0
  22. package/dist/extensions.js +13 -11
  23. package/dist/flows.d.ts +1 -1
  24. package/dist/flows.js +20 -19
  25. package/dist/logger.d.ts +1 -1
  26. package/dist/logger.js +6 -6
  27. package/dist/server.js +0 -11
  28. package/dist/services/collections.js +8 -7
  29. package/dist/services/fields.js +4 -4
  30. package/dist/services/files.d.ts +2 -2
  31. package/dist/services/files.js +4 -9
  32. package/dist/services/graphql/index.js +0 -46
  33. package/dist/services/index.d.ts +1 -0
  34. package/dist/services/index.js +1 -0
  35. package/dist/services/items.js +10 -9
  36. package/dist/services/revisions.d.ts +6 -1
  37. package/dist/services/revisions.js +24 -0
  38. package/dist/services/server.js +0 -18
  39. package/dist/services/specifications.d.ts +2 -2
  40. package/dist/services/specifications.js +6 -5
  41. package/dist/services/translations.d.ts +10 -0
  42. package/dist/services/translations.js +36 -0
  43. package/dist/synchronization.d.ts +7 -0
  44. package/dist/synchronization.js +120 -0
  45. package/dist/types/events.d.ts +2 -2
  46. package/dist/utils/apply-query.d.ts +9 -2
  47. package/dist/utils/apply-query.js +41 -14
  48. package/dist/utils/md.js +1 -1
  49. package/dist/utils/redact.d.ts +11 -0
  50. package/dist/utils/redact.js +75 -0
  51. package/dist/utils/schedule.d.ts +5 -0
  52. package/dist/utils/schedule.js +27 -0
  53. package/dist/utils/should-clear-cache.d.ts +10 -0
  54. package/dist/utils/should-clear-cache.js +18 -0
  55. package/dist/utils/should-skip-cache.js +18 -2
  56. package/package.json +48 -52
  57. package/dist/utils/get-os-info.d.ts +0 -9
  58. package/dist/utils/get-os-info.js +0 -40
@@ -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 (validate(cron)) {
401
- const task = schedule(cron, async () => {
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
- task,
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.task.stop();
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: boolean;
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 (validate(flow.options['cron'])) {
151
- const task = schedule(flow.options['cron'], async () => {
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, task }] });
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.forEach((event) => {
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.task.stop();
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: redactLogs(omit(keyedData, '$accountability.permissions')), // Permissions is a ton of data, and is just a copy of what's in the directus_permissions table
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: REDACT_TEXT,
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: REDACT_TEXT,
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'] = REDACT_TEXT;
51
+ value['set-cookie'] = REDACTED_TEXT;
52
52
  }
53
53
  return value;
54
54
  }
55
- return REDACT_TEXT;
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', REDACT_TEXT);
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 && env['CACHE_AUTO_PURGE'] && opts?.autoPurgeCache !== false) {
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 && env['CACHE_AUTO_PURGE'] && opts?.autoPurgeCache !== false) {
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 && env['CACHE_AUTO_PURGE'] && opts?.autoPurgeCache !== false) {
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 && env['CACHE_AUTO_PURGE'] && opts?.autoPurgeCache !== false) {
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 && env['CACHE_AUTO_PURGE'] && opts?.autoPurgeCache !== false) {
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 && env['CACHE_AUTO_PURGE'] && opts?.autoPurgeCache !== false) {
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 && env['CACHE_AUTO_PURGE'] && opts?.autoPurgeCache !== false) {
555
+ if (shouldClearCache(this.cache, opts)) {
555
556
  await this.cache.clear();
556
557
  }
557
558
  if (opts?.autoPurgeSystemCache !== false) {
@@ -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 && env['CACHE_AUTO_PURGE'] && opts?.autoPurgeCache !== false) {
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 && env['CACHE_AUTO_PURGE'] && opts?.autoPurgeCache !== false) {
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 && env['CACHE_AUTO_PURGE'] && opts?.autoPurgeCache !== false) {
500
+ if (shouldClearCache(this.cache, opts)) {
501
501
  await this.cache.clear();
502
502
  }
503
503
  if (opts?.autoPurgeSystemCache !== false) {
@@ -26,9 +26,9 @@ export declare class FilesService extends ItemsService {
26
26
  /**
27
27
  * Delete a file
28
28
  */
29
- deleteOne(key: PrimaryKey, opts?: MutationOptions): Promise<PrimaryKey>;
29
+ deleteOne(key: PrimaryKey): Promise<PrimaryKey>;
30
30
  /**
31
31
  * Delete multiple files
32
32
  */
33
- deleteMany(keys: PrimaryKey[], opts?: MutationOptions): Promise<PrimaryKey[]>;
33
+ deleteMany(keys: PrimaryKey[]): Promise<PrimaryKey[]>;
34
34
  }
@@ -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, opts) {
248
- await this.deleteMany([key], opts);
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, opts) {
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: {
@@ -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';
@@ -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';
@@ -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 }, { bypassLimits: true });
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 && env['CACHE_AUTO_PURGE'] && opts.autoPurgeCache !== false) {
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 && env['CACHE_AUTO_PURGE'] && opts.autoPurgeCache !== false) {
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 && env['CACHE_AUTO_PURGE'] && opts.autoPurgeCache !== false) {
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, { bypassLimits: true });
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 }, { bypassLimits: true });
530
+ await revisionsService.updateMany(childrenRevisions, { parent: revisionID });
530
531
  }
531
532
  }
532
533
  }
533
534
  }
534
535
  }
535
536
  });
536
- if (this.cache && env['CACHE_AUTO_PURGE'] && opts.autoPurgeCache !== false) {
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 && env['CACHE_AUTO_PURGE'] && opts.autoPurgeCache !== false) {
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 && env['CACHE_AUTO_PURGE'] && opts?.autoPurgeCache !== false) {
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
  }