directus 9.6.0 → 9.7.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.
package/dist/app.js CHANGED
@@ -111,9 +111,11 @@ async function createApp() {
111
111
  // friendly. Ref #10806
112
112
  upgradeInsecureRequests: null,
113
113
  // These are required for MapLibre
114
+ // https://cdn.directus.io is required for images/videos in the official docs
114
115
  workerSrc: ["'self'", 'blob:'],
115
116
  childSrc: ["'self'", 'blob:'],
116
- imgSrc: ["'self'", 'data:', 'blob:'],
117
+ imgSrc: ["'self'", 'data:', 'blob:', 'https://cdn.directus.io'],
118
+ mediaSrc: ["'self'", 'https://cdn.directus.io'],
117
119
  connectSrc: ["'self'", 'https://*'],
118
120
  },
119
121
  }, (0, get_config_from_env_1.getConfigFromEnv)('CONTENT_SECURITY_POLICY_'))));
@@ -18,6 +18,7 @@ const async_handler_1 = __importDefault(require("../../utils/async-handler"));
18
18
  const url_1 = require("../../utils/url");
19
19
  const logger_1 = __importDefault(require("../../logger"));
20
20
  const get_ip_from_req_1 = require("../../utils/get-ip-from-req");
21
+ const get_config_from_env_1 = require("../../utils/get-config-from-env");
21
22
  class OAuth2AuthDriver extends local_1.LocalAuthDriver {
22
23
  constructor(options, config) {
23
24
  super(options, config);
@@ -35,11 +36,13 @@ class OAuth2AuthDriver extends local_1.LocalAuthDriver {
35
36
  userinfo_endpoint: profileUrl,
36
37
  issuer: additionalConfig.provider,
37
38
  });
39
+ const clientOptionsOverrides = (0, get_config_from_env_1.getConfigFromEnv)(`AUTH_${config.provider.toUpperCase()}_CLIENT_`, [`AUTH_${config.provider.toUpperCase()}_CLIENT_ID`, `AUTH_${config.provider.toUpperCase()}_CLIENT_SECRET`], 'underscore');
38
40
  this.client = new issuer.Client({
39
41
  client_id: clientId,
40
42
  client_secret: clientSecret,
41
43
  redirect_uris: [this.redirectUrl],
42
44
  response_types: ['code'],
45
+ ...clientOptionsOverrides,
43
46
  });
44
47
  }
45
48
  generateCodeVerifier() {
@@ -18,6 +18,7 @@ const async_handler_1 = __importDefault(require("../../utils/async-handler"));
18
18
  const url_1 = require("../../utils/url");
19
19
  const logger_1 = __importDefault(require("../../logger"));
20
20
  const get_ip_from_req_1 = require("../../utils/get-ip-from-req");
21
+ const get_config_from_env_1 = require("../../utils/get-config-from-env");
21
22
  class OpenIDAuthDriver extends local_1.LocalAuthDriver {
22
23
  constructor(options, config) {
23
24
  super(options, config);
@@ -26,6 +27,7 @@ class OpenIDAuthDriver extends local_1.LocalAuthDriver {
26
27
  throw new exceptions_1.InvalidConfigException('Invalid provider config', { provider: additionalConfig.provider });
27
28
  }
28
29
  const redirectUrl = new url_1.Url(env_1.default.PUBLIC_URL).addPath('auth', 'login', additionalConfig.provider, 'callback');
30
+ const clientOptionsOverrides = (0, get_config_from_env_1.getConfigFromEnv)(`AUTH_${config.provider.toUpperCase()}_CLIENT_`, [`AUTH_${config.provider.toUpperCase()}_CLIENT_ID`, `AUTH_${config.provider.toUpperCase()}_CLIENT_SECRET`], 'underscore');
29
31
  this.redirectUrl = redirectUrl.toString();
30
32
  this.usersService = new services_1.UsersService({ knex: this.knex, schema: this.schema });
31
33
  this.config = additionalConfig;
@@ -43,6 +45,7 @@ class OpenIDAuthDriver extends local_1.LocalAuthDriver {
43
45
  client_secret: clientSecret,
44
46
  redirect_uris: [this.redirectUrl],
45
47
  response_types: ['code'],
48
+ ...clientOptionsOverrides,
46
49
  }));
47
50
  })
48
51
  .catch(reject);
package/dist/cache.d.ts CHANGED
@@ -2,5 +2,8 @@ import Keyv from 'keyv';
2
2
  export declare function getCache(): {
3
3
  cache: Keyv | null;
4
4
  systemCache: Keyv;
5
+ lockCache: Keyv;
5
6
  };
6
- export declare function flushCaches(): Promise<void>;
7
+ export declare function flushCaches(forced?: boolean): Promise<void>;
8
+ export declare function clearSystemCache(forced?: boolean): Promise<void>;
9
+ export declare function setSystemCache(key: string, value: any, ttl?: number): Promise<void>;
package/dist/cache.js CHANGED
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.flushCaches = exports.getCache = void 0;
6
+ exports.setSystemCache = exports.clearSystemCache = exports.flushCaches = exports.getCache = void 0;
7
7
  const keyv_1 = __importDefault(require("keyv"));
8
8
  const ms_1 = __importDefault(require("ms"));
9
9
  const env_1 = __importDefault(require("./env"));
@@ -12,6 +12,7 @@ const get_config_from_env_1 = require("./utils/get-config-from-env");
12
12
  const validate_env_1 = require("./utils/validate-env");
13
13
  let cache = null;
14
14
  let systemCache = null;
15
+ let lockCache = null;
15
16
  function getCache() {
16
17
  if (env_1.default.CACHE_ENABLED === true && cache === null) {
17
18
  (0, validate_env_1.validateEnv)(['CACHE_NAMESPACE', 'CACHE_TTL', 'CACHE_STORE']);
@@ -22,15 +23,36 @@ function getCache() {
22
23
  systemCache = getKeyvInstance(undefined, '_system');
23
24
  systemCache.on('error', (err) => logger_1.default.warn(err, `[cache] ${err}`));
24
25
  }
25
- return { cache, systemCache };
26
+ if (lockCache === null) {
27
+ lockCache = getKeyvInstance(undefined, '_lock');
28
+ lockCache.on('error', (err) => logger_1.default.warn(err, `[cache] ${err}`));
29
+ }
30
+ return { cache, systemCache, lockCache };
26
31
  }
27
32
  exports.getCache = getCache;
28
- async function flushCaches() {
29
- const { systemCache, cache } = getCache();
30
- await (systemCache === null || systemCache === void 0 ? void 0 : systemCache.clear());
33
+ async function flushCaches(forced) {
34
+ const { cache } = getCache();
35
+ await clearSystemCache(forced);
31
36
  await (cache === null || cache === void 0 ? void 0 : cache.clear());
32
37
  }
33
38
  exports.flushCaches = flushCaches;
39
+ async function clearSystemCache(forced) {
40
+ const { systemCache, lockCache } = getCache();
41
+ // Flush system cache when forced or when system cache lock not set
42
+ if (forced || !(await lockCache.get('system-cache-lock'))) {
43
+ await lockCache.set('system-cache-lock', true, 10000);
44
+ await systemCache.clear();
45
+ await lockCache.delete('system-cache-lock');
46
+ }
47
+ }
48
+ exports.clearSystemCache = clearSystemCache;
49
+ async function setSystemCache(key, value, ttl) {
50
+ const { systemCache, lockCache } = getCache();
51
+ if (!(await lockCache.get('system-cache-lock'))) {
52
+ await systemCache.set(key, value, ttl);
53
+ }
54
+ }
55
+ exports.setSystemCache = setSystemCache;
34
56
  function getKeyvInstance(ttl, namespaceSuffix) {
35
57
  switch (env_1.default.CACHE_STORE) {
36
58
  case 'redis':
@@ -1,3 +1,4 @@
1
1
  export declare function apply(snapshotPath: string, options?: {
2
2
  yes: boolean;
3
+ dryRun: boolean;
3
4
  }): Promise<void>;
@@ -63,7 +63,9 @@ async function apply(snapshotPath, options) {
63
63
  database.destroy();
64
64
  process.exit(0);
65
65
  }
66
- if ((options === null || options === void 0 ? void 0 : options.yes) !== true) {
66
+ const dryRun = (options === null || options === void 0 ? void 0 : options.dryRun) === true;
67
+ const promptForChanges = !dryRun && (options === null || options === void 0 ? void 0 : options.yes) !== true;
68
+ if (dryRun || promptForChanges) {
67
69
  let message = '';
68
70
  if (snapshotDiff.collections.length > 0) {
69
71
  message += chalk_1.default.black.underline.bold('Collections:');
@@ -141,14 +143,16 @@ async function apply(snapshotPath, options) {
141
143
  }
142
144
  }
143
145
  }
146
+ message += 'The following changes will be applied:\n\n' + chalk_1.default.black(message);
147
+ if (dryRun) {
148
+ logger_1.default.info(message);
149
+ process.exit(0);
150
+ }
144
151
  const { proceed } = await inquirer_1.default.prompt([
145
152
  {
146
153
  type: 'confirm',
147
154
  name: 'proceed',
148
- message: 'The following changes will be applied:\n\n' +
149
- chalk_1.default.black(message) +
150
- '\n\n' +
151
- 'Would you like to continue?',
155
+ message: message + '\n\n' + 'Would you like to continue?',
152
156
  },
153
157
  ]);
154
158
  if (proceed === false) {
package/dist/cli/index.js CHANGED
@@ -81,6 +81,7 @@ async function createCli() {
81
81
  .command('apply')
82
82
  .description('Apply a snapshot file to the current database')
83
83
  .option('-y, --yes', `Assume "yes" as answer to all prompts and run non-interactively`)
84
+ .option('-d, --dry-run', 'Plan and log changes to be applied', false)
84
85
  .argument('<path>', 'Path to snapshot file')
85
86
  .action(apply_1.apply);
86
87
  await emitter_1.default.emitInit('cli.after', { program });
@@ -14,6 +14,9 @@ const use_collection_1 = __importDefault(require("../middleware/use-collection")
14
14
  const services_1 = require("../services");
15
15
  const assets_1 = require("../types/assets");
16
16
  const async_handler_1 = __importDefault(require("../utils/async-handler"));
17
+ const helmet_1 = __importDefault(require("helmet"));
18
+ const lodash_2 = require("lodash");
19
+ const get_config_from_env_1 = require("../utils/get-config-from-env");
17
20
  const router = (0, express_1.Router)();
18
21
  router.use((0, use_collection_1.default)('directus_files'));
19
22
  router.get('/:pk',
@@ -88,7 +91,12 @@ router.get('/:pk',
88
91
  return next();
89
92
  throw new exceptions_1.InvalidQueryException(`Dynamic asset generation has been disabled for this project.`);
90
93
  }
91
- }),
94
+ }), helmet_1.default.contentSecurityPolicy((0, lodash_2.merge)({
95
+ useDefaults: false,
96
+ directives: {
97
+ defaultSrc: ['none'],
98
+ },
99
+ }, (0, get_config_from_env_1.getConfigFromEnv)('ASSETS_CONTENT_SECURITY_POLICY'))),
92
100
  // Return file
93
101
  (0, async_handler_1.default)(async (req, res) => {
94
102
  var _a, _b;
@@ -95,12 +95,29 @@ router.post('/import/:collection', collection_exists_1.default, (0, async_handle
95
95
  busboy.on('error', (err) => next(err));
96
96
  req.pipe(busboy);
97
97
  }));
98
+ router.post('/export/:collection', collection_exists_1.default, (0, async_handler_1.default)(async (req, res, next) => {
99
+ if (!req.body.query) {
100
+ throw new exceptions_1.InvalidPayloadException(`"query" is required.`);
101
+ }
102
+ if (!req.body.format) {
103
+ throw new exceptions_1.InvalidPayloadException(`"format" is required.`);
104
+ }
105
+ const service = new services_1.ExportService({
106
+ accountability: req.accountability,
107
+ schema: req.schema,
108
+ });
109
+ // We're not awaiting this, as it's supposed to run async in the background
110
+ service.exportToFile(req.params.collection, req.body.query, req.body.format, {
111
+ file: req.body.file,
112
+ });
113
+ return next();
114
+ }), respond_1.respond);
98
115
  router.post('/cache/clear', (0, async_handler_1.default)(async (req, res) => {
99
116
  var _a;
100
117
  if (((_a = req.accountability) === null || _a === void 0 ? void 0 : _a.admin) !== true) {
101
118
  throw new exceptions_1.ForbiddenException();
102
119
  }
103
- await (0, cache_1.flushCaches)();
120
+ await (0, cache_1.flushCaches)(true);
104
121
  res.status(200).end();
105
122
  }));
106
123
  exports.default = router;
@@ -64,7 +64,10 @@ async function runAST(originalAST, schema, options) {
64
64
  }
65
65
  }
66
66
  else {
67
- nestedItems = (await runAST(nestedNode, schema, { knex, nested: true }));
67
+ const node = (0, lodash_1.merge)({}, nestedNode, {
68
+ query: { limit: -1 },
69
+ });
70
+ nestedItems = (await runAST(node, schema, { knex, nested: true }));
68
71
  if (nestedItems) {
69
72
  // Merge all fetched nested records with the parent items
70
73
  items = mergeWithParentItems(schema, nestedItems, items, nestedNode);
@@ -238,6 +241,9 @@ function mergeWithParentItems(schema, nestedItem, parentItem, nestedNode) {
238
241
  ((_a = nestedItem[nestedNode.relation.field]) === null || _a === void 0 ? void 0 : _a[schema.collections[nestedNode.relation.related_collection].primary]) == parentItem[schema.collections[nestedNode.relation.related_collection].primary]);
239
242
  });
240
243
  parentItem[nestedNode.fieldKey].push(...itemChildren);
244
+ if (nestedNode.query.offset && nestedNode.query.offset >= 0) {
245
+ parentItem[nestedNode.fieldKey] = parentItem[nestedNode.fieldKey].slice(nestedNode.query.offset);
246
+ }
241
247
  parentItem[nestedNode.fieldKey] = parentItem[nestedNode.fieldKey].slice(0, (_a = nestedNode.query.limit) !== null && _a !== void 0 ? _a : 100);
242
248
  parentItem[nestedNode.fieldKey] = parentItem[nestedNode.fieldKey].sort((a, b) => {
243
249
  // This is pre-filled in get-ast-from-query
package/dist/env.js CHANGED
@@ -35,8 +35,8 @@ const defaults = {
35
35
  REFRESH_TOKEN_COOKIE_SAME_SITE: 'lax',
36
36
  REFRESH_TOKEN_COOKIE_NAME: 'directus_refresh_token',
37
37
  ROOT_REDIRECT: './admin',
38
- CORS_ENABLED: true,
39
- CORS_ORIGIN: true,
38
+ CORS_ENABLED: false,
39
+ CORS_ORIGIN: false,
40
40
  CORS_METHODS: 'GET,POST,PATCH,DELETE',
41
41
  CORS_ALLOWED_HEADERS: 'Content-Type,Authorization',
42
42
  CORS_EXPOSED_HEADERS: 'Content-Range',
@@ -65,8 +65,10 @@ const defaults = {
65
65
  ASSETS_TRANSFORM_MAX_OPERATIONS: 5,
66
66
  IP_TRUST_PROXY: true,
67
67
  IP_CUSTOM_HEADER: false,
68
+ IMPORT_IP_DENY_LIST: '0.0.0.0',
68
69
  SERVE_APP: true,
69
70
  RELATIONAL_BATCH_SIZE: 25000,
71
+ EXPORT_BATCH_SIZE: 5000,
70
72
  };
71
73
  // Allows us to force certain environment variable into a type, instead of relying
72
74
  // on the auto-parsed type in processValues. ref #3705
@@ -79,6 +81,7 @@ const typeMap = {
79
81
  DB_DATABASE: 'string',
80
82
  DB_PORT: 'number',
81
83
  DB_EXCLUDE_TABLES: 'array',
84
+ IMPORT_IP_DENY_LIST: 'array',
82
85
  };
83
86
  let env = {
84
87
  ...defaults,
@@ -38,6 +38,7 @@ function extractError(error) {
38
38
  }
39
39
  exports.extractError = extractError;
40
40
  function uniqueViolation(error) {
41
+ var _a, _b, _c, _d, _e;
41
42
  const betweenQuotes = /'([^']+)'/g;
42
43
  const matches = error.sqlMessage.match(betweenQuotes);
43
44
  if (!matches)
@@ -49,13 +50,13 @@ function uniqueViolation(error) {
49
50
  */
50
51
  /** MySQL 8+ style error message */
51
52
  if (matches[1].includes('.')) {
52
- const collection = matches[1].slice(1, -1).split('.')[0];
53
+ const collection = (_a = matches[1]) === null || _a === void 0 ? void 0 : _a.slice(1, -1).split('.')[0];
53
54
  let field = null;
54
- const indexName = matches[1].slice(1, -1).split('.')[1];
55
+ const indexName = (_b = matches[1]) === null || _b === void 0 ? void 0 : _b.slice(1, -1).split('.')[1];
55
56
  if ((indexName === null || indexName === void 0 ? void 0 : indexName.startsWith(`${collection}_`)) && indexName.endsWith('_unique')) {
56
- field = indexName.slice(collection.length + 1, -7);
57
+ field = indexName === null || indexName === void 0 ? void 0 : indexName.slice(collection.length + 1, -7);
57
58
  }
58
- const invalid = matches[0].slice(1, -1);
59
+ const invalid = (_c = matches[0]) === null || _c === void 0 ? void 0 : _c.slice(1, -1);
59
60
  return new record_not_unique_1.RecordNotUniqueException(field, {
60
61
  collection,
61
62
  field,
@@ -64,13 +65,13 @@ function uniqueViolation(error) {
64
65
  }
65
66
  else {
66
67
  /** MySQL 5.7 style error message */
67
- const indexName = matches[1].slice(1, -1);
68
+ const indexName = (_d = matches[1]) === null || _d === void 0 ? void 0 : _d.slice(1, -1);
68
69
  const collection = indexName.split('_')[0];
69
70
  let field = null;
70
71
  if ((indexName === null || indexName === void 0 ? void 0 : indexName.startsWith(`${collection}_`)) && indexName.endsWith('_unique')) {
71
- field = indexName.slice(collection.length + 1, -7);
72
+ field = indexName === null || indexName === void 0 ? void 0 : indexName.slice(collection.length + 1, -7);
72
73
  }
73
- const invalid = matches[0].slice(1, -1);
74
+ const invalid = (_e = matches[0]) === null || _e === void 0 ? void 0 : _e.slice(1, -1);
74
75
  return new record_not_unique_1.RecordNotUniqueException(field, {
75
76
  collection,
76
77
  field,
@@ -79,57 +80,61 @@ function uniqueViolation(error) {
79
80
  }
80
81
  }
81
82
  function numericValueOutOfRange(error) {
83
+ var _a, _b;
82
84
  const betweenTicks = /`([^`]+)`/g;
83
85
  const betweenQuotes = /'([^']+)'/g;
84
86
  const tickMatches = error.sql.match(betweenTicks);
85
87
  const quoteMatches = error.sqlMessage.match(betweenQuotes);
86
88
  if (!tickMatches || !quoteMatches)
87
89
  return error;
88
- const collection = tickMatches[0].slice(1, -1);
89
- const field = quoteMatches[0].slice(1, -1);
90
+ const collection = (_a = tickMatches[0]) === null || _a === void 0 ? void 0 : _a.slice(1, -1);
91
+ const field = (_b = quoteMatches[0]) === null || _b === void 0 ? void 0 : _b.slice(1, -1);
90
92
  return new value_out_of_range_1.ValueOutOfRangeException(field, {
91
93
  collection,
92
94
  field,
93
95
  });
94
96
  }
95
97
  function valueLimitViolation(error) {
98
+ var _a, _b;
96
99
  const betweenTicks = /`([^`]+)`/g;
97
100
  const betweenQuotes = /'([^']+)'/g;
98
101
  const tickMatches = error.sql.match(betweenTicks);
99
102
  const quoteMatches = error.sqlMessage.match(betweenQuotes);
100
103
  if (!tickMatches || !quoteMatches)
101
104
  return error;
102
- const collection = tickMatches[0].slice(1, -1);
103
- const field = quoteMatches[0].slice(1, -1);
105
+ const collection = (_a = tickMatches[0]) === null || _a === void 0 ? void 0 : _a.slice(1, -1);
106
+ const field = (_b = quoteMatches[0]) === null || _b === void 0 ? void 0 : _b.slice(1, -1);
104
107
  return new value_too_long_1.ValueTooLongException(field, {
105
108
  collection,
106
109
  field,
107
110
  });
108
111
  }
109
112
  function notNullViolation(error) {
113
+ var _a, _b;
110
114
  const betweenTicks = /`([^`]+)`/g;
111
115
  const betweenQuotes = /'([^']+)'/g;
112
116
  const tickMatches = error.sql.match(betweenTicks);
113
117
  const quoteMatches = error.sqlMessage.match(betweenQuotes);
114
118
  if (!tickMatches || !quoteMatches)
115
119
  return error;
116
- const collection = tickMatches[0].slice(1, -1);
117
- const field = quoteMatches[0].slice(1, -1);
120
+ const collection = (_a = tickMatches[0]) === null || _a === void 0 ? void 0 : _a.slice(1, -1);
121
+ const field = (_b = quoteMatches[0]) === null || _b === void 0 ? void 0 : _b.slice(1, -1);
118
122
  return new not_null_violation_1.NotNullViolationException(field, {
119
123
  collection,
120
124
  field,
121
125
  });
122
126
  }
123
127
  function foreignKeyViolation(error) {
128
+ var _a, _b, _c;
124
129
  const betweenTicks = /`([^`]+)`/g;
125
130
  const betweenParens = /\(([^)]+)\)/g;
126
131
  const tickMatches = error.sqlMessage.match(betweenTicks);
127
132
  const parenMatches = error.sql.match(betweenParens);
128
133
  if (!tickMatches || !parenMatches)
129
134
  return error;
130
- const collection = tickMatches[1].slice(1, -1);
131
- const field = tickMatches[3].slice(1, -1);
132
- const invalid = parenMatches[1].slice(1, -1);
135
+ const collection = (_a = tickMatches[1]) === null || _a === void 0 ? void 0 : _a.slice(1, -1);
136
+ const field = (_b = tickMatches[3]) === null || _b === void 0 ? void 0 : _b.slice(1, -1);
137
+ const invalid = (_c = parenMatches[1]) === null || _c === void 0 ? void 0 : _c.slice(1, -1);
133
138
  return new invalid_foreign_key_1.InvalidForeignKeyException(field, {
134
139
  collection,
135
140
  field,
@@ -137,12 +142,13 @@ function foreignKeyViolation(error) {
137
142
  });
138
143
  }
139
144
  function containsNullValues(error) {
145
+ var _a;
140
146
  const betweenTicks = /`([^`]+)`/g;
141
147
  // Normally, we shouldn't read from the executed SQL. In this case, we're altering a single
142
148
  // column, so we shouldn't have the problem where multiple columns are altered at the same time
143
149
  const tickMatches = error.sql.match(betweenTicks);
144
150
  if (!tickMatches)
145
151
  return error;
146
- const field = tickMatches[1].slice(1, -1);
152
+ const field = (_a = tickMatches[1]) === null || _a === void 0 ? void 0 : _a.slice(1, -1);
147
153
  return new contains_null_values_1.ContainsNullValuesException(field);
148
154
  }
@@ -4,16 +4,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.respond = void 0;
7
- const json2csv_1 = require("json2csv");
8
7
  const ms_1 = __importDefault(require("ms"));
9
- const stream_1 = require("stream");
10
8
  const cache_1 = require("../cache");
11
9
  const env_1 = __importDefault(require("../env"));
12
10
  const async_handler_1 = __importDefault(require("../utils/async-handler"));
13
11
  const get_cache_key_1 = require("../utils/get-cache-key");
14
- const js2xmlparser_1 = require("js2xmlparser");
15
12
  const get_cache_headers_1 = require("../utils/get-cache-headers");
16
13
  const logger_1 = __importDefault(require("../logger"));
14
+ const services_1 = require("../services");
15
+ const get_date_formatted_1 = require("../utils/get-date-formatted");
17
16
  exports.respond = (0, async_handler_1.default)(async (req, res) => {
18
17
  var _a, _b, _c;
19
18
  const { cache } = (0, cache_1.getCache)();
@@ -39,6 +38,7 @@ exports.respond = (0, async_handler_1.default)(async (req, res) => {
39
38
  res.setHeader('Vary', 'Origin, Cache-Control');
40
39
  }
41
40
  if (req.sanitizedQuery.export) {
41
+ const exportService = new services_1.ExportService({ accountability: req.accountability, schema: req.schema });
42
42
  let filename = '';
43
43
  if (req.collection) {
44
44
  filename += req.collection;
@@ -46,32 +46,21 @@ exports.respond = (0, async_handler_1.default)(async (req, res) => {
46
46
  else {
47
47
  filename += 'Export';
48
48
  }
49
- filename += ' ' + getDateFormatted();
49
+ filename += ' ' + (0, get_date_formatted_1.getDateFormatted)();
50
50
  if (req.sanitizedQuery.export === 'json') {
51
51
  res.attachment(`${filename}.json`);
52
52
  res.set('Content-Type', 'application/json');
53
- return res.status(200).send(JSON.stringify(((_a = res.locals.payload) === null || _a === void 0 ? void 0 : _a.data) || null, null, '\t'));
53
+ return res.status(200).send(exportService.transform((_a = res.locals.payload) === null || _a === void 0 ? void 0 : _a.data, 'json'));
54
54
  }
55
55
  if (req.sanitizedQuery.export === 'xml') {
56
56
  res.attachment(`${filename}.xml`);
57
57
  res.set('Content-Type', 'text/xml');
58
- return res.status(200).send((0, js2xmlparser_1.parse)('data', (_b = res.locals.payload) === null || _b === void 0 ? void 0 : _b.data));
58
+ return res.status(200).send(exportService.transform((_b = res.locals.payload) === null || _b === void 0 ? void 0 : _b.data, 'xml'));
59
59
  }
60
60
  if (req.sanitizedQuery.export === 'csv') {
61
61
  res.attachment(`${filename}.csv`);
62
62
  res.set('Content-Type', 'text/csv');
63
- const stream = new stream_1.PassThrough();
64
- if (!((_c = res.locals.payload) === null || _c === void 0 ? void 0 : _c.data) || res.locals.payload.data.length === 0) {
65
- stream.end(Buffer.from(''));
66
- return stream.pipe(res);
67
- }
68
- else {
69
- stream.end(Buffer.from(JSON.stringify(res.locals.payload.data), 'utf-8'));
70
- const json2csv = new json2csv_1.Transform({
71
- transforms: [json2csv_1.transforms.flatten({ separator: '.' })],
72
- });
73
- return stream.pipe(json2csv).pipe(res);
74
- }
63
+ return res.status(200).send(exportService.transform((_c = res.locals.payload) === null || _c === void 0 ? void 0 : _c.data, 'csv'));
75
64
  }
76
65
  }
77
66
  if (Buffer.isBuffer(res.locals.payload)) {
@@ -84,13 +73,3 @@ exports.respond = (0, async_handler_1.default)(async (req, res) => {
84
73
  return res.status(204).end();
85
74
  }
86
75
  });
87
- function getDateFormatted() {
88
- const date = new Date();
89
- let month = String(date.getMonth() + 1);
90
- if (month.length === 1)
91
- month = '0' + month;
92
- let day = String(date.getDate());
93
- if (day.length === 1)
94
- day = '0' + day;
95
- return `${date.getFullYear()}-${month}-${day} at ${date.getHours()}.${date.getMinutes()}.${date.getSeconds()}`;
96
- }
@@ -129,7 +129,7 @@ class CollectionsService {
129
129
  if (this.cache && env_1.default.CACHE_AUTO_PURGE && (opts === null || opts === void 0 ? void 0 : opts.autoPurgeCache) !== false) {
130
130
  await this.cache.clear();
131
131
  }
132
- await this.systemCache.clear();
132
+ await (0, cache_1.clearSystemCache)();
133
133
  return payload.collection;
134
134
  }
135
135
  /**
@@ -152,7 +152,7 @@ class CollectionsService {
152
152
  if (this.cache && env_1.default.CACHE_AUTO_PURGE && (opts === null || opts === void 0 ? void 0 : opts.autoPurgeCache) !== false) {
153
153
  await this.cache.clear();
154
154
  }
155
- await this.systemCache.clear();
155
+ await (0, cache_1.clearSystemCache)();
156
156
  return collections;
157
157
  }
158
158
  /**
@@ -275,7 +275,7 @@ class CollectionsService {
275
275
  if (this.cache && env_1.default.CACHE_AUTO_PURGE && (opts === null || opts === void 0 ? void 0 : opts.autoPurgeCache) !== false) {
276
276
  await this.cache.clear();
277
277
  }
278
- await this.systemCache.clear();
278
+ await (0, cache_1.clearSystemCache)();
279
279
  return collectionKey;
280
280
  }
281
281
  /**
@@ -298,7 +298,7 @@ class CollectionsService {
298
298
  if (this.cache && env_1.default.CACHE_AUTO_PURGE && (opts === null || opts === void 0 ? void 0 : opts.autoPurgeCache) !== false) {
299
299
  await this.cache.clear();
300
300
  }
301
- await this.systemCache.clear();
301
+ await (0, cache_1.clearSystemCache)();
302
302
  return collectionKeys;
303
303
  }
304
304
  /**
@@ -379,7 +379,7 @@ class CollectionsService {
379
379
  if (this.cache && env_1.default.CACHE_AUTO_PURGE && (opts === null || opts === void 0 ? void 0 : opts.autoPurgeCache) !== false) {
380
380
  await this.cache.clear();
381
381
  }
382
- await this.systemCache.clear();
382
+ await (0, cache_1.clearSystemCache)();
383
383
  return collectionKey;
384
384
  }
385
385
  /**
@@ -402,7 +402,7 @@ class CollectionsService {
402
402
  if (this.cache && env_1.default.CACHE_AUTO_PURGE && (opts === null || opts === void 0 ? void 0 : opts.autoPurgeCache) !== false) {
403
403
  await this.cache.clear();
404
404
  }
405
- await this.systemCache.clear();
405
+ await (0, cache_1.clearSystemCache)();
406
406
  return collectionKeys;
407
407
  }
408
408
  }
@@ -249,7 +249,7 @@ class FieldsService {
249
249
  if (this.cache && env_1.default.CACHE_AUTO_PURGE) {
250
250
  await this.cache.clear();
251
251
  }
252
- await this.systemCache.clear();
252
+ await (0, cache_1.clearSystemCache)();
253
253
  }
254
254
  async updateField(collection, field) {
255
255
  if (this.accountability && this.accountability.admin !== true) {
@@ -300,7 +300,7 @@ class FieldsService {
300
300
  if (this.cache && env_1.default.CACHE_AUTO_PURGE) {
301
301
  await this.cache.clear();
302
302
  }
303
- await this.systemCache.clear();
303
+ await (0, cache_1.clearSystemCache)();
304
304
  emitter_1.default.emitAction(`fields.update`, {
305
305
  payload: hookAdjustedField,
306
306
  keys: [hookAdjustedField.field],
@@ -395,7 +395,7 @@ class FieldsService {
395
395
  if (this.cache && env_1.default.CACHE_AUTO_PURGE) {
396
396
  await this.cache.clear();
397
397
  }
398
- await this.systemCache.clear();
398
+ await (0, cache_1.clearSystemCache)();
399
399
  emitter_1.default.emitAction('fields.delete', {
400
400
  payload: [field],
401
401
  collection: collection,