@directus/api 18.2.1 → 19.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/app.js +0 -3
- package/dist/auth/drivers/ldap.js +1 -1
- package/dist/auth/drivers/local.js +1 -1
- package/dist/auth/drivers/oauth2.js +1 -1
- package/dist/auth/drivers/openid.js +1 -1
- package/dist/cache.js +1 -1
- package/dist/cli/utils/create-env/env-stub.liquid +2 -2
- package/dist/controllers/activity.js +1 -1
- package/dist/controllers/assets.js +9 -12
- package/dist/controllers/auth.js +7 -6
- package/dist/controllers/collections.js +1 -2
- package/dist/controllers/dashboards.js +1 -2
- package/dist/controllers/extensions.js +30 -0
- package/dist/controllers/fields.js +1 -3
- package/dist/controllers/flows.js +1 -2
- package/dist/controllers/folders.js +1 -2
- package/dist/controllers/items.js +3 -4
- package/dist/controllers/notifications.js +1 -2
- package/dist/controllers/operations.js +1 -2
- package/dist/controllers/panels.js +1 -2
- package/dist/controllers/presets.js +1 -2
- package/dist/controllers/roles.js +1 -2
- package/dist/controllers/translations.js +1 -2
- package/dist/controllers/users.js +1 -2
- package/dist/controllers/webhooks.js +10 -74
- package/dist/database/migrations/20240122A-add-report-url-fields.d.ts +3 -0
- package/dist/database/migrations/20240122A-add-report-url-fields.js +14 -0
- package/dist/database/migrations/20240204A-marketplace.js +17 -5
- package/dist/database/migrations/20240305A-change-useragent-type.d.ts +3 -0
- package/dist/database/migrations/20240305A-change-useragent-type.js +19 -0
- package/dist/database/migrations/20240311A-deprecate-webhooks.d.ts +13 -0
- package/dist/database/migrations/20240311A-deprecate-webhooks.js +125 -0
- package/dist/database/run-ast.js +4 -3
- package/dist/extensions/manager.d.ts +1 -0
- package/dist/extensions/manager.js +4 -1
- package/dist/middleware/authenticate.js +1 -1
- package/dist/services/activity.d.ts +2 -1
- package/dist/services/authorization.d.ts +2 -2
- package/dist/services/collections.d.ts +1 -1
- package/dist/services/collections.js +8 -7
- package/dist/services/extensions.d.ts +3 -0
- package/dist/services/extensions.js +42 -10
- package/dist/services/fields.d.ts +2 -1
- package/dist/services/fields.js +37 -7
- package/dist/services/files.d.ts +2 -2
- package/dist/services/flows.d.ts +2 -2
- package/dist/services/graphql/index.d.ts +2 -2
- package/dist/services/graphql/index.js +5 -0
- package/dist/services/import-export.js +4 -3
- package/dist/services/items.d.ts +2 -2
- package/dist/services/items.js +9 -8
- package/dist/services/notifications.d.ts +2 -2
- package/dist/services/operations.d.ts +2 -2
- package/dist/services/payload.d.ts +2 -2
- package/dist/services/permissions/index.d.ts +2 -2
- package/dist/services/relations.js +10 -3
- package/dist/services/revisions.d.ts +2 -1
- package/dist/services/roles.d.ts +2 -2
- package/dist/services/roles.js +2 -1
- package/dist/services/shares.d.ts +2 -1
- package/dist/services/shares.js +1 -1
- package/dist/services/tfa.d.ts +2 -1
- package/dist/services/tfa.js +1 -1
- package/dist/services/users.d.ts +2 -2
- package/dist/services/users.js +3 -2
- package/dist/services/utils.d.ts +2 -2
- package/dist/services/utils.js +2 -2
- package/dist/services/versions.d.ts +1 -1
- package/dist/services/webhooks.d.ts +8 -4
- package/dist/services/webhooks.js +15 -12
- package/dist/types/items.d.ts +1 -8
- package/dist/types/services.d.ts +1 -2
- package/dist/utils/apply-diff.js +2 -1
- package/dist/utils/get-ast-from-query.js +1 -1
- package/dist/utils/get-auth-providers.d.ts +3 -1
- package/dist/utils/get-auth-providers.js +15 -4
- package/dist/utils/get-cache-headers.js +0 -3
- package/dist/utils/get-schema.d.ts +1 -1
- package/dist/utils/get-schema.js +52 -29
- package/dist/utils/merge-version-data.js +1 -1
- package/dist/utils/transaction.d.ts +9 -0
- package/dist/utils/transaction.js +15 -0
- package/dist/utils/validate-keys.d.ts +1 -2
- package/dist/websocket/controllers/base.d.ts +1 -3
- package/dist/websocket/controllers/base.js +12 -3
- package/dist/websocket/utils/items.d.ts +1 -1
- package/license +1 -1
- package/package.json +39 -37
- package/dist/webhooks.d.ts +0 -4
- package/dist/webhooks.js +0 -80
package/dist/services/items.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Action } from '@directus/constants';
|
|
2
2
|
import { useEnv } from '@directus/env';
|
|
3
3
|
import { ForbiddenError, InvalidPayloadError } from '@directus/errors';
|
|
4
|
+
import { isSystemCollection } from '@directus/system-data';
|
|
4
5
|
import { assign, clone, cloneDeep, omit, pick, without } from 'lodash-es';
|
|
5
6
|
import { getCache } from '../cache.js';
|
|
6
7
|
import { translateDatabaseError } from '../database/errors/translate.js';
|
|
@@ -10,10 +11,10 @@ import runAST from '../database/run-ast.js';
|
|
|
10
11
|
import emitter from '../emitter.js';
|
|
11
12
|
import getASTFromQuery from '../utils/get-ast-from-query.js';
|
|
12
13
|
import { shouldClearCache } from '../utils/should-clear-cache.js';
|
|
14
|
+
import { transaction } from '../utils/transaction.js';
|
|
13
15
|
import { validateKeys } from '../utils/validate-keys.js';
|
|
14
16
|
import { AuthorizationService } from './authorization.js';
|
|
15
17
|
import { PayloadService } from './payload.js';
|
|
16
|
-
import { isSystemCollection } from '@directus/system-data';
|
|
17
18
|
const env = useEnv();
|
|
18
19
|
export class ItemsService {
|
|
19
20
|
collection;
|
|
@@ -82,7 +83,7 @@ export class ItemsService {
|
|
|
82
83
|
// changes in the DB if any of the parts contained within throws an error. This also means
|
|
83
84
|
// that any errors thrown in any nested relational changes will bubble up and cancel the whole
|
|
84
85
|
// update tree
|
|
85
|
-
const primaryKey = await this.knex
|
|
86
|
+
const primaryKey = await transaction(this.knex, async (trx) => {
|
|
86
87
|
// We're creating new services instances so they can use the transaction as their Knex interface
|
|
87
88
|
const payloadService = new PayloadService(this.collection, {
|
|
88
89
|
accountability: this.accountability,
|
|
@@ -255,11 +256,11 @@ export class ItemsService {
|
|
|
255
256
|
async createMany(data, opts = {}) {
|
|
256
257
|
if (!opts.mutationTracker)
|
|
257
258
|
opts.mutationTracker = this.createMutationTracker();
|
|
258
|
-
const { primaryKeys, nestedActionEvents } = await this.knex
|
|
259
|
+
const { primaryKeys, nestedActionEvents } = await transaction(this.knex, async (knex) => {
|
|
259
260
|
const service = new ItemsService(this.collection, {
|
|
260
261
|
accountability: this.accountability,
|
|
261
262
|
schema: this.schema,
|
|
262
|
-
knex:
|
|
263
|
+
knex: knex,
|
|
263
264
|
});
|
|
264
265
|
const primaryKeys = [];
|
|
265
266
|
const nestedActionEvents = [];
|
|
@@ -418,7 +419,7 @@ export class ItemsService {
|
|
|
418
419
|
const primaryKeyField = this.schema.collections[this.collection].primary;
|
|
419
420
|
const keys = [];
|
|
420
421
|
try {
|
|
421
|
-
await this.knex
|
|
422
|
+
await transaction(this.knex, async (trx) => {
|
|
422
423
|
const service = new ItemsService(this.collection, {
|
|
423
424
|
accountability: this.accountability,
|
|
424
425
|
knex: trx,
|
|
@@ -488,7 +489,7 @@ export class ItemsService {
|
|
|
488
489
|
if (opts.preMutationError) {
|
|
489
490
|
throw opts.preMutationError;
|
|
490
491
|
}
|
|
491
|
-
await this.knex
|
|
492
|
+
await transaction(this.knex, async (trx) => {
|
|
492
493
|
const payloadService = new PayloadService(this.collection, {
|
|
493
494
|
accountability: this.accountability,
|
|
494
495
|
knex: trx,
|
|
@@ -629,7 +630,7 @@ export class ItemsService {
|
|
|
629
630
|
async upsertMany(payloads, opts = {}) {
|
|
630
631
|
if (!opts.mutationTracker)
|
|
631
632
|
opts.mutationTracker = this.createMutationTracker();
|
|
632
|
-
const primaryKeys = await this.knex
|
|
633
|
+
const primaryKeys = await transaction(this.knex, async (trx) => {
|
|
633
634
|
const service = new ItemsService(this.collection, {
|
|
634
635
|
accountability: this.accountability,
|
|
635
636
|
schema: this.schema,
|
|
@@ -697,7 +698,7 @@ export class ItemsService {
|
|
|
697
698
|
accountability: this.accountability,
|
|
698
699
|
});
|
|
699
700
|
}
|
|
700
|
-
await this.knex
|
|
701
|
+
await transaction(this.knex, async (trx) => {
|
|
701
702
|
await trx(this.collection).whereIn(primaryKeyField, keys).delete();
|
|
702
703
|
if (this.accountability && this.schema.collections[this.collection].accountability !== null) {
|
|
703
704
|
const activityService = new ActivityService({
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Notification } from '@directus/types';
|
|
2
|
-
import type { AbstractServiceOptions, MutationOptions
|
|
1
|
+
import type { Notification, PrimaryKey } from '@directus/types';
|
|
2
|
+
import type { AbstractServiceOptions, MutationOptions } from '../types/index.js';
|
|
3
3
|
import { ItemsService } from './items.js';
|
|
4
4
|
import { MailService } from './mail/index.js';
|
|
5
5
|
import { UsersService } from './users.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { OperationRaw } from '@directus/types';
|
|
2
|
-
import type { AbstractServiceOptions,
|
|
1
|
+
import type { Item, OperationRaw, PrimaryKey } from '@directus/types';
|
|
2
|
+
import type { AbstractServiceOptions, MutationOptions } from '../types/index.js';
|
|
3
3
|
import { ItemsService } from './items.js';
|
|
4
4
|
export declare class OperationsService extends ItemsService<OperationRaw> {
|
|
5
5
|
constructor(options: AbstractServiceOptions);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { Accountability, SchemaOverview } from '@directus/types';
|
|
1
|
+
import type { Accountability, Item, PrimaryKey, SchemaOverview } from '@directus/types';
|
|
2
2
|
import type { Knex } from 'knex';
|
|
3
3
|
import type { Helpers } from '../database/helpers/index.js';
|
|
4
|
-
import type { AbstractServiceOptions, ActionEventParams,
|
|
4
|
+
import type { AbstractServiceOptions, ActionEventParams, MutationOptions } from '../types/index.js';
|
|
5
5
|
type Action = 'create' | 'read' | 'update';
|
|
6
6
|
type Transformers = {
|
|
7
7
|
[type: string]: (context: {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { ItemPermissions, PermissionsAction, Query } from '@directus/types';
|
|
1
|
+
import type { Item, ItemPermissions, PermissionsAction, PrimaryKey, Query } from '@directus/types';
|
|
2
2
|
import type Keyv from 'keyv';
|
|
3
|
-
import type { AbstractServiceOptions,
|
|
3
|
+
import type { AbstractServiceOptions, MutationOptions } from '../../types/index.js';
|
|
4
4
|
import type { QueryOptions } from '../items.js';
|
|
5
5
|
import { ItemsService } from '../items.js';
|
|
6
6
|
export declare class PermissionsService extends ItemsService {
|
|
@@ -8,6 +8,7 @@ import getDatabase, { getSchemaInspector } from '../database/index.js';
|
|
|
8
8
|
import emitter from '../emitter.js';
|
|
9
9
|
import { getDefaultIndexName } from '../utils/get-default-index-name.js';
|
|
10
10
|
import { getSchema } from '../utils/get-schema.js';
|
|
11
|
+
import { transaction } from '../utils/transaction.js';
|
|
11
12
|
import { ItemsService } from './items.js';
|
|
12
13
|
import { PermissionsService } from './permissions/index.js';
|
|
13
14
|
export class RelationsService {
|
|
@@ -150,7 +151,7 @@ export class RelationsService {
|
|
|
150
151
|
many_field: relation.field,
|
|
151
152
|
one_collection: relation.related_collection || null,
|
|
152
153
|
};
|
|
153
|
-
await this.knex
|
|
154
|
+
await transaction(this.knex, async (trx) => {
|
|
154
155
|
if (relation.related_collection) {
|
|
155
156
|
await trx.schema.alterTable(relation.collection, async (table) => {
|
|
156
157
|
this.alterType(table, relation, fieldSchema.nullable);
|
|
@@ -161,6 +162,9 @@ export class RelationsService {
|
|
|
161
162
|
if (relation.schema?.on_delete) {
|
|
162
163
|
builder.onDelete(relation.schema.on_delete);
|
|
163
164
|
}
|
|
165
|
+
if (relation.schema?.on_update) {
|
|
166
|
+
builder.onUpdate(relation.schema.on_update);
|
|
167
|
+
}
|
|
164
168
|
});
|
|
165
169
|
}
|
|
166
170
|
const relationsItemService = new ItemsService('directus_relations', {
|
|
@@ -218,7 +222,7 @@ export class RelationsService {
|
|
|
218
222
|
this.helpers.schema.preRelationChange(relation);
|
|
219
223
|
const nestedActionEvents = [];
|
|
220
224
|
try {
|
|
221
|
-
await this.knex
|
|
225
|
+
await transaction(this.knex, async (trx) => {
|
|
222
226
|
if (existingRelation.related_collection) {
|
|
223
227
|
await trx.schema.alterTable(collection, async (table) => {
|
|
224
228
|
let constraintName = getDefaultIndexName('foreign', collection, field);
|
|
@@ -236,6 +240,9 @@ export class RelationsService {
|
|
|
236
240
|
if (relation.schema?.on_delete) {
|
|
237
241
|
builder.onDelete(relation.schema.on_delete);
|
|
238
242
|
}
|
|
243
|
+
if (relation.schema?.on_update) {
|
|
244
|
+
builder.onUpdate(relation.schema.on_update);
|
|
245
|
+
}
|
|
239
246
|
});
|
|
240
247
|
}
|
|
241
248
|
const relationsItemService = new ItemsService('directus_relations', {
|
|
@@ -302,7 +309,7 @@ export class RelationsService {
|
|
|
302
309
|
const runPostColumnChange = await this.helpers.schema.preColumnChange();
|
|
303
310
|
const nestedActionEvents = [];
|
|
304
311
|
try {
|
|
305
|
-
await this.knex
|
|
312
|
+
await transaction(this.knex, async (trx) => {
|
|
306
313
|
const existingConstraints = await this.schemaInspector.foreignKeys();
|
|
307
314
|
const constraintNames = existingConstraints.map((key) => key.constraint_name);
|
|
308
315
|
if (existingRelation.schema?.constraint_name &&
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Item, PrimaryKey } from '@directus/types';
|
|
2
|
+
import type { AbstractServiceOptions, MutationOptions } from '../types/index.js';
|
|
2
3
|
import { ItemsService } from './items.js';
|
|
3
4
|
export declare class RevisionsService extends ItemsService {
|
|
4
5
|
constructor(options: AbstractServiceOptions);
|
package/dist/services/roles.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Query } from '@directus/types';
|
|
2
|
-
import type { AbstractServiceOptions,
|
|
1
|
+
import type { Item, PrimaryKey, Query } from '@directus/types';
|
|
2
|
+
import type { AbstractServiceOptions, MutationOptions } from '../types/index.js';
|
|
3
3
|
import { ItemsService } from './items.js';
|
|
4
4
|
export declare class RolesService extends ItemsService {
|
|
5
5
|
constructor(options: AbstractServiceOptions);
|
package/dist/services/roles.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ForbiddenError, InvalidPayloadError, UnprocessableContentError } from '@directus/errors';
|
|
2
2
|
import { getMatch } from 'ip-matching';
|
|
3
|
+
import { transaction } from '../utils/transaction.js';
|
|
3
4
|
import { ItemsService } from './items.js';
|
|
4
5
|
import { PermissionsService } from './permissions/index.js';
|
|
5
6
|
import { PresetsService } from './presets.js';
|
|
@@ -217,7 +218,7 @@ export class RolesService extends ItemsService {
|
|
|
217
218
|
catch (err) {
|
|
218
219
|
opts.preMutationError = err;
|
|
219
220
|
}
|
|
220
|
-
await this.knex
|
|
221
|
+
await transaction(this.knex, async (trx) => {
|
|
221
222
|
const itemsService = new ItemsService('directus_roles', {
|
|
222
223
|
knex: trx,
|
|
223
224
|
accountability: this.accountability,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Item, PrimaryKey } from '@directus/types';
|
|
2
|
+
import type { AbstractServiceOptions, LoginResult, MutationOptions } from '../types/index.js';
|
|
2
3
|
import { AuthorizationService } from './authorization.js';
|
|
3
4
|
import { ItemsService } from './items.js';
|
|
4
5
|
export declare class SharesService extends ItemsService {
|
package/dist/services/shares.js
CHANGED
|
@@ -2,6 +2,7 @@ import { useEnv } from '@directus/env';
|
|
|
2
2
|
import { ForbiddenError, InvalidCredentialsError } from '@directus/errors';
|
|
3
3
|
import argon2 from 'argon2';
|
|
4
4
|
import jwt from 'jsonwebtoken';
|
|
5
|
+
import { useLogger } from '../logger.js';
|
|
5
6
|
import { getMilliseconds } from '../utils/get-milliseconds.js';
|
|
6
7
|
import { md } from '../utils/md.js';
|
|
7
8
|
import { Url } from '../utils/url.js';
|
|
@@ -10,7 +11,6 @@ import { AuthorizationService } from './authorization.js';
|
|
|
10
11
|
import { ItemsService } from './items.js';
|
|
11
12
|
import { MailService } from './mail/index.js';
|
|
12
13
|
import { UsersService } from './users.js';
|
|
13
|
-
import { useLogger } from '../logger.js';
|
|
14
14
|
const env = useEnv();
|
|
15
15
|
const logger = useLogger();
|
|
16
16
|
export class SharesService extends ItemsService {
|
package/dist/services/tfa.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import type { PrimaryKey } from '@directus/types';
|
|
1
2
|
import type { Knex } from 'knex';
|
|
2
|
-
import type { AbstractServiceOptions
|
|
3
|
+
import type { AbstractServiceOptions } from '../types/index.js';
|
|
3
4
|
import { ItemsService } from './items.js';
|
|
4
5
|
export declare class TFAService {
|
|
5
6
|
knex: Knex;
|
package/dist/services/tfa.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { InvalidPayloadError } from '@directus/errors';
|
|
1
2
|
import { authenticator } from 'otplib';
|
|
2
3
|
import getDatabase from '../database/index.js';
|
|
3
|
-
import { InvalidPayloadError } from '@directus/errors';
|
|
4
4
|
import { ItemsService } from './items.js';
|
|
5
5
|
export class TFAService {
|
|
6
6
|
knex;
|
package/dist/services/users.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Query } from '@directus/types';
|
|
2
|
-
import type { AbstractServiceOptions,
|
|
1
|
+
import type { Item, PrimaryKey, Query } from '@directus/types';
|
|
2
|
+
import type { AbstractServiceOptions, MutationOptions } from '../types/index.js';
|
|
3
3
|
import { ItemsService } from './items.js';
|
|
4
4
|
export declare class UsersService extends ItemsService {
|
|
5
5
|
constructor(options: AbstractServiceOptions);
|
package/dist/services/users.js
CHANGED
|
@@ -7,14 +7,15 @@ import jwt from 'jsonwebtoken';
|
|
|
7
7
|
import { cloneDeep, isEmpty } from 'lodash-es';
|
|
8
8
|
import { performance } from 'perf_hooks';
|
|
9
9
|
import getDatabase from '../database/index.js';
|
|
10
|
+
import { useLogger } from '../logger.js';
|
|
10
11
|
import isUrlAllowed from '../utils/is-url-allowed.js';
|
|
11
12
|
import { verifyJWT } from '../utils/jwt.js';
|
|
12
13
|
import { stall } from '../utils/stall.js';
|
|
14
|
+
import { transaction } from '../utils/transaction.js';
|
|
13
15
|
import { Url } from '../utils/url.js';
|
|
14
16
|
import { ItemsService } from './items.js';
|
|
15
17
|
import { MailService } from './mail/index.js';
|
|
16
18
|
import { SettingsService } from './settings.js';
|
|
17
|
-
import { useLogger } from '../logger.js';
|
|
18
19
|
const env = useEnv();
|
|
19
20
|
const logger = useLogger();
|
|
20
21
|
export class UsersService extends ItemsService {
|
|
@@ -195,7 +196,7 @@ export class UsersService extends ItemsService {
|
|
|
195
196
|
opts.mutationTracker = this.createMutationTracker();
|
|
196
197
|
const primaryKeyField = this.schema.collections[this.collection].primary;
|
|
197
198
|
const keys = [];
|
|
198
|
-
await this.knex
|
|
199
|
+
await transaction(this.knex, async (trx) => {
|
|
199
200
|
const service = new UsersService({
|
|
200
201
|
accountability: this.accountability,
|
|
201
202
|
knex: trx,
|
package/dist/services/utils.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { Accountability, SchemaOverview } from '@directus/types';
|
|
1
|
+
import type { Accountability, PrimaryKey, SchemaOverview } from '@directus/types';
|
|
2
2
|
import type { Knex } from 'knex';
|
|
3
|
-
import type { AbstractServiceOptions
|
|
3
|
+
import type { AbstractServiceOptions } from '../types/index.js';
|
|
4
4
|
export declare class UtilsService {
|
|
5
5
|
knex: Knex;
|
|
6
6
|
accountability: Accountability | null;
|
package/dist/services/utils.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { ForbiddenError, InvalidPayloadError } from '@directus/errors';
|
|
2
|
+
import { systemCollectionRows } from '@directus/system-data';
|
|
1
3
|
import { flushCaches, getCache } from '../cache.js';
|
|
2
4
|
import getDatabase from '../database/index.js';
|
|
3
|
-
import { systemCollectionRows } from '@directus/system-data';
|
|
4
5
|
import emitter from '../emitter.js';
|
|
5
|
-
import { ForbiddenError, InvalidPayloadError } from '@directus/errors';
|
|
6
6
|
import { shouldClearCache } from '../utils/should-clear-cache.js';
|
|
7
7
|
export class UtilsService {
|
|
8
8
|
knex;
|
|
@@ -17,5 +17,5 @@ export declare class VersionsService extends ItemsService {
|
|
|
17
17
|
createMany(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
18
18
|
updateMany(keys: PrimaryKey[], data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
19
19
|
save(key: PrimaryKey, data: Partial<Item>): Promise<Partial<Item>>;
|
|
20
|
-
promote(version: PrimaryKey, mainHash: string, fields?: string[]): Promise<
|
|
20
|
+
promote(version: PrimaryKey, mainHash: string, fields?: string[]): Promise<PrimaryKey>;
|
|
21
21
|
}
|
|
@@ -1,11 +1,15 @@
|
|
|
1
|
+
import { type DirectusError } from '@directus/errors';
|
|
1
2
|
import type { Bus } from '@directus/memory';
|
|
2
|
-
import type {
|
|
3
|
+
import type { PrimaryKey } from '@directus/types';
|
|
4
|
+
import type { AbstractServiceOptions, MutationOptions, Webhook } from '../types/index.js';
|
|
3
5
|
import { ItemsService } from './items.js';
|
|
4
6
|
export declare class WebhooksService extends ItemsService<Webhook> {
|
|
5
7
|
messenger: Bus;
|
|
8
|
+
errorDeprecation: DirectusError;
|
|
6
9
|
constructor(options: AbstractServiceOptions);
|
|
7
|
-
createOne(
|
|
8
|
-
createMany(
|
|
9
|
-
|
|
10
|
+
createOne(): Promise<PrimaryKey>;
|
|
11
|
+
createMany(): Promise<PrimaryKey[]>;
|
|
12
|
+
updateBatch(): Promise<PrimaryKey[]>;
|
|
13
|
+
updateMany(): Promise<PrimaryKey[]>;
|
|
10
14
|
deleteMany(keys: PrimaryKey[], opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
11
15
|
}
|
|
@@ -1,25 +1,28 @@
|
|
|
1
|
+
import { ErrorCode, createError } from '@directus/errors';
|
|
1
2
|
import { useBus } from '../bus/index.js';
|
|
3
|
+
import { useLogger } from '../logger.js';
|
|
2
4
|
import { ItemsService } from './items.js';
|
|
5
|
+
const logger = useLogger();
|
|
3
6
|
export class WebhooksService extends ItemsService {
|
|
4
7
|
messenger;
|
|
8
|
+
errorDeprecation;
|
|
5
9
|
constructor(options) {
|
|
6
10
|
super('directus_webhooks', options);
|
|
7
11
|
this.messenger = useBus();
|
|
12
|
+
this.errorDeprecation = new (createError(ErrorCode.MethodNotAllowed, 'Webhooks are deprecated, use Flows instead', 405))();
|
|
13
|
+
logger.warn('Webhooks are deprecated and the WebhooksService will be removed in an upcoming release. Creating/Updating Webhooks is disabled, use Flows instead');
|
|
8
14
|
}
|
|
9
|
-
async createOne(
|
|
10
|
-
|
|
11
|
-
this.messenger.publish('webhooks', { type: 'reload' });
|
|
12
|
-
return result;
|
|
15
|
+
async createOne() {
|
|
16
|
+
throw this.errorDeprecation;
|
|
13
17
|
}
|
|
14
|
-
async createMany(
|
|
15
|
-
|
|
16
|
-
this.messenger.publish('webhooks', { type: 'reload' });
|
|
17
|
-
return result;
|
|
18
|
+
async createMany() {
|
|
19
|
+
throw this.errorDeprecation;
|
|
18
20
|
}
|
|
19
|
-
async
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
async updateBatch() {
|
|
22
|
+
throw this.errorDeprecation;
|
|
23
|
+
}
|
|
24
|
+
async updateMany() {
|
|
25
|
+
throw this.errorDeprecation;
|
|
23
26
|
}
|
|
24
27
|
async deleteMany(keys, opts) {
|
|
25
28
|
const result = await super.deleteMany(keys, opts);
|
package/dist/types/items.d.ts
CHANGED
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
import type { DirectusError } from '@directus/errors';
|
|
2
|
-
import type { EventContext } from '@directus/types';
|
|
2
|
+
import type { EventContext, PrimaryKey } from '@directus/types';
|
|
3
3
|
import type { MutationTracker } from '../services/items.js';
|
|
4
|
-
export type Item = Record<string, any>;
|
|
5
|
-
export type PrimaryKey = string | number;
|
|
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)[];
|
|
10
|
-
};
|
|
11
4
|
export type MutationOptions = {
|
|
12
5
|
/**
|
|
13
6
|
* Callback function that's fired whenever a revision is made in the mutation
|
package/dist/types/services.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import type { Accountability, Query, SchemaOverview } from '@directus/types';
|
|
1
|
+
import type { Accountability, Item, PrimaryKey, Query, SchemaOverview } from '@directus/types';
|
|
2
2
|
import type { Knex } from 'knex';
|
|
3
|
-
import type { Item, PrimaryKey } from './items.js';
|
|
4
3
|
export type AbstractServiceOptions = {
|
|
5
4
|
knex?: Knex | undefined;
|
|
6
5
|
accountability?: Accountability | null | undefined;
|
package/dist/utils/apply-diff.js
CHANGED
|
@@ -9,6 +9,7 @@ import { CollectionsService } from '../services/collections.js';
|
|
|
9
9
|
import { FieldsService } from '../services/fields.js';
|
|
10
10
|
import { RelationsService } from '../services/relations.js';
|
|
11
11
|
import { DiffKind } from '../types/index.js';
|
|
12
|
+
import { transaction } from '../utils/transaction.js';
|
|
12
13
|
import { getSchema } from './get-schema.js';
|
|
13
14
|
const logger = useLogger();
|
|
14
15
|
export async function applyDiff(currentSnapshot, snapshotDiff, options) {
|
|
@@ -22,7 +23,7 @@ export async function applyDiff(currentSnapshot, snapshotDiff, options) {
|
|
|
22
23
|
bypassLimits: true,
|
|
23
24
|
};
|
|
24
25
|
const runPostColumnChange = await helpers.schema.preColumnChange();
|
|
25
|
-
await
|
|
26
|
+
await transaction(database, async (trx) => {
|
|
26
27
|
const collectionsService = new CollectionsService({ knex: trx, schema });
|
|
27
28
|
const getNestedCollectionsToCreate = (currentLevelCollection) => snapshotDiff.collections.filter(({ diff }) => diff[0].rhs?.meta?.group === currentLevelCollection);
|
|
28
29
|
const getNestedCollectionsToDelete = (currentLevelCollection) => snapshotDiff.collections.filter(({ diff }) => diff[0].lhs?.meta?.group === currentLevelCollection);
|
|
@@ -73,7 +73,7 @@ export default async function getASTFromQuery(collection, query, schema, options
|
|
|
73
73
|
for (const fieldKey of fields) {
|
|
74
74
|
let name = fieldKey;
|
|
75
75
|
if (query.alias) {
|
|
76
|
-
// check for field alias (is
|
|
76
|
+
// check for field alias (is one of the key)
|
|
77
77
|
if (name in query.alias) {
|
|
78
78
|
name = query.alias[fieldKey];
|
|
79
79
|
}
|
|
@@ -1,10 +1,21 @@
|
|
|
1
1
|
import { useEnv } from '@directus/env';
|
|
2
2
|
import { toArray } from '@directus/utils';
|
|
3
|
-
export function getAuthProviders() {
|
|
3
|
+
export function getAuthProviders({ sessionOnly } = { sessionOnly: false }) {
|
|
4
4
|
const env = useEnv();
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
.
|
|
5
|
+
let providers = toArray(env['AUTH_PROVIDERS']).filter((provider) => provider && env[`AUTH_${provider.toUpperCase()}_DRIVER`]);
|
|
6
|
+
if (sessionOnly) {
|
|
7
|
+
providers = providers.filter((provider) => {
|
|
8
|
+
const driver = env[`AUTH_${provider.toUpperCase()}_DRIVER`];
|
|
9
|
+
// only the following 3 drivers require a mode selection
|
|
10
|
+
if (['oauth2', 'openid', 'saml'].includes(driver)) {
|
|
11
|
+
const mode = env[`AUTH_${provider.toUpperCase()}_MODE`];
|
|
12
|
+
// if mode is not defined it defaults to session
|
|
13
|
+
return !mode || mode === 'session';
|
|
14
|
+
}
|
|
15
|
+
return true;
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
return providers.map((provider) => ({
|
|
8
19
|
name: provider,
|
|
9
20
|
label: env[`AUTH_${provider.toUpperCase()}_LABEL`],
|
|
10
21
|
driver: env[`AUTH_${provider.toUpperCase()}_DRIVER`],
|
|
@@ -16,9 +16,6 @@ export function getCacheControlHeader(req, ttl, globalCacheSettings, personalize
|
|
|
16
16
|
// When the resource / current request shouldn't be cached
|
|
17
17
|
if (ttl === undefined || ttl < 0)
|
|
18
18
|
return 'no-cache';
|
|
19
|
-
// When the API cache can invalidate at any moment
|
|
20
|
-
if (globalCacheSettings && env['CACHE_AUTO_PURGE'] === true)
|
|
21
|
-
return 'no-cache';
|
|
22
19
|
const headerValues = [];
|
|
23
20
|
// When caching depends on the authentication status of the users
|
|
24
21
|
if (personalized) {
|
package/dist/utils/get-schema.js
CHANGED
|
@@ -1,47 +1,70 @@
|
|
|
1
1
|
import { useEnv } from '@directus/env';
|
|
2
2
|
import { createInspector } from '@directus/schema';
|
|
3
|
+
import { systemCollectionRows } from '@directus/system-data';
|
|
3
4
|
import { parseJSON, toArray } from '@directus/utils';
|
|
4
5
|
import { mapValues } from 'lodash-es';
|
|
6
|
+
import { useBus } from '../bus/index.js';
|
|
5
7
|
import { getSchemaCache, setSchemaCache } from '../cache.js';
|
|
6
8
|
import { ALIAS_TYPES } from '../constants.js';
|
|
7
9
|
import getDatabase from '../database/index.js';
|
|
10
|
+
import { useLock } from '../lock/index.js';
|
|
8
11
|
import { useLogger } from '../logger.js';
|
|
9
12
|
import { RelationsService } from '../services/relations.js';
|
|
10
13
|
import getDefaultValue from './get-default-value.js';
|
|
11
|
-
import getLocalType from './get-local-type.js';
|
|
12
|
-
import { systemCollectionRows } from '@directus/system-data';
|
|
13
14
|
import { getSystemFieldRowsWithAuthProviders } from './get-field-system-rows.js';
|
|
15
|
+
import getLocalType from './get-local-type.js';
|
|
14
16
|
const logger = useLogger();
|
|
15
|
-
export async function getSchema(options) {
|
|
17
|
+
export async function getSchema(options, attempt = 0) {
|
|
18
|
+
const MAX_ATTEMPTS = 3;
|
|
16
19
|
const env = useEnv();
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
let result;
|
|
20
|
-
if (!options?.bypassCache && env['CACHE_SCHEMA'] !== false) {
|
|
21
|
-
let cachedSchema;
|
|
22
|
-
try {
|
|
23
|
-
cachedSchema = await getSchemaCache();
|
|
24
|
-
}
|
|
25
|
-
catch (err) {
|
|
26
|
-
logger.warn(err, `[schema-cache] Couldn't retrieve cache. ${err}`);
|
|
27
|
-
}
|
|
28
|
-
if (cachedSchema) {
|
|
29
|
-
result = cachedSchema;
|
|
30
|
-
}
|
|
31
|
-
else {
|
|
32
|
-
result = await getDatabaseSchema(database, schemaInspector);
|
|
33
|
-
try {
|
|
34
|
-
await setSchemaCache(result);
|
|
35
|
-
}
|
|
36
|
-
catch (err) {
|
|
37
|
-
logger.warn(err, `[schema-cache] Couldn't save cache. ${err}`);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
20
|
+
if (attempt >= MAX_ATTEMPTS) {
|
|
21
|
+
throw new Error(`Failed to get Schema information: hit infinite loop`);
|
|
40
22
|
}
|
|
41
|
-
|
|
42
|
-
|
|
23
|
+
if (options?.bypassCache || env['CACHE_SCHEMA'] === false) {
|
|
24
|
+
const database = options?.database || getDatabase();
|
|
25
|
+
const schemaInspector = createInspector(database);
|
|
26
|
+
return await getDatabaseSchema(database, schemaInspector);
|
|
27
|
+
}
|
|
28
|
+
const cached = await getSchemaCache();
|
|
29
|
+
if (cached) {
|
|
30
|
+
return cached;
|
|
31
|
+
}
|
|
32
|
+
const lock = useLock();
|
|
33
|
+
const bus = useBus();
|
|
34
|
+
const lockKey = 'schemaCache--preparing';
|
|
35
|
+
const messageKey = 'schemaCache--done';
|
|
36
|
+
const processId = await lock.increment(lockKey);
|
|
37
|
+
const currentProcessShouldHandleOperation = processId === 1;
|
|
38
|
+
if (currentProcessShouldHandleOperation === false) {
|
|
39
|
+
logger.trace('Schema cache is prepared in another process, waiting for result.');
|
|
40
|
+
return new Promise((resolve) => {
|
|
41
|
+
const TIMEOUT = 10000;
|
|
42
|
+
let timeout;
|
|
43
|
+
const callback = async () => {
|
|
44
|
+
if (timeout)
|
|
45
|
+
clearTimeout(timeout);
|
|
46
|
+
const schema = await getSchema(options, attempt + 1);
|
|
47
|
+
resolve(schema);
|
|
48
|
+
bus.unsubscribe(messageKey, callback);
|
|
49
|
+
};
|
|
50
|
+
bus.subscribe(messageKey, callback);
|
|
51
|
+
timeout = setTimeout(async () => {
|
|
52
|
+
logger.trace('Did not receive schema callback message in time. Pulling schema...');
|
|
53
|
+
callback();
|
|
54
|
+
}, TIMEOUT);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
const database = options?.database || getDatabase();
|
|
59
|
+
const schemaInspector = createInspector(database);
|
|
60
|
+
const schema = await getDatabaseSchema(database, schemaInspector);
|
|
61
|
+
await setSchemaCache(schema);
|
|
62
|
+
return schema;
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
await lock.delete(lockKey);
|
|
66
|
+
bus.publish(messageKey, { ready: true });
|
|
43
67
|
}
|
|
44
|
-
return result;
|
|
45
68
|
}
|
|
46
69
|
async function getDatabaseSchema(database, schemaInspector) {
|
|
47
70
|
const env = useEnv();
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Knex } from 'knex';
|
|
2
|
+
/**
|
|
3
|
+
* Execute the given handler within the current transaction or a newly created one
|
|
4
|
+
* if the current knex state isn't a transaction yet.
|
|
5
|
+
*
|
|
6
|
+
* Can be used to ensure the handler is run within a transaction,
|
|
7
|
+
* while preventing nested transactions.
|
|
8
|
+
*/
|
|
9
|
+
export declare const transaction: <T = unknown>(knex: Knex, handler: (knex: Knex) => Promise<T>) => Promise<T>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Execute the given handler within the current transaction or a newly created one
|
|
3
|
+
* if the current knex state isn't a transaction yet.
|
|
4
|
+
*
|
|
5
|
+
* Can be used to ensure the handler is run within a transaction,
|
|
6
|
+
* while preventing nested transactions.
|
|
7
|
+
*/
|
|
8
|
+
export const transaction = (knex, handler) => {
|
|
9
|
+
if (knex.isTransaction) {
|
|
10
|
+
return handler(knex);
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
return knex.transaction((trx) => handler(trx));
|
|
14
|
+
}
|
|
15
|
+
};
|