directus 9.0.0-rc.99 → 9.1.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 +23 -16
- package/dist/auth/drivers/index.d.ts +1 -0
- package/dist/auth/drivers/index.js +1 -0
- package/dist/auth/drivers/ldap.d.ts +21 -0
- package/dist/auth/drivers/ldap.js +322 -0
- package/dist/auth/drivers/local.d.ts +1 -1
- package/dist/auth/drivers/local.js +3 -3
- package/dist/auth/drivers/oauth2.d.ts +1 -1
- package/dist/auth/drivers/oauth2.js +29 -22
- package/dist/auth/drivers/openid.d.ts +1 -1
- package/dist/auth/drivers/openid.js +34 -12
- package/dist/auth.js +2 -0
- package/dist/cache.d.ts +1 -1
- package/dist/cache.js +7 -7
- package/dist/cli/commands/init/index.js +1 -1
- package/dist/cli/index.js +3 -3
- package/dist/cli/index.test.js +10 -5
- package/dist/constants.js +1 -1
- package/dist/controllers/auth.js +3 -0
- package/dist/controllers/extensions.js +2 -0
- package/dist/controllers/files.js +19 -8
- package/dist/controllers/not-found.js +8 -2
- package/dist/controllers/notifications.d.ts +2 -0
- package/dist/controllers/notifications.js +147 -0
- package/dist/controllers/utils.js +11 -1
- package/dist/database/helpers/date.d.ts +8 -0
- package/dist/database/helpers/date.js +44 -0
- package/dist/database/helpers/geometry.d.ts +4 -2
- package/dist/database/helpers/geometry.js +48 -27
- package/dist/database/index.d.ts +1 -1
- package/dist/database/index.js +15 -21
- package/dist/database/migrations/20211016A-add-webhook-headers.d.ts +3 -0
- package/dist/database/migrations/20211016A-add-webhook-headers.js +15 -0
- package/dist/database/migrations/20211103A-set-unique-to-user-token.d.ts +3 -0
- package/dist/database/migrations/20211103A-set-unique-to-user-token.js +15 -0
- package/dist/database/migrations/20211103B-update-special-geometry.d.ts +3 -0
- package/dist/database/migrations/20211103B-update-special-geometry.js +25 -0
- package/dist/database/migrations/20211104A-remove-collections-listing.d.ts +3 -0
- package/dist/database/migrations/20211104A-remove-collections-listing.js +15 -0
- package/dist/database/migrations/20211118A-add-notifications.d.ts +3 -0
- package/dist/database/migrations/20211118A-add-notifications.js +28 -0
- package/dist/database/migrations/run.d.ts +1 -1
- package/dist/database/migrations/run.js +10 -4
- package/dist/database/run-ast.js +5 -19
- package/dist/database/seeds/run.js +4 -2
- package/dist/database/system-data/app-access-permissions/app-access-permissions.yaml +14 -0
- package/dist/database/system-data/collections/collections.yaml +2 -0
- package/dist/database/system-data/fields/activity.yaml +0 -2
- package/dist/database/system-data/fields/files.yaml +0 -1
- package/dist/database/system-data/fields/notifications.yaml +12 -0
- package/dist/database/system-data/fields/roles.yaml +0 -53
- package/dist/database/system-data/fields/settings.yaml +65 -69
- package/dist/database/system-data/fields/users.yaml +5 -1
- package/dist/database/system-data/fields/webhooks.yaml +25 -12
- package/dist/database/system-data/relations/relations.yaml +6 -0
- package/dist/emitter.d.ts +18 -8
- package/dist/emitter.js +68 -23
- package/dist/env.js +1 -0
- package/dist/exceptions/database/translate.js +26 -5
- package/dist/extensions.js +50 -27
- package/dist/index.d.ts +3 -0
- package/dist/index.js +20 -0
- package/dist/mailer.js +12 -3
- package/dist/middleware/authenticate.js +48 -48
- package/dist/middleware/error-handler.js +10 -3
- package/dist/middleware/get-permissions.d.ts +3 -0
- package/dist/middleware/get-permissions.js +15 -0
- package/dist/server.js +17 -7
- package/dist/services/activity.d.ts +7 -5
- package/dist/services/activity.js +82 -3
- package/dist/services/authentication.js +35 -49
- package/dist/services/authorization.d.ts +1 -1
- package/dist/services/authorization.js +13 -13
- package/dist/services/collections.d.ts +1 -1
- package/dist/services/collections.js +22 -23
- package/dist/services/fields.d.ts +1 -1
- package/dist/services/fields.js +29 -37
- package/dist/services/files.js +10 -11
- package/dist/services/graphql.js +11 -8
- package/dist/services/import.js +4 -4
- package/dist/services/index.d.ts +1 -0
- package/dist/services/index.js +1 -0
- package/dist/services/items.js +43 -83
- package/dist/services/mail/index.js +2 -2
- package/dist/services/mail/templates/base.liquid +153 -85
- package/dist/services/mail/templates/password-reset.liquid +3 -2
- package/dist/services/mail/templates/user-invitation.liquid +4 -4
- package/dist/services/meta.js +7 -8
- package/dist/services/notifications.d.ts +12 -0
- package/dist/services/notifications.js +41 -0
- package/dist/services/payload.js +13 -9
- package/dist/services/permissions.d.ts +13 -1
- package/dist/services/permissions.js +56 -2
- package/dist/services/relations.d.ts +1 -1
- package/dist/services/relations.js +12 -18
- package/dist/services/specifications.js +21 -2
- package/dist/services/users.js +1 -0
- package/dist/services/utils.js +3 -3
- package/dist/services/webhooks.d.ts +2 -2
- package/dist/types/auth.d.ts +1 -1
- package/dist/types/collection.d.ts +1 -0
- package/dist/types/extensions.d.ts +18 -2
- package/dist/types/schema.d.ts +2 -2
- package/dist/types/webhooks.d.ts +7 -2
- package/dist/utils/apply-query.d.ts +3 -3
- package/dist/utils/apply-query.js +52 -14
- package/dist/utils/apply-snapshot.js +2 -2
- package/dist/utils/get-ast-from-query.js +6 -6
- package/dist/utils/get-default-index-name.js +5 -6
- package/dist/utils/get-default-value.js +1 -1
- package/dist/utils/get-local-type.d.ts +2 -7
- package/dist/utils/get-local-type.js +106 -112
- package/dist/utils/get-permissions.d.ts +3 -0
- package/dist/utils/get-permissions.js +106 -0
- package/dist/utils/get-schema.js +11 -51
- package/dist/utils/get-simple-hash.d.ts +5 -0
- package/dist/utils/get-simple-hash.js +15 -0
- package/dist/utils/md.d.ts +4 -0
- package/dist/utils/md.js +15 -0
- package/dist/utils/reduce-schema.d.ts +2 -2
- package/dist/utils/reduce-schema.js +7 -10
- package/dist/utils/sanitize-query.js +3 -3
- package/dist/utils/user-name.d.ts +2 -0
- package/dist/utils/user-name.js +16 -0
- package/dist/utils/validate-query.js +4 -0
- package/dist/webhooks.js +18 -9
- package/package.json +39 -28
- package/dist/utils/geometry.d.ts +0 -2
- package/dist/utils/geometry.js +0 -20
- package/index.js +0 -5
|
@@ -31,6 +31,12 @@ class OpenIDAuthDriver extends local_1.LocalAuthDriver {
|
|
|
31
31
|
this.client = new Promise((resolve, reject) => {
|
|
32
32
|
openid_client_1.Issuer.discover(issuerUrl)
|
|
33
33
|
.then((issuer) => {
|
|
34
|
+
const supportedTypes = issuer.metadata.response_types_supported;
|
|
35
|
+
if (!(supportedTypes === null || supportedTypes === void 0 ? void 0 : supportedTypes.includes('code'))) {
|
|
36
|
+
reject(new exceptions_1.InvalidConfigException('OpenID provider does not support required code flow', {
|
|
37
|
+
provider: additionalConfig.provider,
|
|
38
|
+
}));
|
|
39
|
+
}
|
|
34
40
|
resolve(new issuer.Client({
|
|
35
41
|
client_id: clientId,
|
|
36
42
|
client_secret: clientSecret,
|
|
@@ -48,10 +54,13 @@ class OpenIDAuthDriver extends local_1.LocalAuthDriver {
|
|
|
48
54
|
var _a;
|
|
49
55
|
try {
|
|
50
56
|
const client = await this.client;
|
|
57
|
+
const codeChallenge = openid_client_1.generators.codeChallenge(codeVerifier);
|
|
51
58
|
return client.authorizationUrl({
|
|
52
59
|
scope: (_a = this.config.scope) !== null && _a !== void 0 ? _a : 'openid profile email',
|
|
53
|
-
code_challenge:
|
|
60
|
+
code_challenge: codeChallenge,
|
|
54
61
|
code_challenge_method: 'S256',
|
|
62
|
+
// Some providers require state even with PKCE
|
|
63
|
+
state: codeChallenge,
|
|
55
64
|
access_type: 'offline',
|
|
56
65
|
});
|
|
57
66
|
}
|
|
@@ -63,8 +72,7 @@ class OpenIDAuthDriver extends local_1.LocalAuthDriver {
|
|
|
63
72
|
const user = await this.knex
|
|
64
73
|
.select('id')
|
|
65
74
|
.from('directus_users')
|
|
66
|
-
.whereRaw('LOWER(??) = ?', ['
|
|
67
|
-
.orWhereRaw('LOWER(??) = ?', ['external_identifier', identifier.toLowerCase()])
|
|
75
|
+
.whereRaw('LOWER(??) = ?', ['external_identifier', identifier.toLowerCase()])
|
|
68
76
|
.first();
|
|
69
77
|
return user === null || user === void 0 ? void 0 : user.id;
|
|
70
78
|
}
|
|
@@ -77,8 +85,14 @@ class OpenIDAuthDriver extends local_1.LocalAuthDriver {
|
|
|
77
85
|
let userInfo;
|
|
78
86
|
try {
|
|
79
87
|
const client = await this.client;
|
|
80
|
-
tokenSet = await client.callback(this.redirectUrl, { code: payload.code }, { code_verifier: payload.codeVerifier });
|
|
81
|
-
|
|
88
|
+
tokenSet = await client.callback(this.redirectUrl, { code: payload.code, state: payload.state }, { code_verifier: payload.codeVerifier, state: openid_client_1.generators.codeChallenge(payload.codeVerifier) });
|
|
89
|
+
const issuer = client.issuer;
|
|
90
|
+
if (issuer.metadata.userinfo_endpoint) {
|
|
91
|
+
userInfo = await client.userinfo(tokenSet.access_token);
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
userInfo = tokenSet.claims();
|
|
95
|
+
}
|
|
82
96
|
}
|
|
83
97
|
catch (e) {
|
|
84
98
|
throw handleError(e);
|
|
@@ -106,21 +120,19 @@ class OpenIDAuthDriver extends local_1.LocalAuthDriver {
|
|
|
106
120
|
if (!allowPublicRegistration || !isEmailVerified) {
|
|
107
121
|
throw new exceptions_1.InvalidCredentialsException();
|
|
108
122
|
}
|
|
109
|
-
// If email matches identifier, don't set "external_identifier"
|
|
110
|
-
const emailIsIdentifier = (email === null || email === void 0 ? void 0 : email.toLowerCase()) === identifier.toLowerCase();
|
|
111
123
|
await this.usersService.createOne({
|
|
112
124
|
provider: this.config.provider,
|
|
113
125
|
first_name: userInfo.given_name,
|
|
114
126
|
last_name: userInfo.family_name,
|
|
115
127
|
email: email,
|
|
116
|
-
external_identifier:
|
|
128
|
+
external_identifier: identifier,
|
|
117
129
|
role: this.config.defaultRoleId,
|
|
118
130
|
auth_data: tokenSet.refresh_token && JSON.stringify({ refreshToken: tokenSet.refresh_token }),
|
|
119
131
|
});
|
|
120
132
|
return (await this.fetchUserId(identifier));
|
|
121
133
|
}
|
|
122
|
-
async login(user
|
|
123
|
-
return this.refresh(user,
|
|
134
|
+
async login(user) {
|
|
135
|
+
return this.refresh(user, null);
|
|
124
136
|
}
|
|
125
137
|
async refresh(user, sessionData) {
|
|
126
138
|
let authData = user.auth_data;
|
|
@@ -181,8 +193,14 @@ function createOpenIDAuthRouter(providerName) {
|
|
|
181
193
|
}), respond_1.respond);
|
|
182
194
|
router.get('/callback', (0, async_handler_1.default)(async (req, res, next) => {
|
|
183
195
|
var _a;
|
|
184
|
-
|
|
185
|
-
|
|
196
|
+
let tokenData;
|
|
197
|
+
try {
|
|
198
|
+
tokenData = jsonwebtoken_1.default.verify(req.cookies[`openid.${providerName}`], env_1.default.SECRET, { issuer: 'directus' });
|
|
199
|
+
}
|
|
200
|
+
catch (e) {
|
|
201
|
+
throw new exceptions_1.InvalidCredentialsException();
|
|
202
|
+
}
|
|
203
|
+
const { verifier, redirect } = tokenData;
|
|
186
204
|
const authenticationService = new services_1.AuthenticationService({
|
|
187
205
|
accountability: {
|
|
188
206
|
ip: req.ip,
|
|
@@ -194,9 +212,13 @@ function createOpenIDAuthRouter(providerName) {
|
|
|
194
212
|
let authResponse;
|
|
195
213
|
try {
|
|
196
214
|
res.clearCookie(`openid.${providerName}`);
|
|
215
|
+
if (!req.query.code || !req.query.state) {
|
|
216
|
+
logger_1.default.warn(`Couldn't extract OpenID code or state from query: ${JSON.stringify(req.query)}`);
|
|
217
|
+
}
|
|
197
218
|
authResponse = await authenticationService.login(providerName, {
|
|
198
219
|
code: req.query.code,
|
|
199
220
|
codeVerifier: verifier,
|
|
221
|
+
state: req.query.state,
|
|
200
222
|
});
|
|
201
223
|
}
|
|
202
224
|
catch (error) {
|
package/dist/auth.js
CHANGED
|
@@ -59,5 +59,7 @@ function getProviderInstance(driver, options, config = {}) {
|
|
|
59
59
|
return new drivers_1.OAuth2AuthDriver(options, config);
|
|
60
60
|
case 'openid':
|
|
61
61
|
return new drivers_1.OpenIDAuthDriver(options, config);
|
|
62
|
+
case 'ldap':
|
|
63
|
+
return new drivers_1.LDAPAuthDriver(options, config);
|
|
62
64
|
}
|
|
63
65
|
}
|
package/dist/cache.d.ts
CHANGED
package/dist/cache.js
CHANGED
|
@@ -11,23 +11,23 @@ const logger_1 = __importDefault(require("./logger"));
|
|
|
11
11
|
const get_config_from_env_1 = require("./utils/get-config-from-env");
|
|
12
12
|
const validate_env_1 = require("./utils/validate-env");
|
|
13
13
|
let cache = null;
|
|
14
|
-
let
|
|
14
|
+
let systemCache = null;
|
|
15
15
|
function getCache() {
|
|
16
16
|
if (env_1.default.CACHE_ENABLED === true && cache === null) {
|
|
17
17
|
(0, validate_env_1.validateEnv)(['CACHE_NAMESPACE', 'CACHE_TTL', 'CACHE_STORE']);
|
|
18
18
|
cache = getKeyvInstance((0, ms_1.default)(env_1.default.CACHE_TTL));
|
|
19
19
|
cache.on('error', (err) => logger_1.default.warn(err, `[cache] ${err}`));
|
|
20
20
|
}
|
|
21
|
-
if (
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
if (systemCache === null) {
|
|
22
|
+
systemCache = getKeyvInstance(undefined, '_system');
|
|
23
|
+
systemCache.on('error', (err) => logger_1.default.warn(err, `[cache] ${err}`));
|
|
24
24
|
}
|
|
25
|
-
return { cache,
|
|
25
|
+
return { cache, systemCache };
|
|
26
26
|
}
|
|
27
27
|
exports.getCache = getCache;
|
|
28
28
|
async function flushCaches() {
|
|
29
|
-
const {
|
|
30
|
-
await (
|
|
29
|
+
const { systemCache, cache } = getCache();
|
|
30
|
+
await (systemCache === null || systemCache === void 0 ? void 0 : systemCache.clear());
|
|
31
31
|
await (cache === null || cache === void 0 ? void 0 : cache.clear());
|
|
32
32
|
}
|
|
33
33
|
exports.flushCaches = flushCaches;
|
|
@@ -36,7 +36,7 @@ async function init() {
|
|
|
36
36
|
const db = (0, create_db_connection_1.default)(dbClient, credentials);
|
|
37
37
|
try {
|
|
38
38
|
await (0, run_2.default)(db);
|
|
39
|
-
await (0, run_1.default)(db, 'latest');
|
|
39
|
+
await (0, run_1.default)(db, 'latest', false);
|
|
40
40
|
}
|
|
41
41
|
catch (err) {
|
|
42
42
|
process.stdout.write('\nSomething went wrong while seeding the database:\n');
|
package/dist/cli/index.js
CHANGED
|
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.createCli = void 0;
|
|
7
7
|
const commander_1 = require("commander");
|
|
8
8
|
const server_1 = require("../server");
|
|
9
|
-
const emitter_1 = require("../emitter");
|
|
9
|
+
const emitter_1 = __importDefault(require("../emitter"));
|
|
10
10
|
const extensions_1 = require("../extensions");
|
|
11
11
|
const bootstrap_1 = __importDefault(require("./commands/bootstrap"));
|
|
12
12
|
const count_1 = __importDefault(require("./commands/count"));
|
|
@@ -23,7 +23,7 @@ async function createCli() {
|
|
|
23
23
|
const program = new commander_1.Command();
|
|
24
24
|
const extensionManager = (0, extensions_1.getExtensionManager)();
|
|
25
25
|
await extensionManager.initialize({ schedule: false });
|
|
26
|
-
await
|
|
26
|
+
await emitter_1.default.emitInit('cli.before', { program });
|
|
27
27
|
program.name('directus').usage('[command] [options]');
|
|
28
28
|
program.version(pkg.version, '-v, --version');
|
|
29
29
|
program.command('start').description('Start the Directus API').action(server_1.startServer);
|
|
@@ -83,7 +83,7 @@ async function createCli() {
|
|
|
83
83
|
.option('-y, --yes', `Assume "yes" as answer to all prompts and run non-interactively`)
|
|
84
84
|
.argument('<path>', 'Path to snapshot file')
|
|
85
85
|
.action(apply_1.apply);
|
|
86
|
-
await
|
|
86
|
+
await emitter_1.default.emitInit('cli.after', { program });
|
|
87
87
|
return program;
|
|
88
88
|
}
|
|
89
89
|
exports.createCli = createCli;
|
package/dist/cli/index.test.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const
|
|
3
|
+
const index_1 = require("./index");
|
|
4
4
|
jest.mock('../env', () => ({
|
|
5
5
|
...jest.requireActual('../env').default,
|
|
6
6
|
EXTENSIONS_PATH: '',
|
|
@@ -16,7 +16,7 @@ jest.mock('@directus/shared/utils/node/get-extensions', () => ({
|
|
|
16
16
|
getPackageExtensions: jest.fn(() => Promise.resolve([])),
|
|
17
17
|
getLocalExtensions: jest.fn(() => Promise.resolve([customCliExtension])),
|
|
18
18
|
}));
|
|
19
|
-
jest.mock(`/hooks/custom-cli/index.js`, () =>
|
|
19
|
+
jest.mock(`/hooks/custom-cli/index.js`, () => customCliHook, { virtual: true });
|
|
20
20
|
const customCliExtension = {
|
|
21
21
|
path: `/hooks/custom-cli`,
|
|
22
22
|
name: 'custom-cli',
|
|
@@ -26,12 +26,17 @@ const customCliExtension = {
|
|
|
26
26
|
};
|
|
27
27
|
const beforeHook = jest.fn();
|
|
28
28
|
const afterAction = jest.fn();
|
|
29
|
-
const afterHook = jest.fn(({ program }) =>
|
|
30
|
-
|
|
29
|
+
const afterHook = jest.fn(({ program }) => {
|
|
30
|
+
program.command('custom').action(afterAction);
|
|
31
|
+
});
|
|
32
|
+
const customCliHook = ({ init }) => {
|
|
33
|
+
init('cli.before', beforeHook);
|
|
34
|
+
init('cli.after', afterHook);
|
|
35
|
+
};
|
|
31
36
|
const writeOut = jest.fn();
|
|
32
37
|
const writeErr = jest.fn();
|
|
33
38
|
const setup = async () => {
|
|
34
|
-
const program = await (0,
|
|
39
|
+
const program = await (0, index_1.createCli)();
|
|
35
40
|
program.exitOverride();
|
|
36
41
|
program.configureOutput({ writeOut, writeErr });
|
|
37
42
|
return program;
|
package/dist/constants.js
CHANGED
|
@@ -38,6 +38,6 @@ exports.ASSET_TRANSFORM_QUERY_KEYS = [
|
|
|
38
38
|
'withoutEnlargement',
|
|
39
39
|
];
|
|
40
40
|
exports.FILTER_VARIABLES = ['$NOW', '$CURRENT_USER', '$CURRENT_ROLE'];
|
|
41
|
-
exports.ALIAS_TYPES = ['alias', 'o2m', 'm2m', 'm2a', 'files', '
|
|
41
|
+
exports.ALIAS_TYPES = ['alias', 'o2m', 'm2m', 'm2a', 'files', 'translations'];
|
|
42
42
|
exports.DEFAULT_AUTH_PROVIDER = 'default';
|
|
43
43
|
exports.COLUMN_TRANSFORMS = ['year', 'month', 'day', 'weekday', 'hour', 'minute', 'second'];
|
package/dist/controllers/auth.js
CHANGED
|
@@ -28,6 +28,9 @@ for (const authProvider of authProviders) {
|
|
|
28
28
|
case 'openid':
|
|
29
29
|
authRouter = (0, drivers_1.createOpenIDAuthRouter)(authProvider.name);
|
|
30
30
|
break;
|
|
31
|
+
case 'ldap':
|
|
32
|
+
authRouter = (0, drivers_1.createLDAPAuthRouter)(authProvider.name);
|
|
33
|
+
break;
|
|
31
34
|
}
|
|
32
35
|
if (!authRouter) {
|
|
33
36
|
logger_1.default.warn(`Couldn't create login router for auth provider "${authProvider.name}"`);
|
|
@@ -33,6 +33,8 @@ router.get('/:type/index.js', (0, async_handler_1.default)(async (req, res) => {
|
|
|
33
33
|
throw new exceptions_1.RouteNotFoundException(req.path);
|
|
34
34
|
}
|
|
35
35
|
res.setHeader('Content-Type', 'application/javascript; charset=UTF-8');
|
|
36
|
+
res.setHeader('Cache-Control', 'no-cache');
|
|
37
|
+
res.setHeader('Vary', 'Origin, Cache-Control');
|
|
36
38
|
res.end(extensionSource);
|
|
37
39
|
}));
|
|
38
40
|
exports.default = router;
|
|
@@ -21,7 +21,17 @@ router.use((0, use_collection_1.default)('directus_files'));
|
|
|
21
21
|
const multipartHandler = (0, async_handler_1.default)(async (req, res, next) => {
|
|
22
22
|
if (req.is('multipart/form-data') === false)
|
|
23
23
|
return next();
|
|
24
|
-
|
|
24
|
+
let headers;
|
|
25
|
+
if (req.headers['content-type']) {
|
|
26
|
+
headers = req.headers;
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
headers = {
|
|
30
|
+
...req.headers,
|
|
31
|
+
'content-type': 'application/octet-stream',
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
const busboy = new busboy_1.default({ headers });
|
|
25
35
|
const savedFiles = [];
|
|
26
36
|
const service = new services_1.FilesService({ accountability: req.accountability, schema: req.schema });
|
|
27
37
|
const existingPrimaryKey = req.params.pk || undefined;
|
|
@@ -34,16 +44,17 @@ const multipartHandler = (0, async_handler_1.default)(async (req, res, next) =>
|
|
|
34
44
|
let payload = {};
|
|
35
45
|
let fileCount = 0;
|
|
36
46
|
busboy.on('field', (fieldname, val) => {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
47
|
+
let fieldValue = val;
|
|
48
|
+
if (typeof fieldValue === 'string' && fieldValue.trim() === 'null')
|
|
49
|
+
fieldValue = null;
|
|
50
|
+
if (typeof fieldValue === 'string' && fieldValue.trim() === 'false')
|
|
51
|
+
fieldValue = false;
|
|
52
|
+
if (typeof fieldValue === 'string' && fieldValue.trim() === 'true')
|
|
53
|
+
fieldValue = true;
|
|
43
54
|
if (fieldname === 'storage') {
|
|
44
55
|
disk = val;
|
|
45
56
|
}
|
|
46
|
-
payload[fieldname] =
|
|
57
|
+
payload[fieldname] = fieldValue;
|
|
47
58
|
});
|
|
48
59
|
busboy.on('file', async (fieldname, fileStream, filename, encoding, mimetype) => {
|
|
49
60
|
fileCount++;
|
|
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const database_1 = __importDefault(require("../database"));
|
|
6
7
|
const emitter_1 = __importDefault(require("../emitter"));
|
|
7
8
|
const exceptions_1 = require("../exceptions");
|
|
8
9
|
/**
|
|
@@ -17,9 +18,14 @@ const exceptions_1 = require("../exceptions");
|
|
|
17
18
|
* @param next
|
|
18
19
|
*/
|
|
19
20
|
const notFound = async (req, res, next) => {
|
|
21
|
+
var _a;
|
|
20
22
|
try {
|
|
21
|
-
const hooksResult = await emitter_1.default.
|
|
22
|
-
|
|
23
|
+
const hooksResult = await emitter_1.default.emitFilter('request.not_found', false, { request: req, response: res }, {
|
|
24
|
+
database: (0, database_1.default)(),
|
|
25
|
+
schema: req.schema,
|
|
26
|
+
accountability: (_a = req.accountability) !== null && _a !== void 0 ? _a : null,
|
|
27
|
+
});
|
|
28
|
+
if (hooksResult) {
|
|
23
29
|
return next();
|
|
24
30
|
}
|
|
25
31
|
next(new exceptions_1.RouteNotFoundException(req.path));
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const express_1 = __importDefault(require("express"));
|
|
7
|
+
const exceptions_1 = require("../exceptions");
|
|
8
|
+
const respond_1 = require("../middleware/respond");
|
|
9
|
+
const use_collection_1 = __importDefault(require("../middleware/use-collection"));
|
|
10
|
+
const validate_batch_1 = require("../middleware/validate-batch");
|
|
11
|
+
const services_1 = require("../services");
|
|
12
|
+
const async_handler_1 = __importDefault(require("../utils/async-handler"));
|
|
13
|
+
const router = express_1.default.Router();
|
|
14
|
+
router.use((0, use_collection_1.default)('directus_notifications'));
|
|
15
|
+
router.post('/', (0, async_handler_1.default)(async (req, res, next) => {
|
|
16
|
+
const service = new services_1.NotificationsService({
|
|
17
|
+
accountability: req.accountability,
|
|
18
|
+
schema: req.schema,
|
|
19
|
+
});
|
|
20
|
+
const savedKeys = [];
|
|
21
|
+
if (Array.isArray(req.body)) {
|
|
22
|
+
const keys = await service.createMany(req.body);
|
|
23
|
+
savedKeys.push(...keys);
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
const key = await service.createOne(req.body);
|
|
27
|
+
savedKeys.push(key);
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
if (Array.isArray(req.body)) {
|
|
31
|
+
const records = await service.readMany(savedKeys, req.sanitizedQuery);
|
|
32
|
+
res.locals.payload = { data: records };
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
const record = await service.readOne(savedKeys[0], req.sanitizedQuery);
|
|
36
|
+
res.locals.payload = { data: record };
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
if (error instanceof exceptions_1.ForbiddenException) {
|
|
41
|
+
return next();
|
|
42
|
+
}
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
45
|
+
return next();
|
|
46
|
+
}), respond_1.respond);
|
|
47
|
+
const readHandler = (0, async_handler_1.default)(async (req, res, next) => {
|
|
48
|
+
const service = new services_1.NotificationsService({
|
|
49
|
+
accountability: req.accountability,
|
|
50
|
+
schema: req.schema,
|
|
51
|
+
});
|
|
52
|
+
const metaService = new services_1.MetaService({
|
|
53
|
+
accountability: req.accountability,
|
|
54
|
+
schema: req.schema,
|
|
55
|
+
});
|
|
56
|
+
let result;
|
|
57
|
+
if (req.singleton) {
|
|
58
|
+
result = await service.readSingleton(req.sanitizedQuery);
|
|
59
|
+
}
|
|
60
|
+
else if (req.body.keys) {
|
|
61
|
+
result = await service.readMany(req.body.keys, req.sanitizedQuery);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
result = await service.readByQuery(req.sanitizedQuery);
|
|
65
|
+
}
|
|
66
|
+
const meta = await metaService.getMetaForQuery('directus_presets', req.sanitizedQuery);
|
|
67
|
+
res.locals.payload = { data: result, meta };
|
|
68
|
+
return next();
|
|
69
|
+
});
|
|
70
|
+
router.get('/', (0, validate_batch_1.validateBatch)('read'), readHandler, respond_1.respond);
|
|
71
|
+
router.search('/', (0, validate_batch_1.validateBatch)('read'), readHandler, respond_1.respond);
|
|
72
|
+
router.get('/:pk', (0, async_handler_1.default)(async (req, res, next) => {
|
|
73
|
+
const service = new services_1.NotificationsService({
|
|
74
|
+
accountability: req.accountability,
|
|
75
|
+
schema: req.schema,
|
|
76
|
+
});
|
|
77
|
+
const record = await service.readOne(req.params.pk, req.sanitizedQuery);
|
|
78
|
+
res.locals.payload = { data: record || null };
|
|
79
|
+
return next();
|
|
80
|
+
}), respond_1.respond);
|
|
81
|
+
router.patch('/', (0, validate_batch_1.validateBatch)('update'), (0, async_handler_1.default)(async (req, res, next) => {
|
|
82
|
+
const service = new services_1.NotificationsService({
|
|
83
|
+
accountability: req.accountability,
|
|
84
|
+
schema: req.schema,
|
|
85
|
+
});
|
|
86
|
+
let keys = [];
|
|
87
|
+
if (req.body.keys) {
|
|
88
|
+
keys = await service.updateMany(req.body.keys, req.body.data);
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
keys = await service.updateByQuery(req.body.query, req.body.data);
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
const result = await service.readMany(keys, req.sanitizedQuery);
|
|
95
|
+
res.locals.payload = { data: result };
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
if (error instanceof exceptions_1.ForbiddenException) {
|
|
99
|
+
return next();
|
|
100
|
+
}
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
return next();
|
|
104
|
+
}), respond_1.respond);
|
|
105
|
+
router.patch('/:pk', (0, async_handler_1.default)(async (req, res, next) => {
|
|
106
|
+
const service = new services_1.NotificationsService({
|
|
107
|
+
accountability: req.accountability,
|
|
108
|
+
schema: req.schema,
|
|
109
|
+
});
|
|
110
|
+
const primaryKey = await service.updateOne(req.params.pk, req.body);
|
|
111
|
+
try {
|
|
112
|
+
const record = await service.readOne(primaryKey, req.sanitizedQuery);
|
|
113
|
+
res.locals.payload = { data: record };
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
if (error instanceof exceptions_1.ForbiddenException) {
|
|
117
|
+
return next();
|
|
118
|
+
}
|
|
119
|
+
throw error;
|
|
120
|
+
}
|
|
121
|
+
return next();
|
|
122
|
+
}), respond_1.respond);
|
|
123
|
+
router.delete('/', (0, validate_batch_1.validateBatch)('delete'), (0, async_handler_1.default)(async (req, res, next) => {
|
|
124
|
+
const service = new services_1.NotificationsService({
|
|
125
|
+
accountability: req.accountability,
|
|
126
|
+
schema: req.schema,
|
|
127
|
+
});
|
|
128
|
+
if (Array.isArray(req.body)) {
|
|
129
|
+
await service.deleteMany(req.body);
|
|
130
|
+
}
|
|
131
|
+
else if (req.body.keys) {
|
|
132
|
+
await service.deleteMany(req.body.keys);
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
await service.deleteByQuery(req.body.query);
|
|
136
|
+
}
|
|
137
|
+
return next();
|
|
138
|
+
}), respond_1.respond);
|
|
139
|
+
router.delete('/:pk', (0, async_handler_1.default)(async (req, res, next) => {
|
|
140
|
+
const service = new services_1.NotificationsService({
|
|
141
|
+
accountability: req.accountability,
|
|
142
|
+
schema: req.schema,
|
|
143
|
+
});
|
|
144
|
+
await service.deleteOne(req.params.pk);
|
|
145
|
+
return next();
|
|
146
|
+
}), respond_1.respond);
|
|
147
|
+
exports.default = router;
|
|
@@ -70,7 +70,17 @@ router.post('/import/:collection', collection_exists_1.default, (0, async_handle
|
|
|
70
70
|
accountability: req.accountability,
|
|
71
71
|
schema: req.schema,
|
|
72
72
|
});
|
|
73
|
-
|
|
73
|
+
let headers;
|
|
74
|
+
if (req.headers['content-type']) {
|
|
75
|
+
headers = req.headers;
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
headers = {
|
|
79
|
+
...req.headers,
|
|
80
|
+
'content-type': 'application/octet-stream',
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
const busboy = new busboy_1.default({ headers });
|
|
74
84
|
busboy.on('file', async (fieldname, fileStream, filename, encoding, mimetype) => {
|
|
75
85
|
try {
|
|
76
86
|
await service.import(req.params.collection, mimetype, fileStream);
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getDateHelper = void 0;
|
|
7
|
+
const __1 = __importDefault(require(".."));
|
|
8
|
+
let dateHelper;
|
|
9
|
+
function getDateHelper() {
|
|
10
|
+
if (!dateHelper) {
|
|
11
|
+
const db = (0, __1.default)();
|
|
12
|
+
const client = db.client.config.client;
|
|
13
|
+
const constructor = {
|
|
14
|
+
mysql: KnexDate,
|
|
15
|
+
mariadb: KnexDate,
|
|
16
|
+
sqlite3: KnexDate_SQLITE,
|
|
17
|
+
pg: KnexDate,
|
|
18
|
+
postgres: KnexDate,
|
|
19
|
+
redshift: KnexDate,
|
|
20
|
+
mssql: KnexDate,
|
|
21
|
+
oracledb: KnexDate,
|
|
22
|
+
}[client];
|
|
23
|
+
if (!constructor) {
|
|
24
|
+
throw new Error(`Geometry helper not implemented on ${client}.`);
|
|
25
|
+
}
|
|
26
|
+
dateHelper = new constructor(db);
|
|
27
|
+
}
|
|
28
|
+
return dateHelper;
|
|
29
|
+
}
|
|
30
|
+
exports.getDateHelper = getDateHelper;
|
|
31
|
+
class KnexDate {
|
|
32
|
+
constructor(knex) {
|
|
33
|
+
this.knex = knex;
|
|
34
|
+
}
|
|
35
|
+
parseDate(date) {
|
|
36
|
+
return date;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
class KnexDate_SQLITE extends KnexDate {
|
|
40
|
+
parseDate(date) {
|
|
41
|
+
const newDate = new Date(date);
|
|
42
|
+
return (newDate.getTime() - newDate.getTimezoneOffset() * 60 * 1000).toString();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { Field, RawField } from '@directus/shared/types';
|
|
2
2
|
import { Knex } from 'knex';
|
|
3
3
|
import { GeoJSONGeometry } from 'wellknown';
|
|
4
|
-
export declare function getGeometryHelper(): KnexSpatial;
|
|
5
|
-
declare class KnexSpatial {
|
|
4
|
+
export declare function getGeometryHelper(database?: Knex): KnexSpatial;
|
|
5
|
+
declare abstract class KnexSpatial {
|
|
6
6
|
protected knex: Knex;
|
|
7
7
|
constructor(knex: Knex);
|
|
8
|
+
supported(): boolean | Promise<boolean>;
|
|
8
9
|
isTrue(expression: Knex.Raw): Knex.Raw<any>;
|
|
9
10
|
isFalse(expression: Knex.Raw): Knex.Raw<any>;
|
|
10
11
|
createColumn(table: Knex.CreateTableBuilder, field: RawField | Field): Knex.ColumnBuilder;
|
|
11
12
|
asText(table: string, column: string): Knex.Raw;
|
|
13
|
+
asGeoJSON?(table: string, column: string): Knex.Raw;
|
|
12
14
|
fromText(text: string): Knex.Raw;
|
|
13
15
|
fromGeoJSON(geojson: GeoJSONGeometry): Knex.Raw;
|
|
14
16
|
_intersects(key: string, geojson: GeoJSONGeometry): Knex.Raw;
|