@directus/api 22.2.0 → 23.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/drivers/ldap.js +14 -3
- package/dist/auth/drivers/oauth2.js +13 -2
- package/dist/auth/drivers/openid.js +13 -2
- package/dist/cache.js +4 -4
- package/dist/cli/commands/init/questions.d.ts +5 -5
- package/dist/cli/commands/schema/apply.d.ts +1 -0
- package/dist/cli/commands/schema/apply.js +20 -1
- package/dist/cli/index.js +1 -0
- package/dist/cli/utils/create-env/env-stub.liquid +1 -4
- package/dist/database/migrations/20210518A-add-foreign-key-constraints.js +1 -1
- package/dist/database/migrations/20240806A-permissions-policies.js +1 -1
- package/dist/extensions/lib/sandbox/generate-api-extensions-sandbox-entrypoint.d.ts +0 -3
- package/dist/extensions/lib/sandbox/register/route.d.ts +1 -2
- package/dist/logger/index.d.ts +2 -3
- package/dist/logger/logs-stream.d.ts +0 -1
- package/dist/mailer.js +0 -6
- package/dist/middleware/authenticate.d.ts +1 -3
- package/dist/middleware/error-handler.d.ts +0 -1
- package/dist/middleware/validate-batch.d.ts +1 -4
- package/dist/permissions/lib/fetch-permissions.d.ts +11 -1
- package/dist/permissions/modules/process-ast/utils/get-info-for-path.d.ts +2 -2
- package/dist/permissions/modules/validate-remaining-admin/validate-remaining-admin-users.d.ts +1 -2
- package/dist/permissions/utils/fetch-dynamic-variable-context.js +14 -6
- package/dist/permissions/utils/process-permissions.d.ts +11 -1
- package/dist/permissions/utils/process-permissions.js +6 -4
- package/dist/request/agent-with-ip-validation.d.ts +0 -1
- package/dist/server.d.ts +0 -3
- package/dist/services/assets.d.ts +0 -1
- package/dist/services/fields.js +0 -6
- package/dist/services/files/utils/get-metadata.d.ts +0 -1
- package/dist/services/files/utils/parse-image-metadata.d.ts +0 -1
- package/dist/services/files.d.ts +0 -1
- package/dist/services/import-export.d.ts +0 -1
- package/dist/services/tus/data-store.d.ts +0 -1
- package/dist/services/users.js +2 -2
- package/dist/types/graphql.d.ts +0 -1
- package/dist/utils/apply-query.d.ts +1 -1
- package/dist/utils/compress.d.ts +0 -1
- package/dist/utils/delete-from-require-cache.js +1 -1
- package/dist/utils/fetch-user-count/fetch-user-count.d.ts +1 -2
- package/dist/utils/generate-hash.js +2 -2
- package/dist/utils/get-address.d.ts +0 -3
- package/dist/utils/get-cache-headers.d.ts +0 -1
- package/dist/utils/get-cache-key.d.ts +0 -1
- package/dist/utils/get-column.d.ts +1 -1
- package/dist/utils/get-graphql-query-and-variables.d.ts +0 -1
- package/dist/utils/get-ip-from-req.d.ts +0 -1
- package/dist/utils/get-snapshot.js +1 -1
- package/dist/utils/sanitize-query.js +1 -1
- package/dist/utils/should-skip-cache.d.ts +0 -1
- package/dist/websocket/authenticate.js +1 -1
- package/dist/websocket/controllers/base.d.ts +1 -10
- package/dist/websocket/controllers/base.js +15 -21
- package/dist/websocket/controllers/graphql.d.ts +0 -3
- package/dist/websocket/controllers/index.d.ts +0 -3
- package/dist/websocket/controllers/logs.d.ts +4 -5
- package/dist/websocket/controllers/logs.js +7 -3
- package/dist/websocket/controllers/rest.d.ts +0 -3
- package/dist/websocket/controllers/rest.js +1 -1
- package/dist/websocket/types.d.ts +0 -6
- package/package.json +41 -42
|
@@ -172,7 +172,7 @@ export class LDAPAuthDriver extends AuthDriver {
|
|
|
172
172
|
}
|
|
173
173
|
const logger = useLogger();
|
|
174
174
|
await this.validateBindClient();
|
|
175
|
-
const { userDn, userScope, userAttribute, groupDn, groupScope, groupAttribute, defaultRoleId } = this.config;
|
|
175
|
+
const { userDn, userScope, userAttribute, groupDn, groupScope, groupAttribute, defaultRoleId, syncUserInfo } = this.config;
|
|
176
176
|
const userInfo = await this.fetchUserInfo(userDn, new ldap.EqualityFilter({
|
|
177
177
|
attribute: userAttribute ?? 'cn',
|
|
178
178
|
value: payload['identifier'],
|
|
@@ -201,11 +201,22 @@ export class LDAPAuthDriver extends AuthDriver {
|
|
|
201
201
|
if (userId) {
|
|
202
202
|
// Run hook so the end user has the chance to augment the
|
|
203
203
|
// user that is about to be updated
|
|
204
|
-
let
|
|
204
|
+
let emitPayload = {};
|
|
205
205
|
// Only sync roles if the AD groups are configured
|
|
206
206
|
if (groupDn) {
|
|
207
|
-
|
|
207
|
+
emitPayload = {
|
|
208
|
+
role: userRole?.id ?? defaultRoleId ?? null,
|
|
209
|
+
};
|
|
208
210
|
}
|
|
211
|
+
if (syncUserInfo) {
|
|
212
|
+
emitPayload = {
|
|
213
|
+
...emitPayload,
|
|
214
|
+
first_name: userInfo.firstName,
|
|
215
|
+
last_name: userInfo.lastName,
|
|
216
|
+
email: userInfo.email,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
const updatedUserPayload = await emitter.emitFilter(`auth.update`, emitPayload, { identifier: userInfo.dn, provider: this.config['provider'], providerPayload: { userInfo, userRole } }, { database: getDatabase(), schema: this.schema, accountability: null });
|
|
209
220
|
// Update user to update properties that might have changed
|
|
210
221
|
await this.usersService.updateOne(userId, updatedUserPayload);
|
|
211
222
|
return userId;
|
|
@@ -106,7 +106,7 @@ export class OAuth2AuthDriver extends LocalAuthDriver {
|
|
|
106
106
|
}
|
|
107
107
|
// Flatten response to support dot indexes
|
|
108
108
|
userInfo = flatten(userInfo);
|
|
109
|
-
const { provider, emailKey, identifierKey, allowPublicRegistration } = this.config;
|
|
109
|
+
const { provider, emailKey, identifierKey, allowPublicRegistration, syncUserInfo } = this.config;
|
|
110
110
|
const email = userInfo[emailKey ?? 'email'] ? String(userInfo[emailKey ?? 'email']) : undefined;
|
|
111
111
|
// Fallback to email if explicit identifier not found
|
|
112
112
|
const identifier = userInfo[identifierKey] ? String(userInfo[identifierKey]) : email;
|
|
@@ -127,7 +127,18 @@ export class OAuth2AuthDriver extends LocalAuthDriver {
|
|
|
127
127
|
if (userId) {
|
|
128
128
|
// Run hook so the end user has the chance to augment the
|
|
129
129
|
// user that is about to be updated
|
|
130
|
-
|
|
130
|
+
let emitPayload = {
|
|
131
|
+
auth_data: userPayload.auth_data,
|
|
132
|
+
};
|
|
133
|
+
if (syncUserInfo) {
|
|
134
|
+
emitPayload = {
|
|
135
|
+
...emitPayload,
|
|
136
|
+
first_name: userPayload.first_name,
|
|
137
|
+
last_name: userPayload.last_name,
|
|
138
|
+
email: userPayload.email,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
const updatedUserPayload = await emitter.emitFilter(`auth.update`, emitPayload, {
|
|
131
142
|
identifier,
|
|
132
143
|
provider: this.config['provider'],
|
|
133
144
|
providerPayload: { accessToken: tokenSet.access_token, userInfo },
|
|
@@ -125,7 +125,7 @@ export class OpenIDAuthDriver extends LocalAuthDriver {
|
|
|
125
125
|
}
|
|
126
126
|
// Flatten response to support dot indexes
|
|
127
127
|
userInfo = flatten(userInfo);
|
|
128
|
-
const { provider, identifierKey, allowPublicRegistration, requireVerifiedEmail } = this.config;
|
|
128
|
+
const { provider, identifierKey, allowPublicRegistration, requireVerifiedEmail, syncUserInfo } = this.config;
|
|
129
129
|
const email = userInfo['email'] ? String(userInfo['email']) : undefined;
|
|
130
130
|
// Fallback to email if explicit identifier not found
|
|
131
131
|
const identifier = userInfo[identifierKey ?? 'sub'] ? String(userInfo[identifierKey ?? 'sub']) : email;
|
|
@@ -146,7 +146,18 @@ export class OpenIDAuthDriver extends LocalAuthDriver {
|
|
|
146
146
|
if (userId) {
|
|
147
147
|
// Run hook so the end user has the chance to augment the
|
|
148
148
|
// user that is about to be updated
|
|
149
|
-
|
|
149
|
+
let emitPayload = {
|
|
150
|
+
auth_data: userPayload.auth_data,
|
|
151
|
+
};
|
|
152
|
+
if (syncUserInfo) {
|
|
153
|
+
emitPayload = {
|
|
154
|
+
...emitPayload,
|
|
155
|
+
first_name: userPayload.first_name,
|
|
156
|
+
last_name: userPayload.last_name,
|
|
157
|
+
email: userPayload.email,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
const updatedUserPayload = await emitter.emitFilter(`auth.update`, emitPayload, {
|
|
150
161
|
identifier,
|
|
151
162
|
provider: this.config['provider'],
|
|
152
163
|
providerPayload: { accessToken: tokenSet.access_token, userInfo },
|
package/dist/cache.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { useEnv } from '@directus/env';
|
|
2
|
-
import Keyv from 'keyv';
|
|
2
|
+
import Keyv, {} from 'keyv';
|
|
3
3
|
import { useBus } from './bus/index.js';
|
|
4
4
|
import { useLogger } from './logger/index.js';
|
|
5
|
+
import { clearCache as clearPermissionCache } from './permissions/cache.js';
|
|
5
6
|
import { redisConfigAvailable } from './redis/index.js';
|
|
6
7
|
import { compress, decompress } from './utils/compress.js';
|
|
7
8
|
import { getConfigFromEnv } from './utils/get-config-from-env.js';
|
|
8
9
|
import { getMilliseconds } from './utils/get-milliseconds.js';
|
|
9
10
|
import { validateEnv } from './utils/validate-env.js';
|
|
10
|
-
import { clearCache as clearPermissionCache } from './permissions/cache.js';
|
|
11
11
|
import { createRequire } from 'node:module';
|
|
12
12
|
const logger = useLogger();
|
|
13
13
|
const env = useEnv();
|
|
@@ -106,10 +106,10 @@ function getKeyvInstance(store, ttl, namespaceSuffix) {
|
|
|
106
106
|
function getConfig(store = 'memory', ttl, namespaceSuffix = '') {
|
|
107
107
|
const config = {
|
|
108
108
|
namespace: `${env['CACHE_NAMESPACE']}${namespaceSuffix}`,
|
|
109
|
-
ttl,
|
|
109
|
+
...(ttl && { ttl }),
|
|
110
110
|
};
|
|
111
111
|
if (store === 'redis') {
|
|
112
|
-
const KeyvRedis = require('@keyv/redis');
|
|
112
|
+
const { default: KeyvRedis } = require('@keyv/redis');
|
|
113
113
|
config.store = new KeyvRedis(env['REDIS'] || getConfigFromEnv('REDIS'), { useRedisSets: false });
|
|
114
114
|
}
|
|
115
115
|
return config;
|
|
@@ -4,18 +4,18 @@ export declare const databaseQuestions: {
|
|
|
4
4
|
filepath: string;
|
|
5
5
|
}) => Record<string, string>)[];
|
|
6
6
|
mysql2: (({ client }: {
|
|
7
|
-
client: Exclude<Driver,
|
|
7
|
+
client: Exclude<Driver, "sqlite3">;
|
|
8
8
|
}) => Record<string, any>)[];
|
|
9
9
|
pg: (({ client }: {
|
|
10
|
-
client: Exclude<Driver,
|
|
10
|
+
client: Exclude<Driver, "sqlite3">;
|
|
11
11
|
}) => Record<string, any>)[];
|
|
12
12
|
cockroachdb: (({ client }: {
|
|
13
|
-
client: Exclude<Driver,
|
|
13
|
+
client: Exclude<Driver, "sqlite3">;
|
|
14
14
|
}) => Record<string, any>)[];
|
|
15
15
|
oracledb: (({ client }: {
|
|
16
|
-
client: Exclude<Driver,
|
|
16
|
+
client: Exclude<Driver, "sqlite3">;
|
|
17
17
|
}) => Record<string, any>)[];
|
|
18
18
|
mssql: (({ client }: {
|
|
19
|
-
client: Exclude<Driver,
|
|
19
|
+
client: Exclude<Driver, "sqlite3">;
|
|
20
20
|
}) => Record<string, any>)[];
|
|
21
21
|
};
|
|
@@ -11,6 +11,22 @@ import { isNestedMetaUpdate } from '../../../utils/apply-diff.js';
|
|
|
11
11
|
import { applySnapshot } from '../../../utils/apply-snapshot.js';
|
|
12
12
|
import { getSnapshotDiff } from '../../../utils/get-snapshot-diff.js';
|
|
13
13
|
import { getSnapshot } from '../../../utils/get-snapshot.js';
|
|
14
|
+
function filterSnapshotDiff(snapshot, filters) {
|
|
15
|
+
const filterSet = new Set(filters);
|
|
16
|
+
function shouldKeep(item) {
|
|
17
|
+
if (filterSet.has(item.collection))
|
|
18
|
+
return false;
|
|
19
|
+
if (item.field && filterSet.has(`${item.collection}.${item.field}`))
|
|
20
|
+
return false;
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
const filteredDiff = {
|
|
24
|
+
collections: snapshot.collections.filter((item) => shouldKeep(item)),
|
|
25
|
+
fields: snapshot.fields.filter((item) => shouldKeep(item)),
|
|
26
|
+
relations: snapshot.relations.filter((item) => shouldKeep(item)),
|
|
27
|
+
};
|
|
28
|
+
return filteredDiff;
|
|
29
|
+
}
|
|
14
30
|
export async function apply(snapshotPath, options) {
|
|
15
31
|
const logger = useLogger();
|
|
16
32
|
const filename = path.resolve(process.cwd(), snapshotPath);
|
|
@@ -31,7 +47,10 @@ export async function apply(snapshotPath, options) {
|
|
|
31
47
|
snapshot = parseJSON(fileContents);
|
|
32
48
|
}
|
|
33
49
|
const currentSnapshot = await getSnapshot({ database });
|
|
34
|
-
|
|
50
|
+
let snapshotDiff = getSnapshotDiff(currentSnapshot, snapshot);
|
|
51
|
+
if (options?.ignoreRules) {
|
|
52
|
+
snapshotDiff = filterSnapshotDiff(snapshotDiff, options.ignoreRules.split(','));
|
|
53
|
+
}
|
|
35
54
|
if (snapshotDiff.collections.length === 0 &&
|
|
36
55
|
snapshotDiff.fields.length === 0 &&
|
|
37
56
|
snapshotDiff.relations.length === 0) {
|
package/dist/cli/index.js
CHANGED
|
@@ -81,6 +81,7 @@ export async function createCli() {
|
|
|
81
81
|
.description('Apply a snapshot file to the current database')
|
|
82
82
|
.option('-y, --yes', `Assume "yes" as answer to all prompts and run non-interactively`)
|
|
83
83
|
.option('-d, --dry-run', 'Plan and log changes to be applied', false)
|
|
84
|
+
.option('--ignoreRules <value>', `Comma-separated list of collections and or fields to ignore. Format: "products.title,reviews" this will ignore applying changes to the title field in the products collection and the entire reviews collection`)
|
|
84
85
|
.argument('<path>', 'Path to snapshot file')
|
|
85
86
|
.action(apply);
|
|
86
87
|
await emitter.emitInit('cli.after', { program });
|
|
@@ -313,7 +313,7 @@ EXTENSIONS_AUTO_RELOAD=false
|
|
|
313
313
|
EMAIL_FROM="no-reply@example.com"
|
|
314
314
|
|
|
315
315
|
# What to use to send emails. One of
|
|
316
|
-
# sendmail, smtp, mailgun,
|
|
316
|
+
# sendmail, smtp, mailgun, ses.
|
|
317
317
|
EMAIL_TRANSPORT="sendmail"
|
|
318
318
|
EMAIL_SENDMAIL_NEW_LINE="unix"
|
|
319
319
|
EMAIL_SENDMAIL_PATH="/usr/sbin/sendmail"
|
|
@@ -340,6 +340,3 @@ EMAIL_SENDMAIL_PATH="/usr/sbin/sendmail"
|
|
|
340
340
|
## Email (Mailgun Transport)
|
|
341
341
|
# EMAIL_MAILGUN_API_KEY="key-1234123412341234"
|
|
342
342
|
# EMAIL_MAILGUN_DOMAIN="a domain name from https://app.mailgun.com/app/sending/domains"
|
|
343
|
-
|
|
344
|
-
## Email (SendGrid Transport)
|
|
345
|
-
# EMAIL_SENDGRID_API_KEY="key-1234123412341234"
|
|
@@ -43,7 +43,7 @@ export async function up(knex) {
|
|
|
43
43
|
.update({ [constraint.many_field]: null })
|
|
44
44
|
.whereIn(currentPrimaryKeyField, ids);
|
|
45
45
|
}
|
|
46
|
-
catch
|
|
46
|
+
catch {
|
|
47
47
|
logger.error(`${constraint.many_collection}.${constraint.many_field} contains illegal foreign keys which couldn't be set to NULL. Please fix these references and rerun this migration to complete the upgrade.`);
|
|
48
48
|
if (ids.length < 25) {
|
|
49
49
|
logger.error(`Items with illegal foreign keys: ${ids.join(', ')}`);
|
|
@@ -198,7 +198,7 @@ export async function up(knex) {
|
|
|
198
198
|
table.dropForeign('role', foreignConstraint);
|
|
199
199
|
});
|
|
200
200
|
}
|
|
201
|
-
catch
|
|
201
|
+
catch {
|
|
202
202
|
logger.warn('Failed to drop foreign key constraint on `role` column in `directus_permissions` table');
|
|
203
203
|
}
|
|
204
204
|
await knex('directus_permissions')
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
/// <reference types="node" resolution-mode="require"/>
|
|
2
1
|
import type { Router } from 'express';
|
|
3
2
|
import type { Reference } from 'isolated-vm';
|
|
4
3
|
import type { IncomingHttpHeaders } from 'node:http';
|
|
5
4
|
export declare function registerRouteGenerator(endpointName: string, endpointRouter: Router): {
|
|
6
|
-
register: (path: Reference<string>, method: Reference<
|
|
5
|
+
register: (path: Reference<string>, method: Reference<"GET" | "POST" | "PUT" | "PATCH" | "DELETE">, cb: Reference<(req: {
|
|
7
6
|
url: string;
|
|
8
7
|
headers: IncomingHttpHeaders;
|
|
9
8
|
body: string;
|
package/dist/logger/index.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/// <reference types="qs" />
|
|
2
1
|
import type { RequestHandler } from 'express';
|
|
3
2
|
import { type Logger } from 'pino';
|
|
4
3
|
import { LogsStream } from './logs-stream.js';
|
|
@@ -11,5 +10,5 @@ export declare const useLogger: () => Logger<never>;
|
|
|
11
10
|
export declare const getLogsStream: (pretty: boolean) => LogsStream;
|
|
12
11
|
export declare const getHttpLogsStream: (pretty: boolean) => LogsStream;
|
|
13
12
|
export declare const getLoggerLevelValue: (level: string) => number;
|
|
14
|
-
export declare const createLogger: () => Logger<never>;
|
|
15
|
-
export declare const createExpressLogger: () => RequestHandler
|
|
13
|
+
export declare const createLogger: () => Logger<never, boolean>;
|
|
14
|
+
export declare const createExpressLogger: () => RequestHandler;
|
package/dist/mailer.js
CHANGED
|
@@ -56,12 +56,6 @@ export default function getMailer() {
|
|
|
56
56
|
host: env['EMAIL_MAILGUN_HOST'] || 'api.mailgun.net',
|
|
57
57
|
}));
|
|
58
58
|
}
|
|
59
|
-
else if (transportName === 'sendgrid') {
|
|
60
|
-
const sg = require('nodemailer-sendgrid');
|
|
61
|
-
transporter = nodemailer.createTransport(sg({
|
|
62
|
-
apiKey: env['EMAIL_SENDGRID_API_KEY'],
|
|
63
|
-
}));
|
|
64
|
-
}
|
|
65
59
|
else {
|
|
66
60
|
logger.warn('Illegal transport given for email. Check the EMAIL_TRANSPORT env var.');
|
|
67
61
|
}
|
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
/// <reference types="qs" />
|
|
2
|
-
/// <reference types="cookie-parser" />
|
|
3
1
|
import type { NextFunction, Request, Response } from 'express';
|
|
4
2
|
/**
|
|
5
3
|
* Verify the passed JWT and assign the user ID and role to `req`
|
|
6
4
|
*/
|
|
7
5
|
export declare const handler: (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
8
|
-
declare const _default: (req: Request
|
|
6
|
+
declare const _default: (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
9
7
|
export default _default;
|
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
/// <reference types="qs" />
|
|
2
1
|
import type { ErrorRequestHandler } from 'express';
|
|
3
2
|
export declare const errorHandler: (err: any, req: import("express-serve-static-core").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>, res: import("express-serve-static-core").Response<any, Record<string, any>, number>, next: import("express-serve-static-core").NextFunction) => Promise<ReturnType<ErrorRequestHandler>>;
|
|
@@ -1,4 +1 @@
|
|
|
1
|
-
|
|
2
|
-
/// <reference types="express" />
|
|
3
|
-
/// <reference types="cookie-parser" />
|
|
4
|
-
export declare const validateBatch: (scope: 'read' | 'update' | 'delete') => (req: import("express").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>, res: import("express").Response<any, Record<string, any>>, next: import("express").NextFunction) => Promise<void>;
|
|
1
|
+
export declare const validateBatch: (scope: "read" | "update" | "delete") => (req: import("express").Request, res: import("express").Response, next: import("express").NextFunction) => Promise<void>;
|
|
@@ -7,4 +7,14 @@ export interface FetchPermissionsOptions {
|
|
|
7
7
|
accountability?: Pick<Accountability, 'user' | 'role' | 'roles' | 'app'>;
|
|
8
8
|
bypassDynamicVariableProcessing?: boolean;
|
|
9
9
|
}
|
|
10
|
-
export declare function fetchPermissions(options: FetchPermissionsOptions, context: Context): Promise<
|
|
10
|
+
export declare function fetchPermissions(options: FetchPermissionsOptions, context: Context): Promise<{
|
|
11
|
+
permissions: import("@directus/types").Filter | null;
|
|
12
|
+
validation: import("@directus/types").Filter | null;
|
|
13
|
+
presets: any;
|
|
14
|
+
id?: number;
|
|
15
|
+
policy: string | null;
|
|
16
|
+
collection: string;
|
|
17
|
+
action: PermissionsAction;
|
|
18
|
+
fields: string[] | null;
|
|
19
|
+
system?: true;
|
|
20
|
+
}[]>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CollectionKey, FieldMap, QueryPath } from '../types.js';
|
|
2
2
|
export declare function getInfoForPath(fieldMap: FieldMap, group: keyof FieldMap, path: QueryPath, collection: CollectionKey): {
|
|
3
|
-
collection:
|
|
4
|
-
fields: Set<
|
|
3
|
+
collection: CollectionKey;
|
|
4
|
+
fields: Set<import("../types.js").FieldKey>;
|
|
5
5
|
};
|
package/dist/permissions/modules/validate-remaining-admin/validate-remaining-admin-users.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { type FetchUserCountOptions } from '../../../utils/fetch-user-count/fetch-user-count.js';
|
|
2
2
|
import type { Context } from '../../types.js';
|
|
3
|
-
export
|
|
4
|
-
}
|
|
3
|
+
export type ValidateRemainingAdminUsersOptions = Pick<FetchUserCountOptions, 'excludeAccessRows' | 'excludePolicies' | 'excludeUsers' | 'excludeRoles'>;
|
|
5
4
|
export declare function validateRemainingAdminUsers(options: ValidateRemainingAdminUsersOptions, context: Context): Promise<void>;
|
|
@@ -32,13 +32,21 @@ export async function fetchDynamicVariableContext(options, context) {
|
|
|
32
32
|
});
|
|
33
33
|
});
|
|
34
34
|
}
|
|
35
|
-
if (options.policies.length > 0
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
if (options.policies.length > 0) {
|
|
36
|
+
if ((permissionContext.$CURRENT_POLICIES?.size ?? 0) > 0) {
|
|
37
|
+
// Always add the id field
|
|
38
|
+
permissionContext.$CURRENT_POLICIES.add('id');
|
|
39
|
+
contextData['$CURRENT_POLICIES'] = await fetchContextData('$CURRENT_POLICIES', permissionContext, { policies: options.policies }, async (fields) => {
|
|
40
|
+
const policiesService = new PoliciesService(context);
|
|
41
|
+
return await policiesService.readMany(options.policies, {
|
|
42
|
+
fields,
|
|
43
|
+
});
|
|
40
44
|
});
|
|
41
|
-
}
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
// Always create entries for the policies with the `id` field present
|
|
48
|
+
contextData['$CURRENT_POLICIES'] = options.policies.map((id) => ({ id }));
|
|
49
|
+
}
|
|
42
50
|
}
|
|
43
51
|
return contextData;
|
|
44
52
|
}
|
|
@@ -4,4 +4,14 @@ export interface ProcessPermissionsOptions {
|
|
|
4
4
|
accountability: Pick<Accountability, 'user' | 'role' | 'roles'>;
|
|
5
5
|
permissionsContext: Record<string, any>;
|
|
6
6
|
}
|
|
7
|
-
export declare function processPermissions({ permissions, accountability, permissionsContext }: ProcessPermissionsOptions):
|
|
7
|
+
export declare function processPermissions({ permissions, accountability, permissionsContext }: ProcessPermissionsOptions): {
|
|
8
|
+
permissions: import("@directus/types").Filter | null;
|
|
9
|
+
validation: import("@directus/types").Filter | null;
|
|
10
|
+
presets: any;
|
|
11
|
+
id?: number;
|
|
12
|
+
policy: string | null;
|
|
13
|
+
collection: string;
|
|
14
|
+
action: import("@directus/types").PermissionsAction;
|
|
15
|
+
fields: string[] | null;
|
|
16
|
+
system?: true;
|
|
17
|
+
}[];
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { parseFilter, parsePreset } from '@directus/utils';
|
|
2
2
|
export function processPermissions({ permissions, accountability, permissionsContext }) {
|
|
3
3
|
return permissions.map((permission) => {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
return {
|
|
5
|
+
...permission,
|
|
6
|
+
permissions: parseFilter(permission.permissions, accountability, permissionsContext),
|
|
7
|
+
validation: parseFilter(permission.validation, accountability, permissionsContext),
|
|
8
|
+
presets: parsePreset(permission.presets, accountability, permissionsContext),
|
|
9
|
+
};
|
|
8
10
|
});
|
|
9
11
|
}
|
package/dist/server.d.ts
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
/// <reference types="node" resolution-mode="require"/>
|
|
2
|
-
/// <reference types="node/http.js" />
|
|
3
|
-
/// <reference types="pino-http" />
|
|
4
1
|
import * as http from 'http';
|
|
5
2
|
export declare let SERVER_ONLINE: boolean;
|
|
6
3
|
export declare function createServer(): Promise<http.Server>;
|
package/dist/services/fields.js
CHANGED
|
@@ -388,12 +388,6 @@ export class FieldsService {
|
|
|
388
388
|
if (hookAdjustedField.schema?.is_nullable === true) {
|
|
389
389
|
throw new InvalidPayloadError({ reason: 'Primary key cannot be null' });
|
|
390
390
|
}
|
|
391
|
-
if (hookAdjustedField.schema?.is_unique === false) {
|
|
392
|
-
throw new InvalidPayloadError({ reason: 'Primary key must be unique' });
|
|
393
|
-
}
|
|
394
|
-
if (hookAdjustedField.schema?.is_indexed === true) {
|
|
395
|
-
throw new InvalidPayloadError({ reason: 'Primary key cannot be indexed' });
|
|
396
|
-
}
|
|
397
391
|
}
|
|
398
392
|
// Sanitize column only when applying snapshot diff as opts is only passed from /utils/apply-diff.ts
|
|
399
393
|
const columnToCompare = opts?.bypassLimits && opts.autoPurgeSystemCache === false ? sanitizeColumn(existingColumn) : existingColumn;
|
package/dist/services/files.d.ts
CHANGED
package/dist/services/users.js
CHANGED
|
@@ -373,7 +373,7 @@ export class UsersService extends ItemsService {
|
|
|
373
373
|
await this.createOne(partialUser);
|
|
374
374
|
}
|
|
375
375
|
// We want to be able to re-send the verification email
|
|
376
|
-
else if (user.status !==
|
|
376
|
+
else if (user.status !== 'unverified') {
|
|
377
377
|
// To avoid giving attackers infos about registered emails we dont fail for violated unique constraints
|
|
378
378
|
await stall(STALL_TIME, timeStart);
|
|
379
379
|
return;
|
|
@@ -415,7 +415,7 @@ export class UsersService extends ItemsService {
|
|
|
415
415
|
if (scope !== 'pending-registration')
|
|
416
416
|
throw new ForbiddenError();
|
|
417
417
|
const user = await this.getUserByEmail(email);
|
|
418
|
-
if (user?.status !==
|
|
418
|
+
if (user?.status !== 'unverified') {
|
|
419
419
|
throw new InvalidPayloadError({ reason: 'Invalid verification code' });
|
|
420
420
|
}
|
|
421
421
|
await this.updateOne(user.id, { status: 'active' });
|
package/dist/types/graphql.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Aggregate, Filter, Permission, Query, SchemaOverview } from '@directus/types';
|
|
2
2
|
import type { Knex } from 'knex';
|
|
3
3
|
import type { AliasMap } from './get-column-path.js';
|
|
4
|
-
export declare const generateAlias: (size?: number
|
|
4
|
+
export declare const generateAlias: (size?: number) => string;
|
|
5
5
|
type ApplyQueryOptions = {
|
|
6
6
|
aliasMap?: AliasMap;
|
|
7
7
|
isInnerQuery?: boolean;
|
package/dist/utils/compress.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export function deleteFromRequireCache(modulePath) {
|
|
|
7
7
|
const moduleCachePath = require.resolve(modulePath);
|
|
8
8
|
delete require.cache[moduleCachePath];
|
|
9
9
|
}
|
|
10
|
-
catch
|
|
10
|
+
catch {
|
|
11
11
|
logger.trace(`Module cache not found for ${modulePath}, skipped cache delete.`);
|
|
12
12
|
}
|
|
13
13
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { type FetchAccessLookupOptions } from './fetch-access-lookup.js';
|
|
2
|
-
export
|
|
3
|
-
}
|
|
2
|
+
export type FetchUserCountOptions = FetchAccessLookupOptions;
|
|
4
3
|
export interface UserCount {
|
|
5
4
|
admin: number;
|
|
6
5
|
app: number;
|
|
@@ -3,7 +3,7 @@ import { getConfigFromEnv } from './get-config-from-env.js';
|
|
|
3
3
|
export function generateHash(stringToHash) {
|
|
4
4
|
const argon2HashConfigOptions = getConfigFromEnv('HASH_', 'HASH_RAW'); // Disallow the HASH_RAW option, see https://github.com/directus/directus/discussions/7670#discussioncomment-1255805
|
|
5
5
|
// associatedData, if specified, must be passed as a Buffer to argon2.hash, see https://github.com/ranisalt/node-argon2/wiki/Options#associateddata
|
|
6
|
-
'associatedData' in argon2HashConfigOptions
|
|
7
|
-
|
|
6
|
+
if ('associatedData' in argon2HashConfigOptions)
|
|
7
|
+
argon2HashConfigOptions['associatedData'] = Buffer.from(argon2HashConfigOptions['associatedData']);
|
|
8
8
|
return argon2.hash(stringToHash, argon2HashConfigOptions);
|
|
9
9
|
}
|
|
@@ -21,5 +21,5 @@ type GetColumnOptions = OriginalCollectionName | (FunctionColumnOptions & Origin
|
|
|
21
21
|
* @param options Optional parameters
|
|
22
22
|
* @returns Knex raw instance
|
|
23
23
|
*/
|
|
24
|
-
export declare function getColumn(knex: Knex, table: string, column: string, alias: string | false | undefined, schema: SchemaOverview, options?: GetColumnOptions): Knex.Raw;
|
|
24
|
+
export declare function getColumn(knex: Knex, table: string, column: string, alias: (string | false) | undefined, schema: SchemaOverview, options?: GetColumnOptions): Knex.Raw;
|
|
25
25
|
export {};
|
|
@@ -28,7 +28,7 @@ export async function authenticateConnection(message) {
|
|
|
28
28
|
const expires_at = getExpiresAtForToken(access_token);
|
|
29
29
|
return { accountability, expires_at, refresh_token };
|
|
30
30
|
}
|
|
31
|
-
catch
|
|
31
|
+
catch {
|
|
32
32
|
throw new WebSocketError('auth', 'AUTH_FAILED', 'Authentication failed.', message['uid']);
|
|
33
33
|
}
|
|
34
34
|
}
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/// <reference types="node" resolution-mode="require"/>
|
|
2
|
-
/// <reference types="node" resolution-mode="require"/>
|
|
3
|
-
/// <reference types="node" resolution-mode="require"/>
|
|
4
|
-
/// <reference types="node/http.js" />
|
|
5
|
-
/// <reference types="pino-http" />
|
|
6
1
|
import type { Accountability } from '@directus/types';
|
|
7
2
|
import type { IncomingMessage, Server as httpServer } from 'http';
|
|
8
3
|
import type { RateLimiterAbstract } from 'rate-limiter-flexible';
|
|
@@ -32,12 +27,8 @@ export default abstract class SocketController {
|
|
|
32
27
|
createClient(ws: WebSocket, { accountability, expires_at }: AuthenticationState): WebSocketClient;
|
|
33
28
|
protected parseMessage(data: string): WebSocketMessage;
|
|
34
29
|
protected handleAuthRequest(client: WebSocketClient, message: WebSocketAuthMessage): Promise<void>;
|
|
30
|
+
protected checkUserRequirements(_accountability: Accountability | null): void;
|
|
35
31
|
setTokenExpireTimer(client: WebSocketClient): void;
|
|
36
32
|
checkClientTokens(): void;
|
|
37
|
-
meetsAdminRequirement({ socket, client, accountability, }: {
|
|
38
|
-
socket?: UpgradeContext['socket'];
|
|
39
|
-
client?: WebSocketClient | WebSocket;
|
|
40
|
-
accountability: Accountability | null;
|
|
41
|
-
}): boolean;
|
|
42
33
|
terminate(): void;
|
|
43
34
|
}
|
|
@@ -60,7 +60,6 @@ export default class SocketController {
|
|
|
60
60
|
authentication: {
|
|
61
61
|
mode: authMode.data,
|
|
62
62
|
timeout: authTimeout,
|
|
63
|
-
requireAdmin: false,
|
|
64
63
|
},
|
|
65
64
|
};
|
|
66
65
|
}
|
|
@@ -140,8 +139,15 @@ export default class SocketController {
|
|
|
140
139
|
socket.destroy();
|
|
141
140
|
return;
|
|
142
141
|
}
|
|
143
|
-
|
|
142
|
+
try {
|
|
143
|
+
this.checkUserRequirements(accountability);
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
logger.debug('WebSocket upgrade denied - ' + JSON.stringify(accountability || 'invalid'));
|
|
147
|
+
socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
|
|
148
|
+
socket.destroy();
|
|
144
149
|
return;
|
|
150
|
+
}
|
|
145
151
|
this.server.handleUpgrade(request, socket, head, async (ws) => {
|
|
146
152
|
this.catchInvalidMessages(ws);
|
|
147
153
|
const state = { accountability, expires_at };
|
|
@@ -156,8 +162,7 @@ export default class SocketController {
|
|
|
156
162
|
if (getMessageType(payload) !== 'auth')
|
|
157
163
|
throw new Error();
|
|
158
164
|
const state = await authenticateConnection(WebSocketAuthMessage.parse(payload));
|
|
159
|
-
|
|
160
|
-
return;
|
|
165
|
+
this.checkUserRequirements(state.accountability);
|
|
161
166
|
ws.send(authenticationSuccess(payload['uid'], state.refresh_token));
|
|
162
167
|
this.server.emit('connection', ws, state);
|
|
163
168
|
}
|
|
@@ -238,7 +243,7 @@ export default class SocketController {
|
|
|
238
243
|
try {
|
|
239
244
|
message = WebSocketMessage.parse(parseJSON(data));
|
|
240
245
|
}
|
|
241
|
-
catch
|
|
246
|
+
catch {
|
|
242
247
|
throw new WebSocketError('server', 'INVALID_PAYLOAD', 'Unable to parse the incoming message.');
|
|
243
248
|
}
|
|
244
249
|
return message;
|
|
@@ -246,8 +251,7 @@ export default class SocketController {
|
|
|
246
251
|
async handleAuthRequest(client, message) {
|
|
247
252
|
try {
|
|
248
253
|
const { accountability, expires_at, refresh_token } = await authenticateConnection(message);
|
|
249
|
-
|
|
250
|
-
return;
|
|
254
|
+
this.checkUserRequirements(accountability);
|
|
251
255
|
client.accountability = accountability;
|
|
252
256
|
client.expires_at = expires_at;
|
|
253
257
|
this.setTokenExpireTimer(client);
|
|
@@ -269,6 +273,10 @@ export default class SocketController {
|
|
|
269
273
|
}
|
|
270
274
|
}
|
|
271
275
|
}
|
|
276
|
+
checkUserRequirements(_accountability) {
|
|
277
|
+
// there are no requirements in the abstract class
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
272
280
|
setTokenExpireTimer(client) {
|
|
273
281
|
if (client.auth_timer !== null) {
|
|
274
282
|
// clear up old timeouts if needed
|
|
@@ -305,20 +313,6 @@ export default class SocketController {
|
|
|
305
313
|
}
|
|
306
314
|
}, TOKEN_CHECK_INTERVAL);
|
|
307
315
|
}
|
|
308
|
-
meetsAdminRequirement({ socket, client, accountability, }) {
|
|
309
|
-
if (!this.authentication.requireAdmin || accountability?.admin)
|
|
310
|
-
return true;
|
|
311
|
-
logger.debug('WebSocket connection denied - ' + JSON.stringify(accountability || 'invalid'));
|
|
312
|
-
if (socket) {
|
|
313
|
-
socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
|
|
314
|
-
socket.destroy();
|
|
315
|
-
}
|
|
316
|
-
else if (client) {
|
|
317
|
-
handleWebSocketError(client, new WebSocketError('auth', 'UNAUTHORIZED', 'Unauthorized.'), 'auth');
|
|
318
|
-
client.close();
|
|
319
|
-
}
|
|
320
|
-
return false;
|
|
321
|
-
}
|
|
322
316
|
terminate() {
|
|
323
317
|
if (this.authInterval)
|
|
324
318
|
clearInterval(this.authInterval);
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
/// <reference types="node" resolution-mode="require"/>
|
|
2
|
-
/// <reference types="node/http.js" />
|
|
3
|
-
/// <reference types="pino-http" />
|
|
4
1
|
import type { Server } from 'graphql-ws';
|
|
5
2
|
import type { Server as httpServer } from 'http';
|
|
6
3
|
import type { GraphQLSocket, UpgradeContext, WebSocketClient } from '../types.js';
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
/// <reference types="node" resolution-mode="require"/>
|
|
2
|
-
/// <reference types="node/http.js" />
|
|
3
|
-
/// <reference types="pino-http" />
|
|
4
1
|
import type { Server as httpServer } from 'http';
|
|
5
2
|
import { GraphQLSubscriptionController } from './graphql.js';
|
|
6
3
|
import { LogsController } from './logs.js';
|
|
@@ -1,18 +1,17 @@
|
|
|
1
|
-
/// <reference types="node" resolution-mode="require"/>
|
|
2
|
-
/// <reference types="node/http.js" />
|
|
3
|
-
/// <reference types="pino-http" />
|
|
4
1
|
import type { Server as httpServer } from 'http';
|
|
2
|
+
import { AuthMode } from '../messages.js';
|
|
5
3
|
import SocketController from './base.js';
|
|
4
|
+
import type { Accountability } from '@directus/types';
|
|
6
5
|
export declare class LogsController extends SocketController {
|
|
7
6
|
constructor(httpServer: httpServer);
|
|
8
7
|
getEnvironmentConfig(configPrefix: string): {
|
|
9
8
|
endpoint: string;
|
|
10
9
|
maxConnections: number;
|
|
11
10
|
authentication: {
|
|
12
|
-
mode:
|
|
11
|
+
mode: AuthMode;
|
|
13
12
|
timeout: number;
|
|
14
|
-
requireAdmin: boolean;
|
|
15
13
|
};
|
|
16
14
|
};
|
|
17
15
|
private bindEvents;
|
|
16
|
+
protected checkUserRequirements(accountability: Accountability | null): void;
|
|
18
17
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useEnv } from '@directus/env';
|
|
2
2
|
import emitter from '../../emitter.js';
|
|
3
3
|
import { useLogger } from '../../logger/index.js';
|
|
4
|
-
import { handleWebSocketError } from '../errors.js';
|
|
4
|
+
import { handleWebSocketError, WebSocketError } from '../errors.js';
|
|
5
5
|
import { AuthMode, WebSocketMessage } from '../messages.js';
|
|
6
6
|
import SocketController from './base.js';
|
|
7
7
|
const logger = useLogger();
|
|
@@ -21,11 +21,9 @@ export class LogsController extends SocketController {
|
|
|
21
21
|
return {
|
|
22
22
|
endpoint,
|
|
23
23
|
maxConnections,
|
|
24
|
-
// require strict auth
|
|
25
24
|
authentication: {
|
|
26
25
|
mode: 'strict',
|
|
27
26
|
timeout: 0,
|
|
28
|
-
requireAdmin: true,
|
|
29
27
|
},
|
|
30
28
|
};
|
|
31
29
|
}
|
|
@@ -47,4 +45,10 @@ export class LogsController extends SocketController {
|
|
|
47
45
|
});
|
|
48
46
|
emitter.emitAction('websocket.connect', { client });
|
|
49
47
|
}
|
|
48
|
+
checkUserRequirements(accountability) {
|
|
49
|
+
// enforce admin only access for the logs streaming websocket
|
|
50
|
+
if (!accountability?.admin) {
|
|
51
|
+
throw new WebSocketError('auth', 'AUTH_FAILED', 'Unauthorized access.');
|
|
52
|
+
}
|
|
53
|
+
}
|
|
50
54
|
}
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
/// <reference types="node" resolution-mode="require"/>
|
|
2
|
-
/// <reference types="node/http.js" />
|
|
3
|
-
/// <reference types="pino-http" />
|
|
4
1
|
import type { Server as httpServer } from 'http';
|
|
5
2
|
import { WebSocketMessage } from '../messages.js';
|
|
6
3
|
import SocketController from './base.js';
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/// <reference types="node" resolution-mode="require"/>
|
|
2
|
-
/// <reference types="node" resolution-mode="require"/>
|
|
3
|
-
/// <reference types="node" resolution-mode="require"/>
|
|
4
|
-
/// <reference types="node/http.js" />
|
|
5
|
-
/// <reference types="pino-http" />
|
|
6
1
|
import type { Accountability, Query } from '@directus/types';
|
|
7
2
|
import type { IncomingMessage } from 'http';
|
|
8
3
|
import type internal from 'stream';
|
|
@@ -21,7 +16,6 @@ export type UpgradeRequest = IncomingMessage & AuthenticationState;
|
|
|
21
16
|
export type WebSocketAuthentication = {
|
|
22
17
|
mode: AuthMode;
|
|
23
18
|
timeout: number;
|
|
24
|
-
requireAdmin: boolean;
|
|
25
19
|
};
|
|
26
20
|
export type SubscriptionEvent = 'create' | 'update' | 'delete';
|
|
27
21
|
export type Subscription = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@directus/api",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "23.0.0",
|
|
4
4
|
"description": "Directus is a real-time API and App dashboard for managing SQL database content",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"directus",
|
|
@@ -68,9 +68,9 @@
|
|
|
68
68
|
"@tus/server": "1.6.0",
|
|
69
69
|
"@tus/utils": "0.2.0",
|
|
70
70
|
"@types/cookie": "0.6.0",
|
|
71
|
-
"argon2": "0.
|
|
71
|
+
"argon2": "0.41.1",
|
|
72
72
|
"async": "3.2.5",
|
|
73
|
-
"axios": "1.7.
|
|
73
|
+
"axios": "1.7.7",
|
|
74
74
|
"busboy": "1.6.0",
|
|
75
75
|
"bytes": "3.1.2",
|
|
76
76
|
"camelcase": "8.0.0",
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
"cookie-parser": "1.4.6",
|
|
83
83
|
"cors": "2.8.5",
|
|
84
84
|
"cron-parser": "4.9.0",
|
|
85
|
-
"date-fns": "
|
|
85
|
+
"date-fns": "4.1.0",
|
|
86
86
|
"deep-diff": "1.0.2",
|
|
87
87
|
"destroy": "1.2.0",
|
|
88
88
|
"dotenv": "16.4.5",
|
|
@@ -102,18 +102,18 @@
|
|
|
102
102
|
"inquirer": "9.3.6",
|
|
103
103
|
"ioredis": "5.4.1",
|
|
104
104
|
"ip-matching": "2.1.2",
|
|
105
|
-
"isolated-vm": "
|
|
105
|
+
"isolated-vm": "5.0.1",
|
|
106
106
|
"joi": "17.13.3",
|
|
107
107
|
"js-yaml": "4.1.0",
|
|
108
108
|
"js2xmlparser": "5.0.0",
|
|
109
109
|
"json2csv": "5.0.7",
|
|
110
110
|
"jsonwebtoken": "9.0.2",
|
|
111
|
-
"keyv": "
|
|
111
|
+
"keyv": "5.1.0",
|
|
112
112
|
"knex": "3.1.0",
|
|
113
113
|
"ldapjs": "2.3.3",
|
|
114
114
|
"liquidjs": "10.15.0",
|
|
115
115
|
"lodash-es": "4.17.21",
|
|
116
|
-
"marked": "
|
|
116
|
+
"marked": "14.1.2",
|
|
117
117
|
"micromustache": "8.0.3",
|
|
118
118
|
"mime-types": "2.1.35",
|
|
119
119
|
"minimatch": "9.0.5",
|
|
@@ -124,17 +124,17 @@
|
|
|
124
124
|
"node-schedule": "2.1.1",
|
|
125
125
|
"nodemailer": "6.9.14",
|
|
126
126
|
"object-hash": "3.0.0",
|
|
127
|
-
"openapi3-ts": "4.
|
|
127
|
+
"openapi3-ts": "4.4.0",
|
|
128
128
|
"openid-client": "5.6.5",
|
|
129
129
|
"ora": "8.0.1",
|
|
130
130
|
"otplib": "12.0.1",
|
|
131
|
-
"p-limit": "
|
|
131
|
+
"p-limit": "6.1.0",
|
|
132
132
|
"p-queue": "8.0.1",
|
|
133
133
|
"papaparse": "5.4.1",
|
|
134
|
-
"pino": "9.
|
|
135
|
-
"pino-http": "
|
|
134
|
+
"pino": "9.4.0",
|
|
135
|
+
"pino-http": "10.3.0",
|
|
136
136
|
"pino-http-print": "3.1.0",
|
|
137
|
-
"pino-pretty": "11.2.
|
|
137
|
+
"pino-pretty": "11.2.2",
|
|
138
138
|
"qs": "6.13.0",
|
|
139
139
|
"rate-limiter-flexible": "5.0.3",
|
|
140
140
|
"rollup": "4.17.2",
|
|
@@ -148,30 +148,30 @@
|
|
|
148
148
|
"wellknown": "0.5.0",
|
|
149
149
|
"ws": "8.18.0",
|
|
150
150
|
"zod": "3.23.8",
|
|
151
|
-
"zod-validation-error": "3.
|
|
152
|
-
"@directus/
|
|
153
|
-
"@directus/env": "3.1.
|
|
154
|
-
"@directus/app": "13.2.0",
|
|
151
|
+
"zod-validation-error": "3.4.0",
|
|
152
|
+
"@directus/app": "13.2.1",
|
|
153
|
+
"@directus/env": "3.1.1",
|
|
155
154
|
"@directus/errors": "1.0.0",
|
|
156
|
-
"@directus/extensions": "2.0.
|
|
157
|
-
"@directus/extensions-
|
|
158
|
-
"@directus/
|
|
159
|
-
"@directus/extensions-sdk": "12.0.2",
|
|
155
|
+
"@directus/extensions": "2.0.2",
|
|
156
|
+
"@directus/extensions-sdk": "12.1.0",
|
|
157
|
+
"@directus/extensions-registry": "2.0.2",
|
|
160
158
|
"@directus/format-title": "11.0.0",
|
|
161
|
-
"@directus/
|
|
162
|
-
"@directus/schema": "12.1.
|
|
159
|
+
"@directus/memory": "2.0.2",
|
|
160
|
+
"@directus/schema": "12.1.1",
|
|
163
161
|
"@directus/storage": "11.0.0",
|
|
162
|
+
"@directus/pressure": "2.0.1",
|
|
163
|
+
"@directus/storage-driver-azure": "11.0.1",
|
|
164
164
|
"@directus/specs": "11.0.1",
|
|
165
|
-
"@directus/
|
|
166
|
-
"@directus/storage-driver-
|
|
167
|
-
"@directus/storage-driver-gcs": "11.0.0",
|
|
165
|
+
"@directus/constants": "12.0.0",
|
|
166
|
+
"@directus/storage-driver-gcs": "11.0.1",
|
|
168
167
|
"@directus/storage-driver-local": "11.0.0",
|
|
169
|
-
"@directus/storage-driver-
|
|
168
|
+
"@directus/storage-driver-cloudinary": "11.0.2",
|
|
169
|
+
"@directus/storage-driver-s3": "11.0.1",
|
|
170
|
+
"@directus/storage-driver-supabase": "2.0.1",
|
|
170
171
|
"@directus/system-data": "2.0.0",
|
|
171
|
-
"@directus/
|
|
172
|
-
"
|
|
173
|
-
"@directus/utils": "12.0.
|
|
174
|
-
"directus": "11.1.0"
|
|
172
|
+
"@directus/validation": "1.0.1",
|
|
173
|
+
"directus": "11.1.1",
|
|
174
|
+
"@directus/utils": "12.0.1"
|
|
175
175
|
},
|
|
176
176
|
"devDependencies": {
|
|
177
177
|
"@ngneat/falso": "7.2.0",
|
|
@@ -196,36 +196,35 @@
|
|
|
196
196
|
"@types/lodash-es": "4.17.12",
|
|
197
197
|
"@types/mime-types": "2.1.4",
|
|
198
198
|
"@types/ms": "0.7.34",
|
|
199
|
-
"@types/node": "18.19.
|
|
199
|
+
"@types/node": "18.19.50",
|
|
200
200
|
"@types/node-schedule": "2.1.7",
|
|
201
201
|
"@types/nodemailer": "6.4.15",
|
|
202
202
|
"@types/object-hash": "3.0.6",
|
|
203
203
|
"@types/papaparse": "5.3.14",
|
|
204
204
|
"@types/qs": "6.9.15",
|
|
205
|
-
"@types/sanitize-html": "2.
|
|
205
|
+
"@types/sanitize-html": "2.13.0",
|
|
206
206
|
"@types/stream-json": "1.7.7",
|
|
207
207
|
"@types/wellknown": "0.5.8",
|
|
208
208
|
"@types/ws": "8.5.12",
|
|
209
|
-
"@vitest/coverage-v8": "1.
|
|
209
|
+
"@vitest/coverage-v8": "2.1.2",
|
|
210
210
|
"copyfiles": "2.4.1",
|
|
211
211
|
"form-data": "4.0.0",
|
|
212
212
|
"get-port": "7.1.0",
|
|
213
|
-
"knex-mock-client": "
|
|
214
|
-
"typescript": "5.
|
|
215
|
-
"vitest": "1.
|
|
213
|
+
"knex-mock-client": "3.0.2",
|
|
214
|
+
"typescript": "5.6.2",
|
|
215
|
+
"vitest": "2.1.2",
|
|
216
216
|
"@directus/random": "1.0.0",
|
|
217
|
-
"@directus/
|
|
218
|
-
"@directus/
|
|
217
|
+
"@directus/types": "12.1.0",
|
|
218
|
+
"@directus/tsconfig": "2.0.0"
|
|
219
219
|
},
|
|
220
220
|
"optionalDependencies": {
|
|
221
|
-
"@keyv/redis": "
|
|
222
|
-
"mysql2": "3.11.
|
|
221
|
+
"@keyv/redis": "3.0.1",
|
|
222
|
+
"mysql2": "3.11.2",
|
|
223
223
|
"nodemailer-mailgun-transport": "2.1.5",
|
|
224
|
-
"nodemailer-sendgrid": "1.0.3",
|
|
225
224
|
"oracledb": "6.6.0",
|
|
226
225
|
"pg": "8.12.0",
|
|
227
226
|
"sqlite3": "5.1.7",
|
|
228
|
-
"tedious": "18.
|
|
227
|
+
"tedious": "18.6.1"
|
|
229
228
|
},
|
|
230
229
|
"engines": {
|
|
231
230
|
"node": ">=18.17.0"
|