@wbce-d9/api 12.6.1 → 12.7.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/auth/drivers/baseoauth.js +18 -7
- package/dist/auth/drivers/baseoauth.test.d.ts +1 -0
- package/dist/env.js +1 -0
- package/dist/flows.d.ts +9 -0
- package/dist/flows.js +37 -7
- package/dist/flows.test.d.ts +1 -0
- package/dist/logger.js +4 -3
- package/dist/request/request-interceptor.d.ts +1 -1
- package/dist/request/response-interceptor.d.ts +1 -1
- package/dist/services/graphql/index.js +3 -0
- package/dist/services/payload.js +28 -12
- package/dist/services/users.js +35 -29
- package/package.json +10 -10
|
@@ -39,11 +39,16 @@ export class BaseOAuthDriver extends LocalAuthDriver {
|
|
|
39
39
|
provider: this.getConfig()['provider'],
|
|
40
40
|
providerPayload: { accessToken: tokenSet.access_token, userInfo },
|
|
41
41
|
}, { database: getDatabase(), schema: this.schema, accountability: null });
|
|
42
|
-
// Update user to update refresh_token and other properties that might have changed
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
42
|
+
// Update user to update refresh_token and other properties that might have changed.
|
|
43
|
+
// Keep the stored refresh_token if the provider didn't issue a new one (e.g. Google when consent was
|
|
44
|
+
// already granted): refresh() still checks it against the provider
|
|
45
|
+
const updateUserPayload = userPayload.auth_data
|
|
46
|
+
? { ...updatedUserPayload, auth_data: userPayload.auth_data }
|
|
47
|
+
: updatedUserPayload;
|
|
48
|
+
// Skip empty updates, they crash (#127)
|
|
49
|
+
if (Object.keys(updateUserPayload).length > 0) {
|
|
50
|
+
await this.getUserService().updateOne(userId, updateUserPayload);
|
|
51
|
+
}
|
|
47
52
|
return userId;
|
|
48
53
|
}
|
|
49
54
|
// --- NEW USER ----
|
|
@@ -131,10 +136,16 @@ export class BaseOAuthDriver extends LocalAuthDriver {
|
|
|
131
136
|
throw this.handleError(e);
|
|
132
137
|
}
|
|
133
138
|
}
|
|
139
|
+
else if (this.getConfig()['requireRefreshToken'] === false) {
|
|
140
|
+
// Opt-in: the provider issued no refresh token (e.g. Google when consent was already granted),
|
|
141
|
+
// so the Directus session is the only source of truth for this user
|
|
142
|
+
logger.debug(`[${this.getClientName()}] No refresh token stored, continuing (REQUIRE_REFRESH_TOKEN=false)`);
|
|
143
|
+
}
|
|
134
144
|
else {
|
|
135
|
-
// If no auth connexion, throw user out
|
|
145
|
+
// If no auth connexion, throw user out. On login, the callback retries once with prompt=consent
|
|
146
|
+
// so the provider can issue a refresh_token
|
|
136
147
|
logger.error(`No [${this.getClientName()}] Session found`);
|
|
137
|
-
throw new
|
|
148
|
+
throw new InvalidTokenException();
|
|
138
149
|
}
|
|
139
150
|
}
|
|
140
151
|
handleError = (e) => {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/env.js
CHANGED
|
@@ -141,6 +141,7 @@ const allowedEnvironmentVars = [
|
|
|
141
141
|
'AUTH_.+_FIRST_NAME_KEY',
|
|
142
142
|
'AUTH_.+_LAST_NAME_KEY',
|
|
143
143
|
'AUTH_.+_ALLOW_PUBLIC_REGISTRATION',
|
|
144
|
+
'AUTH_.+_REQUIRE_REFRESH_TOKEN',
|
|
144
145
|
'AUTH_.+_DEFAULT_ROLE_ID',
|
|
145
146
|
'AUTH_.+_REGISTER_URL_ALLOW_LIST',
|
|
146
147
|
'AUTH_.+_ICON',
|
package/dist/flows.d.ts
CHANGED
|
@@ -22,6 +22,15 @@ declare class FlowManager {
|
|
|
22
22
|
private checkManualTriggerAccess;
|
|
23
23
|
private unload;
|
|
24
24
|
private executeFlow;
|
|
25
|
+
/**
|
|
26
|
+
* Return a copy of the event data whose payload has its concealed fields masked, as they would be on read.
|
|
27
|
+
* The data is returned as is when there is nothing to conceal.
|
|
28
|
+
*/
|
|
29
|
+
private concealEventPayload;
|
|
30
|
+
/**
|
|
31
|
+
* Options are resolved against `concealedKeyedData` for the log operation and for the returned options (stored in
|
|
32
|
+
* the flow revisions), and against `keyedData` for every other operation.
|
|
33
|
+
*/
|
|
25
34
|
private executeOperation;
|
|
26
35
|
}
|
|
27
36
|
export {};
|
package/dist/flows.js
CHANGED
|
@@ -2,7 +2,7 @@ import * as sharedExceptions from '@wbce-d9/exceptions';
|
|
|
2
2
|
import { Action } from '@wbce-d9/constants';
|
|
3
3
|
import { applyOptionsData, isValidJSON, parseJSON, toArray } from '@wbce-d9/utils';
|
|
4
4
|
import fastRedact from 'fast-redact';
|
|
5
|
-
import { omit, pick } from 'lodash-es';
|
|
5
|
+
import { cloneDeep, omit, pick } from 'lodash-es';
|
|
6
6
|
import { get } from 'micromustache';
|
|
7
7
|
import { schedule, validate } from 'node-cron';
|
|
8
8
|
import getDatabase from './database/index.js';
|
|
@@ -15,6 +15,7 @@ import { getMessenger } from './messenger.js';
|
|
|
15
15
|
import { ActivityService } from './services/activity.js';
|
|
16
16
|
import * as services from './services/index.js';
|
|
17
17
|
import { FlowsService } from './services/flows.js';
|
|
18
|
+
import { PayloadService } from './services/payload.js';
|
|
18
19
|
import { RevisionsService } from './services/revisions.js';
|
|
19
20
|
import { constructFlowTree } from './utils/construct-flow-tree.js';
|
|
20
21
|
import { getSchema } from './utils/get-schema.js';
|
|
@@ -273,13 +274,22 @@ class FlowManager {
|
|
|
273
274
|
[ACCOUNTABILITY_KEY]: context?.['accountability'] ?? null,
|
|
274
275
|
[ENV_KEY]: pick(env, env['FLOWS_ENV_ALLOW_LIST'] ? toArray(env['FLOWS_ENV_ALLOW_LIST']) : []),
|
|
275
276
|
};
|
|
277
|
+
// Concealed fields of the event payload (e.g. passwords, tokens) must never end up in logs or revisions
|
|
278
|
+
const concealedData = await this.concealEventPayload(flow, data, database, schema);
|
|
279
|
+
const concealedKeyedData = {
|
|
280
|
+
...keyedData,
|
|
281
|
+
[TRIGGER_KEY]: concealedData,
|
|
282
|
+
[LAST_KEY]: concealedData,
|
|
283
|
+
};
|
|
276
284
|
let nextOperation = flow.operation;
|
|
277
285
|
let lastOperationStatus = 'unknown';
|
|
278
286
|
const steps = [];
|
|
279
287
|
while (nextOperation !== null) {
|
|
280
|
-
const { successor, data, status, options } = await this.executeOperation(nextOperation, keyedData, context);
|
|
288
|
+
const { successor, data, status, options } = await this.executeOperation(nextOperation, keyedData, concealedKeyedData, context);
|
|
281
289
|
keyedData[nextOperation.key] = data;
|
|
282
290
|
keyedData[LAST_KEY] = data;
|
|
291
|
+
concealedKeyedData[nextOperation.key] = data;
|
|
292
|
+
concealedKeyedData[LAST_KEY] = data;
|
|
283
293
|
lastOperationStatus = status;
|
|
284
294
|
steps.push({ operation: nextOperation.id, key: nextOperation.key, status, options });
|
|
285
295
|
nextOperation = successor;
|
|
@@ -310,7 +320,7 @@ class FlowManager {
|
|
|
310
320
|
item: flow.id,
|
|
311
321
|
data: {
|
|
312
322
|
steps: steps,
|
|
313
|
-
data: redactLogs(omit(
|
|
323
|
+
data: redactLogs(omit(concealedKeyedData, '$accountability.permissions')), // Permissions is a ton of data, and is just a copy of what's in the directus_permissions table
|
|
314
324
|
},
|
|
315
325
|
});
|
|
316
326
|
}
|
|
@@ -326,15 +336,35 @@ class FlowManager {
|
|
|
326
336
|
}
|
|
327
337
|
return undefined;
|
|
328
338
|
}
|
|
329
|
-
|
|
339
|
+
/**
|
|
340
|
+
* Return a copy of the event data whose payload has its concealed fields masked, as they would be on read.
|
|
341
|
+
* The data is returned as is when there is nothing to conceal.
|
|
342
|
+
*/
|
|
343
|
+
async concealEventPayload(flow, data, knex, schema) {
|
|
344
|
+
if (flow.trigger !== 'event')
|
|
345
|
+
return data;
|
|
346
|
+
const { collection, payload } = data;
|
|
347
|
+
if (!schema.collections[collection] || !payload)
|
|
348
|
+
return data;
|
|
349
|
+
const payloadService = new PayloadService(collection, { knex, schema });
|
|
350
|
+
const concealedData = cloneDeep(data);
|
|
351
|
+
concealedData.payload = await payloadService.processValues('read', concealedData.payload);
|
|
352
|
+
return concealedData;
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Options are resolved against `concealedKeyedData` for the log operation and for the returned options (stored in
|
|
356
|
+
* the flow revisions), and against `keyedData` for every other operation.
|
|
357
|
+
*/
|
|
358
|
+
async executeOperation(operation, keyedData, concealedKeyedData, context = {}) {
|
|
330
359
|
if (!(operation.type in this.operations)) {
|
|
331
360
|
logger.warn(`Couldn't find operation ${operation.type}`);
|
|
332
361
|
return { successor: null, status: 'unknown', data: null, options: null };
|
|
333
362
|
}
|
|
334
363
|
const handler = this.operations[operation.type];
|
|
335
364
|
const options = applyOptionsData(operation.options, keyedData);
|
|
365
|
+
const concealedOptions = applyOptionsData(operation.options, concealedKeyedData);
|
|
336
366
|
try {
|
|
337
|
-
let result = await handler(options, {
|
|
367
|
+
let result = await handler(operation.type === 'log' ? concealedOptions : options, {
|
|
338
368
|
services,
|
|
339
369
|
exceptions: { ...exceptions, ...sharedExceptions },
|
|
340
370
|
env,
|
|
@@ -352,7 +382,7 @@ class FlowManager {
|
|
|
352
382
|
if (typeof result === 'object' && result !== null) {
|
|
353
383
|
result = mapValuesDeep(result, (_, value) => (value === undefined ? null : value));
|
|
354
384
|
}
|
|
355
|
-
return { successor: operation.resolve, status: 'resolve', data: result ?? null, options };
|
|
385
|
+
return { successor: operation.resolve, status: 'resolve', data: result ?? null, options: concealedOptions };
|
|
356
386
|
}
|
|
357
387
|
catch (error) {
|
|
358
388
|
let data;
|
|
@@ -374,7 +404,7 @@ class FlowManager {
|
|
|
374
404
|
successor: operation.reject,
|
|
375
405
|
status: 'reject',
|
|
376
406
|
data,
|
|
377
|
-
options,
|
|
407
|
+
options: concealedOptions,
|
|
378
408
|
};
|
|
379
409
|
}
|
|
380
410
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/logger.js
CHANGED
|
@@ -6,17 +6,18 @@ import { URL } from 'url';
|
|
|
6
6
|
import env from './env.js';
|
|
7
7
|
import { REDACT_TEXT } from './constants.js';
|
|
8
8
|
import { getConfigFromEnv } from './utils/get-config-from-env.js';
|
|
9
|
+
const REDACTED_PATHS = ['req.headers.authorization', 'req.headers.cookie', 'req.query.access_token'];
|
|
9
10
|
const pinoOptions = {
|
|
10
11
|
level: env['LOG_LEVEL'] || 'info',
|
|
11
12
|
redact: {
|
|
12
|
-
paths:
|
|
13
|
+
paths: REDACTED_PATHS,
|
|
13
14
|
censor: REDACT_TEXT,
|
|
14
15
|
},
|
|
15
16
|
};
|
|
16
17
|
export const httpLoggerOptions = {
|
|
17
18
|
level: env['LOG_LEVEL'] || 'info',
|
|
18
19
|
redact: {
|
|
19
|
-
paths:
|
|
20
|
+
paths: REDACTED_PATHS,
|
|
20
21
|
censor: REDACT_TEXT,
|
|
21
22
|
},
|
|
22
23
|
};
|
|
@@ -43,7 +44,7 @@ if (env['LOG_STYLE'] !== 'raw') {
|
|
|
43
44
|
}
|
|
44
45
|
if (env['LOG_STYLE'] === 'raw') {
|
|
45
46
|
httpLoggerOptions.redact = {
|
|
46
|
-
paths: [
|
|
47
|
+
paths: [...REDACTED_PATHS, 'res.headers'],
|
|
47
48
|
censor: (value, pathParts) => {
|
|
48
49
|
const path = pathParts.join('.');
|
|
49
50
|
if (path === 'res.headers') {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { InternalAxiosRequestConfig } from 'axios';
|
|
2
|
-
export declare const requestInterceptor: (config: InternalAxiosRequestConfig) => Promise<InternalAxiosRequestConfig<any>>;
|
|
2
|
+
export declare const requestInterceptor: (config: InternalAxiosRequestConfig) => Promise<InternalAxiosRequestConfig<any, any>>;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { AxiosResponse } from 'axios';
|
|
2
|
-
export declare const responseInterceptor: (config: AxiosResponse<any, any>) => Promise<AxiosResponse<any, any, {}>>;
|
|
2
|
+
export declare const responseInterceptor: (config: AxiosResponse<any, any>) => Promise<AxiosResponse<any, any, {}, any>>;
|
|
@@ -104,6 +104,9 @@ export class GraphQLService {
|
|
|
104
104
|
return formattedResult;
|
|
105
105
|
}
|
|
106
106
|
getSchema(type = 'schema') {
|
|
107
|
+
if (type === 'sdl' && env['GRAPHQL_INTROSPECTION'] === false) {
|
|
108
|
+
throw new ForbiddenException();
|
|
109
|
+
}
|
|
107
110
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
108
111
|
const self = this;
|
|
109
112
|
const schemaComposer = new SchemaComposer();
|
package/dist/services/payload.js
CHANGED
|
@@ -3,7 +3,7 @@ import { format, isValid, parseISO } from 'date-fns';
|
|
|
3
3
|
import flat from 'flat';
|
|
4
4
|
import Joi from 'joi';
|
|
5
5
|
import { clone, cloneDeep, isNil, isObject, isPlainObject, omit, pick } from 'lodash-es';
|
|
6
|
-
import { v4 as uuid } from 'uuid';
|
|
6
|
+
import { v4 as uuid, validate as validateUUID } from 'uuid';
|
|
7
7
|
import { parse as wktToGeoJSON } from 'wellknown';
|
|
8
8
|
import { getHelpers } from '../database/helpers/index.js';
|
|
9
9
|
import getDatabase from '../database/index.js';
|
|
@@ -44,7 +44,10 @@ export class PayloadService {
|
|
|
44
44
|
if (action === 'create' && !value) {
|
|
45
45
|
return uuid();
|
|
46
46
|
}
|
|
47
|
-
|
|
47
|
+
if (action === 'create' && !validateUUID(String(value))) {
|
|
48
|
+
throw new InvalidPayloadException(`Invalid UUID "${value}"`);
|
|
49
|
+
}
|
|
50
|
+
return action === 'create' ? String(value).toLowerCase() : value;
|
|
48
51
|
},
|
|
49
52
|
async 'cast-boolean'({ action, value }) {
|
|
50
53
|
if (action === 'read') {
|
|
@@ -475,6 +478,23 @@ export class PayloadService {
|
|
|
475
478
|
knex: this.knex,
|
|
476
479
|
schema: this.schema,
|
|
477
480
|
});
|
|
481
|
+
const adminItemService = new ItemsService(relation.collection, {
|
|
482
|
+
knex: this.knex,
|
|
483
|
+
schema: this.schema,
|
|
484
|
+
});
|
|
485
|
+
const parentKey = parent || payload[currentPrimaryKeyField];
|
|
486
|
+
// Add the back-reference to the item, unless it is already linked to this parent.
|
|
487
|
+
const attachToParent = async (item) => {
|
|
488
|
+
const key = item[relatedPrimaryKeyField];
|
|
489
|
+
if (key) {
|
|
490
|
+
// readMany rather than readOne: not found is not an error.
|
|
491
|
+
const currentItems = await adminItemService.readMany([key], { fields: [relation.field] });
|
|
492
|
+
if (currentItems.length > 0 && currentItems[0][relation.field] == parentKey) {
|
|
493
|
+
return omit(item, relation.field);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
return { ...item, [relation.field]: parentKey };
|
|
497
|
+
};
|
|
478
498
|
const recordsToUpsert = [];
|
|
479
499
|
const savedPrimaryKeys = [];
|
|
480
500
|
// Nested array of individual items
|
|
@@ -509,10 +529,8 @@ export class PayloadService {
|
|
|
509
529
|
[relatedPrimaryKeyField]: relatedRecord,
|
|
510
530
|
};
|
|
511
531
|
}
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
[relation.field]: parent || payload[currentPrimaryKeyField],
|
|
515
|
-
});
|
|
532
|
+
const attachedRecord = await attachToParent(record);
|
|
533
|
+
recordsToUpsert.push(attachedRecord);
|
|
516
534
|
}
|
|
517
535
|
savedPrimaryKeys.push(...(await itemsService.upsertMany(recordsToUpsert, {
|
|
518
536
|
onRevisionCreate: (pk) => revisions.push(pk),
|
|
@@ -578,14 +596,14 @@ export class PayloadService {
|
|
|
578
596
|
}
|
|
579
597
|
return {
|
|
580
598
|
...record,
|
|
581
|
-
[relation.field]:
|
|
599
|
+
[relation.field]: parentKey,
|
|
582
600
|
};
|
|
583
601
|
});
|
|
584
602
|
}
|
|
585
603
|
else {
|
|
586
604
|
createPayload = alterations.create.map((item) => ({
|
|
587
605
|
...item,
|
|
588
|
-
[relation.field]:
|
|
606
|
+
[relation.field]: parentKey,
|
|
589
607
|
}));
|
|
590
608
|
}
|
|
591
609
|
await itemsService.createMany(createPayload, {
|
|
@@ -598,10 +616,8 @@ export class PayloadService {
|
|
|
598
616
|
if (alterations.update) {
|
|
599
617
|
const primaryKeyField = this.schema.collections[relation.collection].primary;
|
|
600
618
|
for (const item of alterations.update) {
|
|
601
|
-
await
|
|
602
|
-
|
|
603
|
-
[relation.field]: parent || payload[currentPrimaryKeyField],
|
|
604
|
-
}, {
|
|
619
|
+
const attachedItem = await attachToParent(item);
|
|
620
|
+
await itemsService.updateOne(attachedItem[primaryKeyField], attachedItem, {
|
|
605
621
|
onRevisionCreate: (pk) => revisions.push(pk),
|
|
606
622
|
bypassEmitAction: (params) => opts?.bypassEmitAction ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
|
|
607
623
|
emitEvents: opts?.emitEvents,
|
package/dist/services/users.js
CHANGED
|
@@ -5,6 +5,7 @@ import { cloneDeep, isEmpty } from 'lodash-es';
|
|
|
5
5
|
import { performance } from 'perf_hooks';
|
|
6
6
|
import getDatabase from '../database/index.js';
|
|
7
7
|
import env from '../env.js';
|
|
8
|
+
import logger from '../logger.js';
|
|
8
9
|
import { RecordNotUniqueException } from '../exceptions/database/record-not-unique.js';
|
|
9
10
|
import { ForbiddenException, InvalidPayloadException, UnprocessableEntityException } from '../exceptions/index.js';
|
|
10
11
|
import isUrlAllowed from '../utils/is-url-allowed.js';
|
|
@@ -340,37 +341,42 @@ export class UsersService extends ItemsService {
|
|
|
340
341
|
async requestPasswordReset(email, url, subject) {
|
|
341
342
|
const STALL_TIME = 500;
|
|
342
343
|
const timeStart = performance.now();
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
344
|
+
try {
|
|
345
|
+
if (url && isUrlAllowed(url, env['PASSWORD_RESET_URL_ALLOW_LIST']) === false) {
|
|
346
|
+
throw new InvalidPayloadException(`Url "${url}" can't be used to reset passwords.`);
|
|
347
|
+
}
|
|
348
|
+
const user = await this.getUserByEmail(email);
|
|
349
|
+
if (user?.status !== 'active') {
|
|
350
|
+
throw new ForbiddenException();
|
|
351
|
+
}
|
|
352
|
+
const mailService = new MailService({
|
|
353
|
+
schema: this.schema,
|
|
354
|
+
knex: this.knex,
|
|
355
|
+
accountability: this.accountability,
|
|
356
|
+
});
|
|
357
|
+
const payload = { email: user.email, scope: 'password-reset', hash: getSimpleHash('' + user.password) };
|
|
358
|
+
const token = jwt.sign(payload, env['SECRET'], { expiresIn: '1d', issuer: 'directus' });
|
|
359
|
+
const acceptURL = url
|
|
360
|
+
? new Url(url).setQuery('token', token).toString()
|
|
361
|
+
: new Url(env['PUBLIC_URL']).addPath('admin', 'reset-password').setQuery('token', token).toString();
|
|
362
|
+
const subjectLine = subject ? subject : 'Password Reset Request';
|
|
363
|
+
mailService
|
|
364
|
+
.send({
|
|
365
|
+
to: user.email,
|
|
366
|
+
subject: subjectLine,
|
|
367
|
+
template: {
|
|
368
|
+
name: 'password-reset',
|
|
369
|
+
data: {
|
|
370
|
+
url: acceptURL,
|
|
371
|
+
email: user.email,
|
|
372
|
+
},
|
|
373
|
+
},
|
|
374
|
+
})
|
|
375
|
+
.catch((err) => logger.warn(err, `[email] ${err}`));
|
|
347
376
|
}
|
|
348
|
-
|
|
349
|
-
|
|
377
|
+
finally {
|
|
378
|
+
await stall(STALL_TIME, timeStart);
|
|
350
379
|
}
|
|
351
|
-
const mailService = new MailService({
|
|
352
|
-
schema: this.schema,
|
|
353
|
-
knex: this.knex,
|
|
354
|
-
accountability: this.accountability,
|
|
355
|
-
});
|
|
356
|
-
const payload = { email: user.email, scope: 'password-reset', hash: getSimpleHash('' + user.password) };
|
|
357
|
-
const token = jwt.sign(payload, env['SECRET'], { expiresIn: '1d', issuer: 'directus' });
|
|
358
|
-
const acceptURL = url
|
|
359
|
-
? new Url(url).setQuery('token', token).toString()
|
|
360
|
-
: new Url(env['PUBLIC_URL']).addPath('admin', 'reset-password').setQuery('token', token).toString();
|
|
361
|
-
const subjectLine = subject ? subject : 'Password Reset Request';
|
|
362
|
-
await mailService.send({
|
|
363
|
-
to: user.email,
|
|
364
|
-
subject: subjectLine,
|
|
365
|
-
template: {
|
|
366
|
-
name: 'password-reset',
|
|
367
|
-
data: {
|
|
368
|
-
url: acceptURL,
|
|
369
|
-
email: user.email,
|
|
370
|
-
},
|
|
371
|
-
},
|
|
372
|
-
});
|
|
373
|
-
await stall(STALL_TIME, timeStart);
|
|
374
380
|
}
|
|
375
381
|
async resetPassword(token, password) {
|
|
376
382
|
const { email, scope, hash } = jwt.verify(token, env['SECRET'], { issuer: 'directus' });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wbce-d9/api",
|
|
3
|
-
"version": "12.
|
|
3
|
+
"version": "12.7.1",
|
|
4
4
|
"description": "Directus is a real-time API and App dashboard for managing SQL database content",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"@rollup/plugin-node-resolve": "15.0.2",
|
|
67
67
|
"@rollup/plugin-virtual": "3.0.1",
|
|
68
68
|
"@wbce-d9/format-title": "10.0.0",
|
|
69
|
-
"axios": "^1.
|
|
69
|
+
"axios": "^1.20.0",
|
|
70
70
|
"chalk": "5.2.0",
|
|
71
71
|
"commander": "10.0.1",
|
|
72
72
|
"date-fns": "2.29.3",
|
|
@@ -144,20 +144,20 @@
|
|
|
144
144
|
"tmp-promise": "3.0.3",
|
|
145
145
|
"tsx": "3.12.6",
|
|
146
146
|
"uuid-validate": "0.0.3",
|
|
147
|
-
"@wbce-d9/app": "11.
|
|
147
|
+
"@wbce-d9/app": "11.3.0",
|
|
148
148
|
"@wbce-d9/constants": "10.0.0",
|
|
149
|
+
"@wbce-d9/extensions-sdk": "10.1.0",
|
|
149
150
|
"@wbce-d9/exceptions": "10.0.1",
|
|
150
151
|
"@wbce-d9/schema": "10.0.0",
|
|
151
152
|
"@wbce-d9/storage": "10.0.0",
|
|
152
153
|
"@wbce-d9/specs": "9.26.5",
|
|
153
|
-
"@wbce-d9/storage-driver-
|
|
154
|
-
"@wbce-d9/storage-driver-
|
|
155
|
-
"@wbce-d9/
|
|
156
|
-
"@wbce-d9/
|
|
157
|
-
"@wbce-d9/storage-driver-
|
|
158
|
-
"@wbce-d9/storage-driver-s3": "10.0.4",
|
|
154
|
+
"@wbce-d9/storage-driver-cloudinary": "10.0.6",
|
|
155
|
+
"@wbce-d9/storage-driver-azure": "10.0.5",
|
|
156
|
+
"@wbce-d9/storage-driver-local": "10.0.5",
|
|
157
|
+
"@wbce-d9/utils": "10.0.5",
|
|
158
|
+
"@wbce-d9/storage-driver-s3": "10.0.5",
|
|
159
159
|
"@wbce-d9/update-check": "10.0.4",
|
|
160
|
-
"@wbce-d9/
|
|
160
|
+
"@wbce-d9/storage-driver-gcs": "10.0.5"
|
|
161
161
|
},
|
|
162
162
|
"devDependencies": {
|
|
163
163
|
"@ngneat/falso": "6.4.0",
|