@directus/api 21.0.0-rc.0 → 22.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 (65) hide show
  1. package/dist/app.js +1 -1
  2. package/dist/cache.d.ts +0 -1
  3. package/dist/cache.js +7 -22
  4. package/dist/controllers/tus.js +7 -5
  5. package/dist/database/get-ast-from-query/lib/parse-fields.d.ts +1 -1
  6. package/dist/database/get-ast-from-query/lib/parse-fields.js +10 -0
  7. package/dist/database/helpers/schema/dialects/cockroachdb.d.ts +2 -1
  8. package/dist/database/helpers/schema/dialects/cockroachdb.js +4 -0
  9. package/dist/database/helpers/schema/dialects/mssql.d.ts +2 -1
  10. package/dist/database/helpers/schema/dialects/mssql.js +4 -0
  11. package/dist/database/helpers/schema/dialects/oracle.d.ts +2 -1
  12. package/dist/database/helpers/schema/dialects/oracle.js +4 -0
  13. package/dist/database/helpers/schema/dialects/postgres.d.ts +2 -1
  14. package/dist/database/helpers/schema/dialects/postgres.js +4 -0
  15. package/dist/database/helpers/schema/types.d.ts +5 -0
  16. package/dist/database/helpers/schema/types.js +3 -0
  17. package/dist/database/helpers/schema/utils/preprocess-bindings.d.ts +8 -0
  18. package/dist/database/helpers/schema/utils/preprocess-bindings.js +30 -0
  19. package/dist/database/index.js +14 -6
  20. package/dist/database/migrations/20240305A-change-useragent-type.js +1 -1
  21. package/dist/database/migrations/20240716A-update-files-date-fields.js +33 -0
  22. package/dist/database/migrations/20240806A-permissions-policies.d.ts +6 -0
  23. package/dist/database/migrations/20240806A-permissions-policies.js +338 -0
  24. package/dist/database/run-ast/lib/get-db-query.js +12 -2
  25. package/dist/database/run-ast/utils/apply-case-when.js +5 -4
  26. package/dist/database/run-ast/utils/with-preprocess-bindings.d.ts +2 -0
  27. package/dist/database/run-ast/utils/with-preprocess-bindings.js +14 -0
  28. package/dist/logger/index.js +1 -1
  29. package/dist/middleware/error-handler.d.ts +2 -2
  30. package/dist/middleware/error-handler.js +54 -51
  31. package/dist/permissions/lib/fetch-permissions.d.ts +1 -0
  32. package/dist/permissions/lib/fetch-permissions.js +3 -2
  33. package/dist/permissions/lib/fetch-policies.d.ts +7 -0
  34. package/dist/permissions/lib/fetch-policies.js +16 -1
  35. package/dist/permissions/modules/process-ast/lib/inject-cases.js +6 -6
  36. package/dist/permissions/modules/process-ast/types.d.ts +0 -6
  37. package/dist/permissions/modules/process-ast/utils/extract-paths-from-query.js +11 -1
  38. package/dist/permissions/utils/filter-policies-by-ip.d.ts +1 -1
  39. package/dist/services/assets.js +2 -5
  40. package/dist/services/fields.d.ts +3 -0
  41. package/dist/services/fields.js +29 -5
  42. package/dist/services/files/lib/get-sharp-instance.d.ts +2 -0
  43. package/dist/services/files/lib/get-sharp-instance.js +10 -0
  44. package/dist/services/files/utils/get-metadata.js +7 -6
  45. package/dist/services/files.js +5 -0
  46. package/dist/services/import-export.d.ts +3 -1
  47. package/dist/services/import-export.js +49 -5
  48. package/dist/services/mail/index.d.ts +1 -1
  49. package/dist/services/mail/index.js +9 -1
  50. package/dist/services/relations.d.ts +3 -1
  51. package/dist/services/relations.js +27 -5
  52. package/dist/services/tus/data-store.js +4 -5
  53. package/dist/services/tus/server.d.ts +1 -1
  54. package/dist/services/tus/server.js +9 -2
  55. package/dist/utils/apply-query.d.ts +8 -5
  56. package/dist/utils/apply-query.js +40 -5
  57. package/dist/utils/fetch-user-count/fetch-access-lookup.d.ts +2 -0
  58. package/dist/utils/fetch-user-count/fetch-access-lookup.js +3 -2
  59. package/dist/utils/fetch-user-count/fetch-user-count.js +10 -3
  60. package/dist/utils/fetch-user-count/get-user-count-query.js +1 -1
  61. package/dist/utils/get-schema.js +3 -3
  62. package/dist/utils/sanitize-schema.d.ts +1 -1
  63. package/package.json +38 -38
  64. package/dist/database/migrations/20240710A-permissions-policies.js +0 -169
  65. /package/dist/database/migrations/{20240710A-permissions-policies.d.ts → 20240716A-update-files-date-fields.d.ts} +0 -0
package/dist/app.js CHANGED
@@ -50,7 +50,7 @@ import { createExpressLogger, useLogger } from './logger/index.js';
50
50
  import authenticate from './middleware/authenticate.js';
51
51
  import cache from './middleware/cache.js';
52
52
  import cors from './middleware/cors.js';
53
- import errorHandler from './middleware/error-handler.js';
53
+ import { errorHandler } from './middleware/error-handler.js';
54
54
  import extractToken from './middleware/extract-token.js';
55
55
  import rateLimiterGlobal from './middleware/rate-limiter-global.js';
56
56
  import rateLimiter from './middleware/rate-limiter-ip.js';
package/dist/cache.d.ts CHANGED
@@ -3,7 +3,6 @@ import Keyv from 'keyv';
3
3
  export declare function getCache(): {
4
4
  cache: Keyv | null;
5
5
  systemCache: Keyv;
6
- sharedSchemaCache: Keyv;
7
6
  localSchemaCache: Keyv;
8
7
  lockCache: Keyv;
9
8
  };
package/dist/cache.js CHANGED
@@ -1,5 +1,4 @@
1
1
  import { useEnv } from '@directus/env';
2
- import { getSimpleHash } from '@directus/utils';
3
2
  import Keyv from 'keyv';
4
3
  import { useBus } from './bus/index.js';
5
4
  import { useLogger } from './logger/index.js';
@@ -16,16 +15,16 @@ const require = createRequire(import.meta.url);
16
15
  let cache = null;
17
16
  let systemCache = null;
18
17
  let localSchemaCache = null;
19
- let sharedSchemaCache = null;
20
18
  let lockCache = null;
21
19
  let messengerSubscribed = false;
22
20
  const messenger = useBus();
23
- if (redisConfigAvailable() && env['CACHE_STORE'] === 'memory' && env['CACHE_AUTO_PURGE'] && !messengerSubscribed) {
21
+ if (redisConfigAvailable() && !messengerSubscribed) {
24
22
  messengerSubscribed = true;
25
23
  messenger.subscribe('schemaChanged', async (opts) => {
26
- if (cache && opts?.['autoPurgeCache'] !== false) {
24
+ if (env['CACHE_STORE'] === 'memory' && env['CACHE_AUTO_PURGE'] && cache && opts?.['autoPurgeCache'] !== false) {
27
25
  await cache.clear();
28
26
  }
27
+ await localSchemaCache?.clear();
29
28
  });
30
29
  }
31
30
  export function getCache() {
@@ -38,10 +37,6 @@ export function getCache() {
38
37
  systemCache = getKeyvInstance(env['CACHE_STORE'], getMilliseconds(env['CACHE_SYSTEM_TTL']), '_system');
39
38
  systemCache.on('error', (err) => logger.warn(err, `[system-cache] ${err}`));
40
39
  }
41
- if (sharedSchemaCache === null) {
42
- sharedSchemaCache = getKeyvInstance(env['CACHE_STORE'], getMilliseconds(env['CACHE_SYSTEM_TTL']), '_schema_shared');
43
- sharedSchemaCache.on('error', (err) => logger.warn(err, `[shared-schema-cache] ${err}`));
44
- }
45
40
  if (localSchemaCache === null) {
46
41
  localSchemaCache = getKeyvInstance('memory', getMilliseconds(env['CACHE_SYSTEM_TTL']), '_schema');
47
42
  localSchemaCache.on('error', (err) => logger.warn(err, `[schema-cache] ${err}`));
@@ -50,7 +45,7 @@ export function getCache() {
50
45
  lockCache = getKeyvInstance(env['CACHE_STORE'], undefined, '_lock');
51
46
  lockCache.on('error', (err) => logger.warn(err, `[lock-cache] ${err}`));
52
47
  }
53
- return { cache, systemCache, sharedSchemaCache, localSchemaCache, lockCache };
48
+ return { cache, systemCache, localSchemaCache, lockCache };
54
49
  }
55
50
  export async function flushCaches(forced) {
56
51
  const { cache } = getCache();
@@ -58,14 +53,13 @@ export async function flushCaches(forced) {
58
53
  await cache?.clear();
59
54
  }
60
55
  export async function clearSystemCache(opts) {
61
- const { systemCache, localSchemaCache, lockCache, sharedSchemaCache } = getCache();
56
+ const { systemCache, localSchemaCache, lockCache } = getCache();
62
57
  // Flush system cache when forced or when system cache lock not set
63
58
  if (opts?.forced || !(await lockCache.get('system-cache-lock'))) {
64
59
  await lockCache.set('system-cache-lock', true, 10000);
65
60
  await systemCache.clear();
66
61
  await lockCache.delete('system-cache-lock');
67
62
  }
68
- await sharedSchemaCache.clear();
69
63
  await localSchemaCache.clear();
70
64
  // Since a lot of cached permission function rely on the schema it needs to be cleared as well
71
65
  await clearPermissionCache();
@@ -82,20 +76,11 @@ export async function getSystemCache(key) {
82
76
  return await getCacheValue(systemCache, key);
83
77
  }
84
78
  export async function setSchemaCache(schema) {
85
- const { localSchemaCache, sharedSchemaCache } = getCache();
86
- const schemaHash = getSimpleHash(JSON.stringify(schema));
87
- await sharedSchemaCache.set('hash', schemaHash);
79
+ const { localSchemaCache } = getCache();
88
80
  await localSchemaCache.set('schema', schema);
89
- await localSchemaCache.set('hash', schemaHash);
90
81
  }
91
82
  export async function getSchemaCache() {
92
- const { localSchemaCache, sharedSchemaCache } = getCache();
93
- const sharedSchemaHash = await sharedSchemaCache.get('hash');
94
- if (!sharedSchemaHash)
95
- return;
96
- const localSchemaHash = await localSchemaCache.get('hash');
97
- if (!localSchemaHash || localSchemaHash !== sharedSchemaHash)
98
- return;
83
+ const { localSchemaCache } = getCache();
99
84
  return await localSchemaCache.get('schema');
100
85
  }
101
86
  export async function setCacheValue(cache, key, value, ttl) {
@@ -1,11 +1,11 @@
1
1
  import { Router } from 'express';
2
+ import { RESUMABLE_UPLOADS } from '../constants.js';
2
3
  import getDatabase from '../database/index.js';
3
4
  import { validateAccess } from '../permissions/modules/validate-access/validate-access.js';
4
- import { getSchema } from '../utils/get-schema.js';
5
- import { scheduleSynchronizedJob, validateCron } from '../utils/schedule.js';
6
5
  import { createTusServer } from '../services/tus/index.js';
7
6
  import asyncHandler from '../utils/async-handler.js';
8
- import { RESUMABLE_UPLOADS } from '../constants.js';
7
+ import { getSchema } from '../utils/get-schema.js';
8
+ import { scheduleSynchronizedJob, validateCron } from '../utils/schedule.js';
9
9
  const mapAction = (method) => {
10
10
  switch (method) {
11
11
  case 'POST':
@@ -33,21 +33,23 @@ const checkFileAccess = asyncHandler(async (req, _res, next) => {
33
33
  return next();
34
34
  });
35
35
  const handler = asyncHandler(async (req, res) => {
36
- const tusServer = await createTusServer({
36
+ const [tusServer, cleanupServer] = await createTusServer({
37
37
  schema: req.schema,
38
38
  accountability: req.accountability,
39
39
  });
40
40
  await tusServer.handle(req, res);
41
+ cleanupServer();
41
42
  });
42
43
  export function scheduleTusCleanup() {
43
44
  if (!RESUMABLE_UPLOADS.ENABLED)
44
45
  return;
45
46
  if (validateCron(RESUMABLE_UPLOADS.SCHEDULE)) {
46
47
  scheduleSynchronizedJob('tus-cleanup', RESUMABLE_UPLOADS.SCHEDULE, async () => {
47
- const tusServer = await createTusServer({
48
+ const [tusServer, cleanupServer] = await createTusServer({
48
49
  schema: await getSchema(),
49
50
  });
50
51
  await tusServer.cleanUpExpiredUploads();
52
+ cleanupServer();
51
53
  });
52
54
  }
53
55
  }
@@ -12,4 +12,4 @@ export interface ParseFieldsContext {
12
12
  schema: SchemaOverview;
13
13
  knex: Knex;
14
14
  }
15
- export declare function parseFields(options: ParseFieldsOptions, context: ParseFieldsContext): Promise<(NestedCollectionNode | FieldNode | FunctionFieldNode)[]>;
15
+ export declare function parseFields(options: ParseFieldsOptions, context: ParseFieldsContext): Promise<[] | (NestedCollectionNode | FieldNode | FunctionFieldNode)[]>;
@@ -88,6 +88,16 @@ export async function parseFields(options, context) {
88
88
  }
89
89
  }
90
90
  }
91
+ if (name.includes(':')) {
92
+ const [key, scope] = name.split(':');
93
+ if (key in relationalStructure === false) {
94
+ relationalStructure[key] = { [scope]: [] };
95
+ }
96
+ else if (scope in relationalStructure[key] === false) {
97
+ relationalStructure[key][scope] = [];
98
+ }
99
+ continue;
100
+ }
91
101
  children.push({ type: 'field', name, fieldKey, whenCase: [] });
92
102
  }
93
103
  }
@@ -1,8 +1,9 @@
1
1
  import type { KNEX_TYPES } from '@directus/constants';
2
- import type { Options } from '../types.js';
2
+ import type { Options, Sql } from '../types.js';
3
3
  import { SchemaHelper } from '../types.js';
4
4
  export declare class SchemaHelperCockroachDb extends SchemaHelper {
5
5
  changeToType(table: string, column: string, type: (typeof KNEX_TYPES)[number], options?: Options): Promise<void>;
6
6
  constraintName(existingName: string): string;
7
7
  getDatabaseSize(): Promise<number | null>;
8
+ preprocessBindings(queryParams: Sql): Sql;
8
9
  }
@@ -1,5 +1,6 @@
1
1
  import { SchemaHelper } from '../types.js';
2
2
  import { useEnv } from '@directus/env';
3
+ import { preprocessBindings } from '../utils/preprocess-bindings.js';
3
4
  const env = useEnv();
4
5
  export class SchemaHelperCockroachDb extends SchemaHelper {
5
6
  async changeToType(table, column, type, options = {}) {
@@ -27,4 +28,7 @@ export class SchemaHelperCockroachDb extends SchemaHelper {
27
28
  return null;
28
29
  }
29
30
  }
31
+ preprocessBindings(queryParams) {
32
+ return preprocessBindings(queryParams, { format: (index) => `$${index + 1}` });
33
+ }
30
34
  }
@@ -1,8 +1,9 @@
1
1
  import type { Knex } from 'knex';
2
- import { SchemaHelper } from '../types.js';
2
+ import { SchemaHelper, type Sql } from '../types.js';
3
3
  export declare class SchemaHelperMSSQL extends SchemaHelper {
4
4
  applyLimit(rootQuery: Knex.QueryBuilder, limit: number): void;
5
5
  applyOffset(rootQuery: Knex.QueryBuilder, offset: number): void;
6
6
  formatUUID(uuid: string): string;
7
7
  getDatabaseSize(): Promise<number | null>;
8
+ preprocessBindings(queryParams: Sql): Sql;
8
9
  }
@@ -1,4 +1,5 @@
1
1
  import { SchemaHelper } from '../types.js';
2
+ import { preprocessBindings } from '../utils/preprocess-bindings.js';
2
3
  export class SchemaHelperMSSQL extends SchemaHelper {
3
4
  applyLimit(rootQuery, limit) {
4
5
  // The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries,
@@ -26,4 +27,7 @@ export class SchemaHelperMSSQL extends SchemaHelper {
26
27
  return null;
27
28
  }
28
29
  }
30
+ preprocessBindings(queryParams) {
31
+ return preprocessBindings(queryParams, { format: (index) => `@p${index}` });
32
+ }
29
33
  }
@@ -1,6 +1,6 @@
1
1
  import type { KNEX_TYPES } from '@directus/constants';
2
2
  import type { Field, Relation, Type } from '@directus/types';
3
- import type { Options } from '../types.js';
3
+ import type { Options, Sql } from '../types.js';
4
4
  import { SchemaHelper } from '../types.js';
5
5
  export declare class SchemaHelperOracle extends SchemaHelper {
6
6
  changeToType(table: string, column: string, type: (typeof KNEX_TYPES)[number], options?: Options): Promise<void>;
@@ -8,4 +8,5 @@ export declare class SchemaHelperOracle extends SchemaHelper {
8
8
  preRelationChange(relation: Partial<Relation>): void;
9
9
  processFieldType(field: Field): Type;
10
10
  getDatabaseSize(): Promise<number | null>;
11
+ preprocessBindings(queryParams: Sql): Sql;
11
12
  }
@@ -1,4 +1,5 @@
1
1
  import { SchemaHelper } from '../types.js';
2
+ import { preprocessBindings } from '../utils/preprocess-bindings.js';
2
3
  export class SchemaHelperOracle extends SchemaHelper {
3
4
  async changeToType(table, column, type, options = {}) {
4
5
  await this.changeToTypeByCopy(table, column, type, options);
@@ -38,4 +39,7 @@ export class SchemaHelperOracle extends SchemaHelper {
38
39
  return null;
39
40
  }
40
41
  }
42
+ preprocessBindings(queryParams) {
43
+ return preprocessBindings(queryParams, { format: (index) => `:${index + 1}` });
44
+ }
41
45
  }
@@ -1,4 +1,5 @@
1
- import { SchemaHelper } from '../types.js';
1
+ import { SchemaHelper, type Sql } from '../types.js';
2
2
  export declare class SchemaHelperPostgres extends SchemaHelper {
3
3
  getDatabaseSize(): Promise<number | null>;
4
+ preprocessBindings(queryParams: Sql): Sql;
4
5
  }
@@ -1,5 +1,6 @@
1
1
  import { useEnv } from '@directus/env';
2
2
  import { SchemaHelper } from '../types.js';
3
+ import { preprocessBindings } from '../utils/preprocess-bindings.js';
3
4
  const env = useEnv();
4
5
  export class SchemaHelperPostgres extends SchemaHelper {
5
6
  async getDatabaseSize() {
@@ -11,4 +12,7 @@ export class SchemaHelperPostgres extends SchemaHelper {
11
12
  return null;
12
13
  }
13
14
  }
15
+ preprocessBindings(queryParams) {
16
+ return preprocessBindings(queryParams, { format: (index) => `$${index + 1}` });
17
+ }
14
18
  }
@@ -8,6 +8,10 @@ export type Options = {
8
8
  default?: any;
9
9
  length?: number;
10
10
  };
11
+ export type Sql = {
12
+ sql: string;
13
+ bindings: readonly Knex.Value[];
14
+ };
11
15
  export declare abstract class SchemaHelper extends DatabaseHelper {
12
16
  isOneOfClients(clients: DatabaseClient[]): boolean;
13
17
  changeNullable(table: string, column: string, nullable: boolean): Promise<void>;
@@ -27,4 +31,5 @@ export declare abstract class SchemaHelper extends DatabaseHelper {
27
31
  * @returns Size of the database in bytes
28
32
  */
29
33
  getDatabaseSize(): Promise<number | null>;
34
+ preprocessBindings(queryParams: Sql): Sql;
30
35
  }
@@ -94,4 +94,7 @@ export class SchemaHelper extends DatabaseHelper {
94
94
  async getDatabaseSize() {
95
95
  return null;
96
96
  }
97
+ preprocessBindings(queryParams) {
98
+ return queryParams;
99
+ }
97
100
  }
@@ -0,0 +1,8 @@
1
+ import type { Sql } from '../types.js';
2
+ export type PreprocessBindingsOptions = {
3
+ format(index: number): string;
4
+ };
5
+ export declare function preprocessBindings(queryParams: (Partial<Sql> & Pick<Sql, 'sql'>) | string, options: PreprocessBindingsOptions): {
6
+ sql: string;
7
+ bindings: import("knex").Knex.Value[];
8
+ };
@@ -0,0 +1,30 @@
1
+ import { isString } from 'lodash-es';
2
+ export function preprocessBindings(queryParams, options) {
3
+ const query = { bindings: [], ...(isString(queryParams) ? { sql: queryParams } : queryParams) };
4
+ const bindingIndices = new Array(query.bindings.length);
5
+ for (let i = 0; i < query.bindings.length; i++) {
6
+ const binding = query.bindings[i];
7
+ const prevIndex = query.bindings.findIndex((b, j) => j < i && b === binding);
8
+ if (prevIndex !== -1) {
9
+ bindingIndices[i] = prevIndex;
10
+ }
11
+ else {
12
+ bindingIndices[i] = i;
13
+ }
14
+ }
15
+ let matchIndex = 0;
16
+ let currentBindingIndex = 0;
17
+ const sql = query.sql.replace(/(\\*)(\?)/g, function (_, escapes) {
18
+ if (escapes.length % 2) {
19
+ // Return an escaped question mark, so it stays escaped
20
+ return `${'\\'.repeat(escapes.length)}?`;
21
+ }
22
+ else {
23
+ const bindingIndex = bindingIndices[matchIndex] === matchIndex ? currentBindingIndex++ : bindingIndices[matchIndex];
24
+ matchIndex++;
25
+ return options.format(bindingIndex);
26
+ }
27
+ });
28
+ const bindings = query.bindings.filter((_, i) => bindingIndices[i] === i);
29
+ return { ...query, sql, bindings };
30
+ }
@@ -1,5 +1,6 @@
1
1
  import { useEnv } from '@directus/env';
2
2
  import { createInspector } from '@directus/schema';
3
+ import { isObject } from '@directus/utils';
3
4
  import fse from 'fs-extra';
4
5
  import knex from 'knex';
5
6
  import { merge } from 'lodash-es';
@@ -106,6 +107,9 @@ export function getDatabase() {
106
107
  };
107
108
  }
108
109
  if (client === 'mysql') {
110
+ // Remove the conflicting `filename` option, defined by default in the Docker Image
111
+ if (isObject(knexConfig.connection))
112
+ delete knexConfig.connection['filename'];
109
113
  Object.assign(knexConfig, { client: 'mysql2' });
110
114
  poolConfig.afterCreate = async (conn, callback) => {
111
115
  logger.trace('Retrieving database version');
@@ -123,15 +127,19 @@ export function getDatabase() {
123
127
  }
124
128
  database = knex.default(knexConfig);
125
129
  validateDatabaseCharset(database);
126
- const times = {};
130
+ const times = new Map();
127
131
  database
128
- .on('query', (queryInfo) => {
129
- times[queryInfo.__knexUid] = performance.now();
132
+ .on('query', ({ __knexUid }) => {
133
+ times.set(__knexUid, performance.now());
130
134
  })
131
135
  .on('query-response', (_response, queryInfo) => {
132
- const delta = performance.now() - times[queryInfo.__knexUid];
133
- logger.trace(`[${delta.toFixed(3)}ms] ${queryInfo.sql} [${queryInfo.bindings.join(', ')}]`);
134
- delete times[queryInfo.__knexUid];
136
+ const time = times.get(queryInfo.__knexUid);
137
+ let delta;
138
+ if (time) {
139
+ delta = performance.now() - time;
140
+ times.delete(queryInfo.__knexUid);
141
+ }
142
+ logger.trace(`[${delta ? delta.toFixed(3) : '?'}ms] ${queryInfo.sql} [${(queryInfo.bindings ?? []).join(', ')}]`);
135
143
  });
136
144
  return database;
137
145
  }
@@ -7,7 +7,7 @@ export async function up(knex) {
7
7
  ]);
8
8
  }
9
9
  export async function down(knex) {
10
- const helper = await getHelpers(knex).schema;
10
+ const helper = getHelpers(knex).schema;
11
11
  const opts = {
12
12
  nullable: false,
13
13
  length: 255,
@@ -0,0 +1,33 @@
1
+ import { getHelpers } from '../helpers/index.js';
2
+ export async function up(knex) {
3
+ const helper = getHelpers(knex).schema;
4
+ const isMysql = helper.isOneOfClients(['mysql']);
5
+ if (isMysql) {
6
+ // Knex creates invalid statement on MySQL, see https://github.com/knex/knex/issues/1888
7
+ await knex.schema.raw('ALTER TABLE `directus_files` CHANGE `uploaded_on` `created_on` TIMESTAMP NOT NULL DEFAULT current_timestamp();');
8
+ }
9
+ else {
10
+ await knex.schema.alterTable('directus_files', (table) => {
11
+ table.renameColumn('uploaded_on', 'created_on');
12
+ });
13
+ }
14
+ await knex.schema.alterTable('directus_files', (table) => {
15
+ table.timestamp('uploaded_on');
16
+ });
17
+ await knex('directus_files').update('uploaded_on', knex.ref('created_on'));
18
+ }
19
+ export async function down(knex) {
20
+ await knex.schema.alterTable('directus_files', (table) => {
21
+ table.dropColumn('uploaded_on');
22
+ });
23
+ const helper = getHelpers(knex).schema;
24
+ const isMysql = helper.isOneOfClients(['mysql']);
25
+ if (isMysql) {
26
+ await knex.schema.raw('ALTER TABLE `directus_files` CHANGE `created_on` `uploaded_on` TIMESTAMP NOT NULL DEFAULT current_timestamp();');
27
+ }
28
+ else {
29
+ await knex.schema.alterTable('directus_files', (table) => {
30
+ table.renameColumn('created_on', 'uploaded_on');
31
+ });
32
+ }
33
+ }
@@ -0,0 +1,6 @@
1
+ import type { Knex } from 'knex';
2
+ import type { Permission } from '@directus/types';
3
+ export declare function mergePermissions(strategy: 'and' | 'or', ...permissions: Permission[][]): any[];
4
+ export declare function mergePermission(strategy: 'and' | 'or', currentPerm: Permission, newPerm: Permission): Omit<Permission, 'id' | 'system'>;
5
+ export declare function up(knex: Knex): Promise<void>;
6
+ export declare function down(knex: Knex): Promise<void>;