@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.
- package/dist/controllers/items.js +8 -7
- package/dist/controllers/permissions.js +11 -2
- package/dist/controllers/utils.js +13 -32
- package/dist/extensions/lib/sandbox/generate-api-extensions-sandbox-entrypoint.d.ts +1 -1
- package/dist/flows.js +2 -1
- package/dist/middleware/collection-exists.js +6 -6
- package/dist/middleware/respond.js +1 -1
- package/dist/operations/request/index.js +5 -5
- package/dist/request/agent-with-ip-validation.d.ts +11 -0
- package/dist/request/agent-with-ip-validation.js +34 -0
- package/dist/request/index.js +6 -5
- package/dist/request/is-denied-ip.d.ts +1 -0
- package/dist/request/{validate-ip.js → is-denied-ip.js} +10 -12
- package/dist/services/assets.js +1 -3
- package/dist/services/authorization.d.ts +1 -1
- package/dist/services/authorization.js +15 -3
- package/dist/services/collections.d.ts +3 -2
- package/dist/services/collections.js +1 -1
- package/dist/services/fields.js +2 -1
- package/dist/services/files.js +4 -3
- package/dist/services/graphql/index.js +3 -2
- package/dist/services/{import-export/index.d.ts → import-export.d.ts} +1 -1
- package/dist/services/{import-export/index.js → import-export.js} +12 -11
- package/dist/services/index.d.ts +1 -1
- package/dist/services/index.js +1 -1
- package/dist/services/items.js +2 -1
- package/dist/services/permissions.d.ts +3 -2
- package/dist/services/permissions.js +77 -2
- package/dist/services/relations.js +1 -1
- package/dist/services/roles.js +83 -15
- package/dist/services/server.js +2 -1
- package/dist/services/specifications.js +4 -3
- package/dist/services/utils.js +1 -1
- package/dist/telemetry/utils/get-user-item-count.js +2 -1
- package/dist/types/collection.d.ts +2 -13
- package/dist/types/items.d.ts +4 -12
- package/dist/types/items.js +0 -4
- package/dist/utils/get-field-system-rows.d.ts +2 -0
- package/dist/utils/get-field-system-rows.js +17 -0
- package/dist/utils/get-permissions.js +1 -1
- package/dist/utils/get-schema.js +3 -2
- package/dist/utils/merge-permissions-for-share.js +1 -1
- package/dist/utils/should-skip-cache.js +1 -1
- package/dist/websocket/handlers/items.js +2 -1
- package/dist/websocket/messages.d.ts +18 -18
- package/package.json +36 -35
- package/dist/database/system-data/app-access-permissions/app-access-permissions.yaml +0 -107
- package/dist/database/system-data/app-access-permissions/index.d.ts +0 -3
- package/dist/database/system-data/app-access-permissions/index.js +0 -17
- package/dist/database/system-data/app-access-permissions/schema-access-permissions.yaml +0 -17
- package/dist/database/system-data/collections/collections.yaml +0 -103
- package/dist/database/system-data/collections/index.d.ts +0 -2
- package/dist/database/system-data/collections/index.js +0 -9
- package/dist/database/system-data/fields/_defaults.yaml +0 -16
- package/dist/database/system-data/fields/activity.yaml +0 -83
- package/dist/database/system-data/fields/collections.yaml +0 -249
- package/dist/database/system-data/fields/dashboards.yaml +0 -20
- package/dist/database/system-data/fields/extensions.yaml +0 -10
- package/dist/database/system-data/fields/fields.yaml +0 -104
- package/dist/database/system-data/fields/files.yaml +0 -160
- package/dist/database/system-data/fields/flows.yaml +0 -26
- package/dist/database/system-data/fields/folders.yaml +0 -14
- package/dist/database/system-data/fields/index.d.ts +0 -2
- package/dist/database/system-data/fields/index.js +0 -33
- package/dist/database/system-data/fields/migrations.yaml +0 -10
- package/dist/database/system-data/fields/notifications.yaml +0 -15
- package/dist/database/system-data/fields/operations.yaml +0 -23
- package/dist/database/system-data/fields/panels.yaml +0 -29
- package/dist/database/system-data/fields/permissions.yaml +0 -37
- package/dist/database/system-data/fields/presets.yaml +0 -56
- package/dist/database/system-data/fields/relations.yaml +0 -34
- package/dist/database/system-data/fields/revisions.yaml +0 -30
- package/dist/database/system-data/fields/roles.yaml +0 -61
- package/dist/database/system-data/fields/sessions.yaml +0 -16
- package/dist/database/system-data/fields/settings.yaml +0 -471
- package/dist/database/system-data/fields/shares.yaml +0 -83
- package/dist/database/system-data/fields/translations.yaml +0 -27
- package/dist/database/system-data/fields/users.yaml +0 -224
- package/dist/database/system-data/fields/versions.yaml +0 -38
- package/dist/database/system-data/fields/webhooks.yaml +0 -141
- package/dist/database/system-data/relations/index.d.ts +0 -2
- package/dist/database/system-data/relations/index.js +0 -9
- package/dist/database/system-data/relations/relations.yaml +0 -197
- package/dist/request/request-interceptor.d.ts +0 -2
- package/dist/request/request-interceptor.js +0 -28
- package/dist/request/response-interceptor.d.ts +0 -2
- package/dist/request/response-interceptor.js +0 -5
- package/dist/request/validate-ip.d.ts +0 -1
- package/dist/services/import-export/import-worker.d.ts +0 -9
- package/dist/services/import-export/import-worker.js +0 -9
- package/dist/worker-pool.d.ts +0 -2
- 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 '
|
|
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;
|
package/dist/services/roles.js
CHANGED
|
@@ -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 =
|
|
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
|
-
|
|
28
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
//
|
|
39
|
-
|
|
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 =
|
|
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
|
}
|
package/dist/services/server.js
CHANGED
|
@@ -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
|
|
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']
|
|
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
|
|
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]);
|
package/dist/services/utils.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { flushCaches, getCache } from '../cache.js';
|
|
2
2
|
import getDatabase from '../database/index.js';
|
|
3
|
-
import { systemCollectionRows } from '
|
|
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
|
|
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
|
-
|
|
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:
|
|
7
|
+
meta: BaseCollectionMeta | null;
|
|
19
8
|
schema: Table | null;
|
|
20
9
|
};
|
package/dist/types/items.d.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
/**
|
package/dist/types/items.js
CHANGED
|
@@ -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 '
|
|
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';
|
package/dist/utils/get-schema.js
CHANGED
|
@@ -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 '
|
|
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,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
|
|
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>;
|