@directus/api 16.0.0 → 17.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.
Files changed (92) hide show
  1. package/dist/controllers/items.js +8 -7
  2. package/dist/controllers/permissions.js +11 -2
  3. package/dist/controllers/utils.js +13 -32
  4. package/dist/extensions/lib/sandbox/generate-api-extensions-sandbox-entrypoint.d.ts +1 -1
  5. package/dist/flows.js +2 -1
  6. package/dist/middleware/collection-exists.js +6 -6
  7. package/dist/middleware/respond.js +1 -1
  8. package/dist/operations/request/index.js +5 -5
  9. package/dist/request/agent-with-ip-validation.d.ts +11 -0
  10. package/dist/request/agent-with-ip-validation.js +34 -0
  11. package/dist/request/index.js +6 -5
  12. package/dist/request/is-denied-ip.d.ts +1 -0
  13. package/dist/request/{validate-ip.js → is-denied-ip.js} +10 -12
  14. package/dist/services/assets.js +1 -3
  15. package/dist/services/authorization.d.ts +1 -1
  16. package/dist/services/authorization.js +15 -3
  17. package/dist/services/collections.d.ts +3 -2
  18. package/dist/services/collections.js +1 -1
  19. package/dist/services/fields.js +2 -1
  20. package/dist/services/files.js +4 -3
  21. package/dist/services/graphql/index.js +3 -2
  22. package/dist/services/{import-export/index.d.ts → import-export.d.ts} +1 -1
  23. package/dist/services/{import-export/index.js → import-export.js} +12 -11
  24. package/dist/services/index.d.ts +1 -1
  25. package/dist/services/index.js +1 -1
  26. package/dist/services/items.js +2 -1
  27. package/dist/services/permissions.d.ts +3 -2
  28. package/dist/services/permissions.js +77 -2
  29. package/dist/services/relations.js +1 -1
  30. package/dist/services/roles.js +83 -15
  31. package/dist/services/server.js +2 -1
  32. package/dist/services/specifications.js +4 -3
  33. package/dist/services/utils.js +1 -1
  34. package/dist/telemetry/utils/get-user-item-count.js +2 -1
  35. package/dist/types/collection.d.ts +2 -13
  36. package/dist/types/items.d.ts +4 -12
  37. package/dist/types/items.js +0 -4
  38. package/dist/utils/get-field-system-rows.d.ts +2 -0
  39. package/dist/utils/get-field-system-rows.js +17 -0
  40. package/dist/utils/get-permissions.js +1 -1
  41. package/dist/utils/get-schema.js +3 -2
  42. package/dist/utils/merge-permissions-for-share.js +1 -1
  43. package/dist/utils/should-skip-cache.js +1 -1
  44. package/dist/websocket/handlers/items.js +2 -1
  45. package/dist/websocket/messages.d.ts +18 -18
  46. package/package.json +36 -35
  47. package/dist/database/system-data/app-access-permissions/app-access-permissions.yaml +0 -107
  48. package/dist/database/system-data/app-access-permissions/index.d.ts +0 -3
  49. package/dist/database/system-data/app-access-permissions/index.js +0 -17
  50. package/dist/database/system-data/app-access-permissions/schema-access-permissions.yaml +0 -17
  51. package/dist/database/system-data/collections/collections.yaml +0 -103
  52. package/dist/database/system-data/collections/index.d.ts +0 -2
  53. package/dist/database/system-data/collections/index.js +0 -9
  54. package/dist/database/system-data/fields/_defaults.yaml +0 -16
  55. package/dist/database/system-data/fields/activity.yaml +0 -83
  56. package/dist/database/system-data/fields/collections.yaml +0 -249
  57. package/dist/database/system-data/fields/dashboards.yaml +0 -20
  58. package/dist/database/system-data/fields/extensions.yaml +0 -10
  59. package/dist/database/system-data/fields/fields.yaml +0 -104
  60. package/dist/database/system-data/fields/files.yaml +0 -160
  61. package/dist/database/system-data/fields/flows.yaml +0 -26
  62. package/dist/database/system-data/fields/folders.yaml +0 -14
  63. package/dist/database/system-data/fields/index.d.ts +0 -2
  64. package/dist/database/system-data/fields/index.js +0 -33
  65. package/dist/database/system-data/fields/migrations.yaml +0 -10
  66. package/dist/database/system-data/fields/notifications.yaml +0 -15
  67. package/dist/database/system-data/fields/operations.yaml +0 -23
  68. package/dist/database/system-data/fields/panels.yaml +0 -29
  69. package/dist/database/system-data/fields/permissions.yaml +0 -37
  70. package/dist/database/system-data/fields/presets.yaml +0 -56
  71. package/dist/database/system-data/fields/relations.yaml +0 -34
  72. package/dist/database/system-data/fields/revisions.yaml +0 -30
  73. package/dist/database/system-data/fields/roles.yaml +0 -61
  74. package/dist/database/system-data/fields/sessions.yaml +0 -16
  75. package/dist/database/system-data/fields/settings.yaml +0 -471
  76. package/dist/database/system-data/fields/shares.yaml +0 -83
  77. package/dist/database/system-data/fields/translations.yaml +0 -27
  78. package/dist/database/system-data/fields/users.yaml +0 -224
  79. package/dist/database/system-data/fields/versions.yaml +0 -38
  80. package/dist/database/system-data/fields/webhooks.yaml +0 -141
  81. package/dist/database/system-data/relations/index.d.ts +0 -2
  82. package/dist/database/system-data/relations/index.js +0 -9
  83. package/dist/database/system-data/relations/relations.yaml +0 -197
  84. package/dist/request/request-interceptor.d.ts +0 -2
  85. package/dist/request/request-interceptor.js +0 -28
  86. package/dist/request/response-interceptor.d.ts +0 -2
  87. package/dist/request/response-interceptor.js +0 -5
  88. package/dist/request/validate-ip.d.ts +0 -1
  89. package/dist/services/import-export/import-worker.d.ts +0 -9
  90. package/dist/services/import-export/import-worker.js +0 -9
  91. package/dist/worker-pool.d.ts +0 -2
  92. package/dist/worker-pool.js +0 -19
@@ -1,7 +1,9 @@
1
+ import { ForbiddenError } from '@directus/errors';
1
2
  import { clearSystemCache, getCache } from '../cache.js';
2
- import { appAccessMinimalPermissions } from '../database/system-data/app-access-permissions/index.js';
3
- import { ItemsService } from './items.js';
3
+ import { appAccessMinimalPermissions } from '@directus/system-data';
4
4
  import { filterItems } from '../utils/filter-items.js';
5
+ import { AuthorizationService } from './authorization.js';
6
+ import { ItemsService } from './items.js';
5
7
  export class PermissionsService extends ItemsService {
6
8
  systemCache;
7
9
  constructor(options) {
@@ -95,4 +97,77 @@ export class PermissionsService extends ItemsService {
95
97
  }
96
98
  return res;
97
99
  }
100
+ async getItemPermissions(collection, primaryKey) {
101
+ if (!this.accountability?.user)
102
+ throw new ForbiddenError();
103
+ if (this.accountability?.admin) {
104
+ return {
105
+ update: { access: true },
106
+ delete: { access: true },
107
+ share: { access: true },
108
+ };
109
+ }
110
+ const itemPermissions = {
111
+ update: { access: false },
112
+ delete: { access: false },
113
+ share: { access: false },
114
+ };
115
+ let updateAction = 'update';
116
+ const schema = this.schema.collections[collection];
117
+ if (schema?.singleton) {
118
+ const itemsService = new ItemsService(collection, {
119
+ knex: this.knex,
120
+ schema: this.schema,
121
+ });
122
+ const query = {
123
+ fields: [schema.primary],
124
+ limit: 1,
125
+ };
126
+ try {
127
+ const result = await itemsService.readByQuery(query);
128
+ if (!result[0])
129
+ updateAction = 'create';
130
+ }
131
+ catch {
132
+ updateAction = 'create';
133
+ }
134
+ }
135
+ const authorizationService = new AuthorizationService({
136
+ knex: this.knex,
137
+ accountability: this.accountability,
138
+ schema: this.schema,
139
+ });
140
+ await Promise.all(Object.keys(itemPermissions).map((key) => {
141
+ const action = key;
142
+ const checkAction = action === 'update' ? updateAction : action;
143
+ return authorizationService
144
+ .checkAccess(checkAction, collection, primaryKey)
145
+ .then(() => (itemPermissions[action].access = true))
146
+ .catch(() => { });
147
+ }));
148
+ if (schema?.singleton && itemPermissions.update.access) {
149
+ const query = {
150
+ filter: {
151
+ _and: [
152
+ ...(this.accountability?.role ? [{ role: { _eq: this.accountability.role } }] : []),
153
+ { collection: { _eq: collection } },
154
+ { action: { _eq: updateAction } },
155
+ ],
156
+ },
157
+ fields: ['presets', 'fields'],
158
+ };
159
+ try {
160
+ const result = await this.readByQuery(query);
161
+ const permission = result[0];
162
+ if (permission) {
163
+ itemPermissions.update.presets = permission['presets'];
164
+ itemPermissions.update.fields = permission['fields'];
165
+ }
166
+ }
167
+ catch {
168
+ // No permission
169
+ }
170
+ }
171
+ return itemPermissions;
172
+ }
98
173
  }
@@ -4,12 +4,12 @@ import { toArray } from '@directus/utils';
4
4
  import { clearSystemCache, getCache } from '../cache.js';
5
5
  import { getHelpers } from '../database/helpers/index.js';
6
6
  import getDatabase, { getSchemaInspector } from '../database/index.js';
7
- import { systemRelationRows } from '../database/system-data/relations/index.js';
8
7
  import emitter from '../emitter.js';
9
8
  import { getDefaultIndexName } from '../utils/get-default-index-name.js';
10
9
  import { getSchema } from '../utils/get-schema.js';
11
10
  import { ItemsService } from './items.js';
12
11
  import { PermissionsService } from './permissions.js';
12
+ import { systemRelationRows } from '@directus/system-data';
13
13
  export class RelationsService {
14
14
  knex;
15
15
  permissionsService;
@@ -15,7 +15,7 @@ export class RolesService extends ItemsService {
15
15
  .whereNotIn('id', excludeKeys)
16
16
  .andWhere({ admin_access: true })
17
17
  .first();
18
- const otherAdminRolesCount = +(otherAdminRoles?.count || 0);
18
+ const otherAdminRolesCount = Number(otherAdminRoles?.count ?? 0);
19
19
  if (otherAdminRolesCount === 0) {
20
20
  throw new UnprocessableContentError({ reason: `You can't delete the last admin role` });
21
21
  }
@@ -24,30 +24,98 @@ export class RolesService extends ItemsService {
24
24
  const role = await this.knex.select('admin_access').from('directus_roles').where('id', '=', key).first();
25
25
  if (!role)
26
26
  throw new ForbiddenError();
27
- // The users that will now be in this new non-admin role
28
- let userKeys = [];
27
+ const usersBefore = (await this.knex.select('id').from('directus_users').where('role', '=', key)).map((user) => user.id);
28
+ const usersAdded = [];
29
+ const usersUpdated = [];
30
+ const usersCreated = [];
31
+ const usersRemoved = [];
29
32
  if (Array.isArray(users)) {
30
- userKeys = users.map((user) => (typeof user === 'string' ? user : user['id'])).filter((id) => id);
33
+ const usersKept = [];
34
+ for (const user of users) {
35
+ if (typeof user === 'string') {
36
+ if (usersBefore.includes(user)) {
37
+ usersKept.push(user);
38
+ }
39
+ else {
40
+ usersAdded.push({ id: user });
41
+ }
42
+ }
43
+ else if (user.id) {
44
+ if (usersBefore.includes(user.id)) {
45
+ usersKept.push(user.id);
46
+ usersUpdated.push(user);
47
+ }
48
+ else {
49
+ usersAdded.push(user);
50
+ }
51
+ }
52
+ else {
53
+ usersCreated.push(user);
54
+ }
55
+ }
56
+ usersRemoved.push(...usersBefore.filter((user) => !usersKept.includes(user)));
31
57
  }
32
58
  else {
33
- userKeys = users.update.map((user) => user['id']).filter((id) => id);
59
+ for (const user of users.update) {
60
+ if (usersBefore.includes(user['id'])) {
61
+ usersUpdated.push(user);
62
+ }
63
+ else {
64
+ usersAdded.push(user);
65
+ }
66
+ }
67
+ usersCreated.push(...users.create);
68
+ usersRemoved.push(...users.delete);
69
+ }
70
+ if (role.admin_access === false || role.admin_access === 0) {
71
+ // Admin users might have moved in from other role, thus becoming non-admin
72
+ if (usersAdded.length > 0) {
73
+ const otherAdminUsers = await this.knex
74
+ .count('*', { as: 'count' })
75
+ .from('directus_users')
76
+ .leftJoin('directus_roles', 'directus_users.role', 'directus_roles.id')
77
+ .whereNotIn('directus_users.id', usersAdded)
78
+ .andWhere({ 'directus_roles.admin_access': true, status: 'active' })
79
+ .first();
80
+ const otherAdminUsersCount = Number(otherAdminUsers?.count ?? 0);
81
+ if (otherAdminUsersCount === 0) {
82
+ throw new UnprocessableContentError({ reason: `You can't remove the last admin user from the admin role` });
83
+ }
84
+ }
85
+ return;
34
86
  }
35
- const usersThatWereInRoleBefore = (await this.knex.select('id').from('directus_users').where('role', '=', key)).map((user) => user.id);
36
- const usersThatAreRemoved = usersThatWereInRoleBefore.filter((id) => Array.isArray(users) ? userKeys.includes(id) === false : users.delete.includes(id) === true);
37
- const usersThatAreAdded = Array.isArray(users) ? users : users.create;
38
- // If the role the users are moved to is an admin-role, and there's at least 1 (new) admin
39
- // user, we don't have to check for other admin
40
- // users
41
- if ((role.admin_access === true || role.admin_access === 1) && usersThatAreAdded.length > 0)
87
+ // Only added or created new users
88
+ if (usersUpdated.length === 0 && usersRemoved.length === 0)
89
+ return;
90
+ // Active admin user(s) about to be created
91
+ if (usersCreated.some((user) => !('status' in user) || user.status === 'active'))
42
92
  return;
93
+ const usersDeactivated = [...usersAdded, ...usersUpdated]
94
+ .filter((user) => 'status' in user && user.status !== 'active')
95
+ .map((user) => user.id);
96
+ const usersAddedNonDeactivated = usersAdded
97
+ .filter((user) => !usersDeactivated.includes(user.id))
98
+ .map((user) => user.id);
99
+ // Active user(s) about to become admin
100
+ if (usersAddedNonDeactivated.length > 0) {
101
+ const userCount = await this.knex
102
+ .count('*', { as: 'count' })
103
+ .from('directus_users')
104
+ .whereIn('id', usersAddedNonDeactivated)
105
+ .andWhere({ status: 'active' })
106
+ .first();
107
+ if (Number(userCount?.count ?? 0) > 0) {
108
+ return;
109
+ }
110
+ }
43
111
  const otherAdminUsers = await this.knex
44
112
  .count('*', { as: 'count' })
45
113
  .from('directus_users')
46
- .whereNotIn('directus_users.id', [...userKeys, ...usersThatAreRemoved])
47
- .andWhere({ 'directus_roles.admin_access': true })
48
114
  .leftJoin('directus_roles', 'directus_users.role', 'directus_roles.id')
115
+ .whereNotIn('directus_users.id', [...usersDeactivated, ...usersRemoved])
116
+ .andWhere({ 'directus_roles.admin_access': true, status: 'active' })
49
117
  .first();
50
- const otherAdminUsersCount = +(otherAdminUsers?.count || 0);
118
+ const otherAdminUsersCount = Number(otherAdminUsers?.count ?? 0);
51
119
  if (otherAdminUsersCount === 0) {
52
120
  throw new UnprocessableContentError({ reason: `You can't remove the last admin user from the admin role` });
53
121
  }
@@ -41,7 +41,8 @@ export class ServerService {
41
41
  'theme_dark_overrides',
42
42
  'default_language',
43
43
  'public_foreground',
44
- 'public_background',
44
+ 'public_background.id',
45
+ 'public_background.type',
45
46
  'public_favicon',
46
47
  'public_note',
47
48
  'custom_css',
@@ -8,6 +8,7 @@ import getDatabase from '../database/index.js';
8
8
  import { getRelationType } from '../utils/get-relation-type.js';
9
9
  import { reduceSchema } from '../utils/reduce-schema.js';
10
10
  import { GraphQLService } from './graphql/index.js';
11
+ import { isSystemCollection } from '@directus/system-data';
11
12
  const env = useEnv();
12
13
  export class SpecificationService {
13
14
  accountability;
@@ -77,7 +78,7 @@ class OASSpecsService {
77
78
  }
78
79
  }
79
80
  for (const collection of collections) {
80
- const isSystem = collection.collection.startsWith('directus_');
81
+ const isSystem = isSystemCollection(collection.collection);
81
82
  // If the collection is one of the system collections, pull the tag from the static spec
82
83
  if (isSystem) {
83
84
  for (const tag of spec.tags) {
@@ -106,7 +107,7 @@ class OASSpecsService {
106
107
  if (!tags)
107
108
  return paths;
108
109
  for (const tag of tags) {
109
- const isSystem = 'x-collection' in tag === false || tag['x-collection'].startsWith('directus_');
110
+ const isSystem = 'x-collection' in tag === false || isSystemCollection(tag['x-collection']);
110
111
  if (isSystem) {
111
112
  for (const [path, pathItem] of Object.entries(spec.paths)) {
112
113
  for (const [method, operation] of Object.entries(pathItem)) {
@@ -268,7 +269,7 @@ class OASSpecsService {
268
269
  const tag = tags.find((tag) => tag['x-collection'] === collection.collection);
269
270
  if (!tag)
270
271
  continue;
271
- const isSystem = collection.collection.startsWith('directus_');
272
+ const isSystem = isSystemCollection(collection.collection);
272
273
  const fieldsInCollection = Object.values(collection.fields);
273
274
  if (isSystem) {
274
275
  const schemaComponent = cloneDeep(spec.components.schemas[tag.name]);
@@ -1,6 +1,6 @@
1
1
  import { flushCaches, getCache } from '../cache.js';
2
2
  import getDatabase from '../database/index.js';
3
- import { systemCollectionRows } from '../database/system-data/collections/index.js';
3
+ import { systemCollectionRows } from '@directus/system-data';
4
4
  import emitter from '../emitter.js';
5
5
  import { ForbiddenError, InvalidPayloadError } from '@directus/errors';
6
6
  import { shouldClearCache } from '../utils/should-clear-cache.js';
@@ -1,6 +1,7 @@
1
1
  import {} from 'knex';
2
2
  import { getSchema } from '../../utils/get-schema.js';
3
3
  import { getItemCount } from './get-item-count.js';
4
+ import { isSystemCollection } from '@directus/system-data';
4
5
  /**
5
6
  * Sum all passed values together. Meant to be used with .reduce()
6
7
  */
@@ -10,7 +11,7 @@ export const sum = (acc, val) => (acc += val);
10
11
  */
11
12
  export const getUserItemCount = async (db) => {
12
13
  const schema = await getSchema({ database: db });
13
- const userCollections = Object.keys(schema.collections).filter((collection) => collection.startsWith('directus_') === false);
14
+ const userCollections = Object.keys(schema.collections).filter((collection) => isSystemCollection(collection) === false);
14
15
  const counts = await getItemCount(db, userCollections);
15
16
  const collections = userCollections.length;
16
17
  const items = Object.values(counts).reduce(sum, 0);
@@ -1,20 +1,9 @@
1
1
  import type { Field } from '@directus/types';
2
2
  import type { Table } from '@directus/schema';
3
- export type CollectionMeta = {
4
- collection: string;
5
- note: string | null;
6
- hidden: boolean;
7
- singleton: boolean;
8
- icon: string | null;
9
- translations: Record<string, string>;
10
- versioning: boolean;
11
- item_duplication_fields: string[] | null;
12
- accountability: 'all' | 'accountability' | null;
13
- group: string | null;
14
- };
3
+ import type { BaseCollectionMeta } from '@directus/system-data';
15
4
  export type Collection = {
16
5
  collection: string;
17
6
  fields?: Field[];
18
- meta: CollectionMeta | null;
7
+ meta: BaseCollectionMeta | null;
19
8
  schema: Table | null;
20
9
  };
@@ -1,20 +1,12 @@
1
- /**
2
- * I know this looks a little silly, but it allows us to explicitly differentiate between when we're
3
- * expecting an item vs any other generic object.
4
- */
5
1
  import type { DirectusError } from '@directus/errors';
6
2
  import type { EventContext } from '@directus/types';
7
3
  import type { MutationTracker } from '../services/items.js';
8
4
  export type Item = Record<string, any>;
9
5
  export type PrimaryKey = string | number;
10
- export type Alterations = {
11
- create: {
12
- [key: string]: any;
13
- }[];
14
- update: {
15
- [key: string]: any;
16
- }[];
17
- delete: (number | string)[];
6
+ export type Alterations<T extends Item = Item, K extends keyof T | undefined = undefined> = {
7
+ create: Partial<T>[];
8
+ update: (K extends keyof T ? Partial<T> & Pick<T, K> : Partial<T>)[];
9
+ delete: (K extends keyof T ? T[K] : PrimaryKey)[];
18
10
  };
19
11
  export type MutationOptions = {
20
12
  /**
@@ -1,5 +1 @@
1
- /**
2
- * I know this looks a little silly, but it allows us to explicitly differentiate between when we're
3
- * expecting an item vs any other generic object.
4
- */
5
1
  export {};
@@ -0,0 +1,2 @@
1
+ import type { FieldMeta } from '@directus/types';
2
+ export declare function getSystemFieldRowsWithAuthProviders(): FieldMeta[];
@@ -0,0 +1,17 @@
1
+ import { systemFieldRows } from '@directus/system-data';
2
+ import formatTitle from '@directus/format-title';
3
+ import { getAuthProviders } from './get-auth-providers.js';
4
+ // Dynamically populate auth providers field
5
+ export function getSystemFieldRowsWithAuthProviders() {
6
+ return systemFieldRows.map((systemField) => {
7
+ if (systemField.collection === 'directus_users' && systemField.field === 'provider') {
8
+ if (!systemField.options)
9
+ systemField.options = {};
10
+ systemField.options['choices'] = getAuthProviders().map(({ name }) => ({
11
+ text: formatTitle(name),
12
+ value: name,
13
+ }));
14
+ }
15
+ return systemField;
16
+ });
17
+ }
@@ -4,7 +4,7 @@ import { cloneDeep } from 'lodash-es';
4
4
  import hash from 'object-hash';
5
5
  import { getCache, getCacheValue, getSystemCache, setCacheValue, setSystemCache } from '../cache.js';
6
6
  import getDatabase from '../database/index.js';
7
- import { appAccessMinimalPermissions } from '../database/system-data/app-access-permissions/index.js';
7
+ import { appAccessMinimalPermissions } from '@directus/system-data';
8
8
  import { useLogger } from '../logger.js';
9
9
  import { RolesService } from '../services/roles.js';
10
10
  import { UsersService } from '../services/users.js';
@@ -5,12 +5,12 @@ import { mapValues } from 'lodash-es';
5
5
  import { getSchemaCache, setSchemaCache } from '../cache.js';
6
6
  import { ALIAS_TYPES } from '../constants.js';
7
7
  import getDatabase from '../database/index.js';
8
- import { systemCollectionRows } from '../database/system-data/collections/index.js';
9
- import { systemFieldRows } from '../database/system-data/fields/index.js';
10
8
  import { useLogger } from '../logger.js';
11
9
  import { RelationsService } from '../services/relations.js';
12
10
  import getDefaultValue from './get-default-value.js';
13
11
  import getLocalType from './get-local-type.js';
12
+ import { systemCollectionRows } from '@directus/system-data';
13
+ import { getSystemFieldRowsWithAuthProviders } from './get-field-system-rows.js';
14
14
  const logger = useLogger();
15
15
  export async function getSchema(options) {
16
16
  const env = useEnv();
@@ -49,6 +49,7 @@ async function getDatabaseSchema(database, schemaInspector) {
49
49
  collections: {},
50
50
  relations: [],
51
51
  };
52
+ const systemFieldRows = getSystemFieldRowsWithAuthProviders();
52
53
  const schemaOverview = await schemaInspector.overview();
53
54
  const collections = [
54
55
  ...(await database
@@ -1,5 +1,5 @@
1
1
  import { assign, set, uniq } from 'lodash-es';
2
- import { schemaPermissions } from '../database/system-data/app-access-permissions/index.js';
2
+ import { schemaPermissions } from '@directus/system-data';
3
3
  import { mergePermissions } from './merge-permissions.js';
4
4
  import { reduceSchema } from './reduce-schema.js';
5
5
  export function mergePermissionsForShare(currentPermissions, accountability, schema) {
@@ -1,7 +1,7 @@
1
1
  import { useEnv } from '@directus/env';
2
- import { getEndpoint } from '@directus/utils';
3
2
  import url from 'url';
4
3
  import { Url } from './url.js';
4
+ import { getEndpoint } from '@directus/utils';
5
5
  /**
6
6
  * Whether to skip caching for the current request
7
7
  *
@@ -1,6 +1,7 @@
1
1
  import emitter from '../../emitter.js';
2
2
  import { ItemsService, MetaService } from '../../services/index.js';
3
3
  import { getSchema } from '../../utils/get-schema.js';
4
+ import { isSystemCollection } from '@directus/system-data';
4
5
  import { sanitizeQuery } from '../../utils/sanitize-query.js';
5
6
  import { WebSocketError, handleWebSocketError } from '../errors.js';
6
7
  import { WebSocketItemsMessage } from '../messages.js';
@@ -26,7 +27,7 @@ export class ItemsHandler {
26
27
  const uid = message.uid;
27
28
  const accountability = client.accountability;
28
29
  const schema = await getSchema();
29
- if (!schema.collections[message.collection] || message.collection.startsWith('directus_')) {
30
+ if (!schema.collections[message.collection] || isSystemCollection(message.collection)) {
30
31
  throw new WebSocketError('items', 'INVALID_COLLECTION', 'The provided collection does not exists or is not accessible.', uid);
31
32
  }
32
33
  const isSingleton = !!schema.collections[message.collection]?.singleton;
@@ -163,53 +163,53 @@ export declare const WebSocketSubscribeMessage: z.ZodDiscriminatedUnion<"type",
163
163
  }, z.ZodTypeAny, "passthrough">>]>;
164
164
  export type WebSocketSubscribeMessage = z.infer<typeof WebSocketSubscribeMessage>;
165
165
  export declare const WebSocketItemsMessage: z.ZodUnion<[z.ZodObject<{
166
- type: z.ZodLiteral<"items">;
167
166
  collection: z.ZodString;
167
+ type: z.ZodLiteral<"items">;
168
168
  uid: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
169
169
  action: z.ZodLiteral<"create">;
170
170
  data: z.ZodUnion<[z.ZodArray<z.ZodType<Partial<Item>, z.ZodTypeDef, Partial<Item>>, "many">, z.ZodType<Partial<Item>, z.ZodTypeDef, Partial<Item>>]>;
171
171
  query: z.ZodOptional<z.ZodType<Query, z.ZodTypeDef, Query>>;
172
172
  }, "strip", z.ZodTypeAny, {
173
+ collection: string;
173
174
  type: "items";
174
175
  action: "create";
175
176
  data: (Partial<Item> | Partial<Item>[]) & (Partial<Item> | Partial<Item>[] | undefined);
176
- collection: string;
177
177
  uid?: string | number | undefined;
178
178
  query?: Query | undefined;
179
179
  }, {
180
+ collection: string;
180
181
  type: "items";
181
182
  action: "create";
182
183
  data: (Partial<Item> | Partial<Item>[]) & (Partial<Item> | Partial<Item>[] | undefined);
183
- collection: string;
184
184
  uid?: string | number | undefined;
185
185
  query?: Query | undefined;
186
186
  }>, z.ZodObject<{
187
- type: z.ZodLiteral<"items">;
188
187
  collection: z.ZodString;
188
+ type: z.ZodLiteral<"items">;
189
189
  uid: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
190
190
  action: z.ZodLiteral<"read">;
191
191
  ids: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodString, z.ZodNumber]>, "many">>;
192
192
  id: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
193
193
  query: z.ZodOptional<z.ZodType<Query, z.ZodTypeDef, Query>>;
194
194
  }, "strip", z.ZodTypeAny, {
195
+ collection: string;
195
196
  type: "items";
196
197
  action: "read";
197
- collection: string;
198
198
  uid?: string | number | undefined;
199
199
  ids?: (string | number)[] | undefined;
200
200
  id?: string | number | undefined;
201
201
  query?: Query | undefined;
202
202
  }, {
203
+ collection: string;
203
204
  type: "items";
204
205
  action: "read";
205
- collection: string;
206
206
  uid?: string | number | undefined;
207
207
  ids?: (string | number)[] | undefined;
208
208
  id?: string | number | undefined;
209
209
  query?: Query | undefined;
210
210
  }>, z.ZodObject<{
211
- type: z.ZodLiteral<"items">;
212
211
  collection: z.ZodString;
212
+ type: z.ZodLiteral<"items">;
213
213
  uid: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
214
214
  action: z.ZodLiteral<"update">;
215
215
  data: z.ZodType<Partial<Item>, z.ZodTypeDef, Partial<Item>>;
@@ -217,43 +217,43 @@ export declare const WebSocketItemsMessage: z.ZodUnion<[z.ZodObject<{
217
217
  id: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
218
218
  query: z.ZodOptional<z.ZodType<Query, z.ZodTypeDef, Query>>;
219
219
  }, "strip", z.ZodTypeAny, {
220
+ collection: string;
220
221
  type: "items";
221
222
  action: "update";
222
223
  data: Partial<Item>;
223
- collection: string;
224
224
  uid?: string | number | undefined;
225
225
  ids?: (string | number)[] | undefined;
226
226
  id?: string | number | undefined;
227
227
  query?: Query | undefined;
228
228
  }, {
229
+ collection: string;
229
230
  type: "items";
230
231
  action: "update";
231
232
  data: Partial<Item>;
232
- collection: string;
233
233
  uid?: string | number | undefined;
234
234
  ids?: (string | number)[] | undefined;
235
235
  id?: string | number | undefined;
236
236
  query?: Query | undefined;
237
237
  }>, z.ZodObject<{
238
- type: z.ZodLiteral<"items">;
239
238
  collection: z.ZodString;
239
+ type: z.ZodLiteral<"items">;
240
240
  uid: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
241
241
  action: z.ZodLiteral<"delete">;
242
242
  ids: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodString, z.ZodNumber]>, "many">>;
243
243
  id: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
244
244
  query: z.ZodOptional<z.ZodType<Query, z.ZodTypeDef, Query>>;
245
245
  }, "strip", z.ZodTypeAny, {
246
+ collection: string;
246
247
  type: "items";
247
248
  action: "delete";
248
- collection: string;
249
249
  uid?: string | number | undefined;
250
250
  ids?: (string | number)[] | undefined;
251
251
  id?: string | number | undefined;
252
252
  query?: Query | undefined;
253
253
  }, {
254
+ collection: string;
254
255
  type: "items";
255
256
  action: "delete";
256
- collection: string;
257
257
  uid?: string | number | undefined;
258
258
  ids?: (string | number)[] | undefined;
259
259
  id?: string | number | undefined;
@@ -266,14 +266,14 @@ export declare const WebSocketEvent: z.ZodDiscriminatedUnion<"action", [z.ZodObj
266
266
  payload: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
267
267
  key: z.ZodUnion<[z.ZodString, z.ZodNumber]>;
268
268
  }, "strip", z.ZodTypeAny, {
269
+ collection: string;
269
270
  key: string | number;
270
271
  action: "create";
271
- collection: string;
272
272
  payload?: Record<string, any> | undefined;
273
273
  }, {
274
+ collection: string;
274
275
  key: string | number;
275
276
  action: "create";
276
- collection: string;
277
277
  payload?: Record<string, any> | undefined;
278
278
  }>, z.ZodObject<{
279
279
  action: z.ZodLiteral<"update">;
@@ -281,14 +281,14 @@ export declare const WebSocketEvent: z.ZodDiscriminatedUnion<"action", [z.ZodObj
281
281
  payload: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
282
282
  keys: z.ZodArray<z.ZodUnion<[z.ZodString, z.ZodNumber]>, "many">;
283
283
  }, "strip", z.ZodTypeAny, {
284
+ collection: string;
284
285
  action: "update";
285
286
  keys: (string | number)[];
286
- collection: string;
287
287
  payload?: Record<string, any> | undefined;
288
288
  }, {
289
+ collection: string;
289
290
  action: "update";
290
291
  keys: (string | number)[];
291
- collection: string;
292
292
  payload?: Record<string, any> | undefined;
293
293
  }>, z.ZodObject<{
294
294
  action: z.ZodLiteral<"delete">;
@@ -296,14 +296,14 @@ export declare const WebSocketEvent: z.ZodDiscriminatedUnion<"action", [z.ZodObj
296
296
  payload: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
297
297
  keys: z.ZodArray<z.ZodUnion<[z.ZodString, z.ZodNumber]>, "many">;
298
298
  }, "strip", z.ZodTypeAny, {
299
+ collection: string;
299
300
  action: "delete";
300
301
  keys: (string | number)[];
301
- collection: string;
302
302
  payload?: Record<string, any> | undefined;
303
303
  }, {
304
+ collection: string;
304
305
  action: "delete";
305
306
  keys: (string | number)[];
306
- collection: string;
307
307
  payload?: Record<string, any> | undefined;
308
308
  }>]>;
309
309
  export type WebSocketEvent = z.infer<typeof WebSocketEvent>;