directus 9.8.0 → 9.10.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/README.md +1 -1
- package/dist/__mocks__/cache.d.ts +5 -0
- package/dist/__mocks__/cache.js +7 -0
- package/dist/app.js +3 -0
- package/dist/auth/drivers/ldap.js +10 -11
- package/dist/auth/drivers/oauth2.js +11 -6
- package/dist/auth/drivers/openid.js +9 -6
- package/dist/cli/commands/schema/apply.js +9 -3
- package/dist/controllers/assets.js +5 -0
- package/dist/controllers/files.d.ts +2 -0
- package/dist/controllers/files.js +13 -5
- package/dist/database/helpers/date/dialects/mssql.d.ts +4 -0
- package/dist/database/helpers/date/dialects/mssql.js +12 -0
- package/dist/database/helpers/date/dialects/mysql.d.ts +5 -0
- package/dist/database/helpers/date/dialects/mysql.js +16 -0
- package/dist/database/helpers/date/dialects/oracle.d.ts +4 -0
- package/dist/database/helpers/date/dialects/oracle.js +15 -0
- package/dist/database/helpers/date/dialects/sqlite.d.ts +1 -0
- package/dist/database/helpers/date/dialects/sqlite.js +8 -0
- package/dist/database/helpers/date/index.d.ts +3 -3
- package/dist/database/helpers/date/index.js +6 -6
- package/dist/database/helpers/date/types.d.ts +3 -0
- package/dist/database/helpers/date/types.js +10 -0
- package/dist/database/helpers/fn/dialects/postgres.js +5 -1
- package/dist/database/helpers/index.d.ts +1 -1
- package/dist/database/migrations/20220402A-remove-default-value-panel-icon.d.ts +3 -0
- package/dist/database/migrations/20220402A-remove-default-value-panel-icon.js +22 -0
- package/dist/database/run-ast.js +3 -3
- package/dist/database/system-data/fields/collections.yaml +1 -1
- package/dist/database/system-data/fields/settings.yaml +0 -1
- package/dist/database/system-data/fields/users.yaml +3 -0
- package/dist/env.js +9 -3
- package/dist/exceptions/index.d.ts +1 -0
- package/dist/exceptions/index.js +1 -0
- package/dist/exceptions/token-expired.d.ts +4 -0
- package/dist/exceptions/token-expired.js +10 -0
- package/dist/middleware/cache.js +10 -0
- package/dist/services/authorization.js +72 -30
- package/dist/services/collections.d.ts +2 -0
- package/dist/services/collections.js +10 -0
- package/dist/services/fields.js +26 -1
- package/dist/services/files.d.ts +5 -1
- package/dist/services/files.js +59 -40
- package/dist/services/graphql.d.ts +2 -3
- package/dist/services/graphql.js +65 -11
- package/dist/services/import-export.js +2 -0
- package/dist/services/items.js +12 -5
- package/dist/services/payload.d.ts +2 -1
- package/dist/services/payload.js +22 -17
- package/dist/services/specifications.js +1 -3
- package/dist/services/users.js +4 -1
- package/dist/types/files.d.ts +8 -0
- package/dist/utils/apply-query.d.ts +2 -1
- package/dist/utils/apply-query.js +134 -156
- package/dist/utils/apply-snapshot.d.ts +3 -1
- package/dist/utils/apply-snapshot.js +34 -5
- package/dist/utils/get-ast-from-query.js +15 -3
- package/dist/utils/get-column-path.d.ts +16 -0
- package/dist/utils/get-column-path.js +46 -0
- package/dist/utils/get-graphql-type.js +1 -0
- package/dist/utils/get-local-type.js +5 -0
- package/dist/utils/get-relation-info.d.ts +7 -0
- package/dist/utils/get-relation-info.js +45 -0
- package/dist/utils/get-relation-type.d.ts +1 -1
- package/dist/utils/get-schema.js +3 -0
- package/dist/utils/jwt.js +1 -1
- package/dist/utils/merge-permissions-for-share.js +1 -1
- package/dist/utils/reduce-schema.js +18 -11
- package/dist/utils/validate-query.js +19 -15
- package/example.env +4 -0
- package/package.json +18 -19
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ view, author, and manage your raw database content. Our performant and flexible
|
|
|
16
16
|
schema, and includes rule-based permissions, event/web hooks, custom endpoints, numerous auth options, configurable
|
|
17
17
|
storage adapters, and much more.
|
|
18
18
|
|
|
19
|
-
Current database support includes: PostgreSQL, MySQL, SQLite, MS-SQL Server, OracleDB, MariaDB, and
|
|
19
|
+
Current database support includes: PostgreSQL, MySQL, SQLite, MS-SQL Server, OracleDB, MariaDB, and variants such as AWS
|
|
20
20
|
Aurora/Redshift or Google Cloud Platform SQL.
|
|
21
21
|
|
|
22
22
|
Learn more at...
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.setSystemCache = exports.clearSystemCache = exports.flushCaches = exports.getCache = void 0;
|
|
4
|
+
exports.getCache = jest.fn().mockReturnValue({ cache: undefined, systemCache: undefined, lockCache: undefined });
|
|
5
|
+
exports.flushCaches = jest.fn();
|
|
6
|
+
exports.clearSystemCache = jest.fn();
|
|
7
|
+
exports.setSystemCache = jest.fn();
|
package/dist/app.js
CHANGED
|
@@ -119,6 +119,9 @@ async function createApp() {
|
|
|
119
119
|
connectSrc: ["'self'", 'https://*'],
|
|
120
120
|
},
|
|
121
121
|
}, (0, get_config_from_env_1.getConfigFromEnv)('CONTENT_SECURITY_POLICY_'))));
|
|
122
|
+
if (env_1.default.HSTS_ENABLED) {
|
|
123
|
+
app.use(helmet_1.default.hsts((0, get_config_from_env_1.getConfigFromEnv)('HSTS_', ['HSTS_ENABLED'])));
|
|
124
|
+
}
|
|
122
125
|
await emitter_1.default.emitInit('app.before', { app });
|
|
123
126
|
await emitter_1.default.emitInit('middlewares.before', { app });
|
|
124
127
|
app.use(logger_1.expressLogger);
|
|
@@ -71,12 +71,8 @@ class LDAPAuthDriver extends auth_1.AuthDriver {
|
|
|
71
71
|
res.on('searchEntry', () => {
|
|
72
72
|
resolve();
|
|
73
73
|
});
|
|
74
|
-
res.on('error', (
|
|
75
|
-
|
|
76
|
-
reject(handleError(err));
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
// Rebind on OperationsError
|
|
74
|
+
res.on('error', () => {
|
|
75
|
+
// Attempt to rebind on search error
|
|
80
76
|
this.bindClient.bind(bindDn, bindPassword, (err) => {
|
|
81
77
|
if (err) {
|
|
82
78
|
const error = handleError(err);
|
|
@@ -93,8 +89,8 @@ class LDAPAuthDriver extends auth_1.AuthDriver {
|
|
|
93
89
|
});
|
|
94
90
|
});
|
|
95
91
|
res.on('end', (result) => {
|
|
92
|
+
// Handle edge case where authenticated bind user cannot read their own DN
|
|
96
93
|
if ((result === null || result === void 0 ? void 0 : result.status) === 0) {
|
|
97
|
-
// Handle edge case where authenticated bind user could not fetch their own DN
|
|
98
94
|
reject(new exceptions_1.UnexpectedResponseException('Failed to find bind user record'));
|
|
99
95
|
}
|
|
100
96
|
});
|
|
@@ -177,12 +173,12 @@ class LDAPAuthDriver extends auth_1.AuthDriver {
|
|
|
177
173
|
return user === null || user === void 0 ? void 0 : user.id;
|
|
178
174
|
}
|
|
179
175
|
async getUserID(payload) {
|
|
180
|
-
var _a;
|
|
176
|
+
var _a, _b, _c;
|
|
181
177
|
if (!payload.identifier) {
|
|
182
178
|
throw new exceptions_1.InvalidCredentialsException();
|
|
183
179
|
}
|
|
184
180
|
await this.validateBindClient();
|
|
185
|
-
const { userDn, userScope, userAttribute, groupDn, groupScope, groupAttribute } = this.config;
|
|
181
|
+
const { userDn, userScope, userAttribute, groupDn, groupScope, groupAttribute, defaultRoleId } = this.config;
|
|
186
182
|
const userInfo = await this.fetchUserInfo(userDn, new ldapjs_1.EqualityFilter({
|
|
187
183
|
attribute: userAttribute !== null && userAttribute !== void 0 ? userAttribute : 'cn',
|
|
188
184
|
value: payload.identifier,
|
|
@@ -209,7 +205,10 @@ class LDAPAuthDriver extends auth_1.AuthDriver {
|
|
|
209
205
|
}
|
|
210
206
|
const userId = await this.fetchUserId(userInfo.dn);
|
|
211
207
|
if (userId) {
|
|
212
|
-
|
|
208
|
+
// Only sync roles if the AD groups are configured
|
|
209
|
+
if (groupDn) {
|
|
210
|
+
await this.usersService.updateOne(userId, { role: (_b = (_a = userRole === null || userRole === void 0 ? void 0 : userRole.id) !== null && _a !== void 0 ? _a : defaultRoleId) !== null && _b !== void 0 ? _b : null });
|
|
211
|
+
}
|
|
213
212
|
return userId;
|
|
214
213
|
}
|
|
215
214
|
if (!userInfo) {
|
|
@@ -221,7 +220,7 @@ class LDAPAuthDriver extends auth_1.AuthDriver {
|
|
|
221
220
|
last_name: userInfo.lastName,
|
|
222
221
|
email: userInfo.email,
|
|
223
222
|
external_identifier: userInfo.dn,
|
|
224
|
-
role: userRole === null || userRole === void 0 ? void 0 : userRole.id,
|
|
223
|
+
role: (_c = userRole === null || userRole === void 0 ? void 0 : userRole.id) !== null && _c !== void 0 ? _c : defaultRoleId,
|
|
225
224
|
});
|
|
226
225
|
return (await this.fetchUserId(userInfo.dn));
|
|
227
226
|
}
|
|
@@ -8,6 +8,7 @@ const express_1 = require("express");
|
|
|
8
8
|
const openid_client_1 = require("openid-client");
|
|
9
9
|
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
|
10
10
|
const ms_1 = __importDefault(require("ms"));
|
|
11
|
+
const flat_1 = __importDefault(require("flat"));
|
|
11
12
|
const local_1 = require("./local");
|
|
12
13
|
const auth_1 = require("../../auth");
|
|
13
14
|
const env_1 = __importDefault(require("../../env"));
|
|
@@ -77,7 +78,7 @@ class OAuth2AuthDriver extends local_1.LocalAuthDriver {
|
|
|
77
78
|
return user === null || user === void 0 ? void 0 : user.id;
|
|
78
79
|
}
|
|
79
80
|
async getUserID(payload) {
|
|
80
|
-
var _a;
|
|
81
|
+
var _a, _b;
|
|
81
82
|
if (!payload.code || !payload.codeVerifier) {
|
|
82
83
|
logger_1.default.trace('[OAuth2] No code or codeVerifier in payload');
|
|
83
84
|
throw new exceptions_1.InvalidCredentialsException();
|
|
@@ -91,12 +92,14 @@ class OAuth2AuthDriver extends local_1.LocalAuthDriver {
|
|
|
91
92
|
catch (e) {
|
|
92
93
|
throw handleError(e);
|
|
93
94
|
}
|
|
94
|
-
|
|
95
|
+
// Flatten response to support dot indexes
|
|
96
|
+
userInfo = (0, flat_1.default)(userInfo);
|
|
97
|
+
const { provider, emailKey, identifierKey, allowPublicRegistration } = this.config;
|
|
95
98
|
const email = userInfo[emailKey !== null && emailKey !== void 0 ? emailKey : 'email'];
|
|
96
99
|
// Fallback to email if explicit identifier not found
|
|
97
|
-
const identifier = (_a = userInfo[identifierKey]) !== null && _a !== void 0 ? _a : email;
|
|
100
|
+
const identifier = (_b = ((_a = userInfo[identifierKey]) !== null && _a !== void 0 ? _a : email)) === null || _b === void 0 ? void 0 : _b.toString();
|
|
98
101
|
if (!identifier) {
|
|
99
|
-
logger_1.default.warn(`[OAuth2] Failed to find user identifier for provider "${
|
|
102
|
+
logger_1.default.warn(`[OAuth2] Failed to find user identifier for provider "${provider}"`);
|
|
100
103
|
throw new exceptions_1.InvalidCredentialsException();
|
|
101
104
|
}
|
|
102
105
|
const userId = await this.fetchUserId(identifier);
|
|
@@ -111,11 +114,13 @@ class OAuth2AuthDriver extends local_1.LocalAuthDriver {
|
|
|
111
114
|
}
|
|
112
115
|
// Is public registration allowed?
|
|
113
116
|
if (!allowPublicRegistration) {
|
|
114
|
-
logger_1.default.trace(`[OAuth2] User doesn't exist, and public registration not allowed for provider "${
|
|
117
|
+
logger_1.default.trace(`[OAuth2] User doesn't exist, and public registration not allowed for provider "${provider}"`);
|
|
115
118
|
throw new exceptions_1.InvalidCredentialsException();
|
|
116
119
|
}
|
|
117
120
|
await this.usersService.createOne({
|
|
118
|
-
provider
|
|
121
|
+
provider,
|
|
122
|
+
first_name: userInfo[this.config.firstNameKey],
|
|
123
|
+
last_name: userInfo[this.config.lastNameKey],
|
|
119
124
|
email: email,
|
|
120
125
|
external_identifier: identifier,
|
|
121
126
|
role: this.config.defaultRoleId,
|
|
@@ -8,6 +8,7 @@ const express_1 = require("express");
|
|
|
8
8
|
const openid_client_1 = require("openid-client");
|
|
9
9
|
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
|
10
10
|
const ms_1 = __importDefault(require("ms"));
|
|
11
|
+
const flat_1 = __importDefault(require("flat"));
|
|
11
12
|
const local_1 = require("./local");
|
|
12
13
|
const auth_1 = require("../../auth");
|
|
13
14
|
const env_1 = __importDefault(require("../../env"));
|
|
@@ -84,7 +85,7 @@ class OpenIDAuthDriver extends local_1.LocalAuthDriver {
|
|
|
84
85
|
return user === null || user === void 0 ? void 0 : user.id;
|
|
85
86
|
}
|
|
86
87
|
async getUserID(payload) {
|
|
87
|
-
var _a;
|
|
88
|
+
var _a, _b;
|
|
88
89
|
if (!payload.code || !payload.codeVerifier) {
|
|
89
90
|
logger_1.default.trace('[OpenID] No code or codeVerifier in payload');
|
|
90
91
|
throw new exceptions_1.InvalidCredentialsException();
|
|
@@ -105,12 +106,14 @@ class OpenIDAuthDriver extends local_1.LocalAuthDriver {
|
|
|
105
106
|
catch (e) {
|
|
106
107
|
throw handleError(e);
|
|
107
108
|
}
|
|
108
|
-
|
|
109
|
+
// Flatten response to support dot indexes
|
|
110
|
+
userInfo = (0, flat_1.default)(userInfo);
|
|
111
|
+
const { provider, identifierKey, allowPublicRegistration, requireVerifiedEmail } = this.config;
|
|
109
112
|
const email = userInfo.email;
|
|
110
113
|
// Fallback to email if explicit identifier not found
|
|
111
|
-
const identifier = (_a = userInfo[identifierKey !== null && identifierKey !== void 0 ? identifierKey : 'sub']) !== null &&
|
|
114
|
+
const identifier = (_b = (_a = userInfo[identifierKey !== null && identifierKey !== void 0 ? identifierKey : 'sub']) === null || _a === void 0 ? void 0 : _a.toString()) !== null && _b !== void 0 ? _b : email;
|
|
112
115
|
if (!identifier) {
|
|
113
|
-
logger_1.default.warn(`[OpenID] Failed to find user identifier for provider "${
|
|
116
|
+
logger_1.default.warn(`[OpenID] Failed to find user identifier for provider "${provider}"`);
|
|
114
117
|
throw new exceptions_1.InvalidCredentialsException();
|
|
115
118
|
}
|
|
116
119
|
const userId = await this.fetchUserId(identifier);
|
|
@@ -126,11 +129,11 @@ class OpenIDAuthDriver extends local_1.LocalAuthDriver {
|
|
|
126
129
|
const isEmailVerified = !requireVerifiedEmail || userInfo.email_verified;
|
|
127
130
|
// Is public registration allowed?
|
|
128
131
|
if (!allowPublicRegistration || !isEmailVerified) {
|
|
129
|
-
logger_1.default.trace(`[OpenID] User doesn't exist, and public registration not allowed for provider "${
|
|
132
|
+
logger_1.default.trace(`[OpenID] User doesn't exist, and public registration not allowed for provider "${provider}"`);
|
|
130
133
|
throw new exceptions_1.InvalidCredentialsException();
|
|
131
134
|
}
|
|
132
135
|
await this.usersService.createOne({
|
|
133
|
-
provider
|
|
136
|
+
provider,
|
|
134
137
|
first_name: userInfo.given_name,
|
|
135
138
|
last_name: userInfo.family_name,
|
|
136
139
|
email: email,
|
|
@@ -93,13 +93,19 @@ async function apply(snapshotPath, options) {
|
|
|
93
93
|
if (snapshotDiff.fields.length > 0) {
|
|
94
94
|
message += '\n\n' + chalk_1.default.black.underline.bold('Fields:');
|
|
95
95
|
for (const { collection, field, diff } of snapshotDiff.fields) {
|
|
96
|
-
if (((_e = diff[0]) === null || _e === void 0 ? void 0 : _e.kind) === 'E') {
|
|
96
|
+
if (((_e = diff[0]) === null || _e === void 0 ? void 0 : _e.kind) === 'E' || (0, apply_snapshot_1.isNestedMetaUpdate)(diff[0])) {
|
|
97
97
|
message += `\n - ${chalk_1.default.blue('Update')} ${collection}.${field}`;
|
|
98
98
|
for (const change of diff) {
|
|
99
|
+
const path = change.path.slice(1).join('.');
|
|
99
100
|
if (change.kind === 'E') {
|
|
100
|
-
const path = change.path.slice(1).join('.');
|
|
101
101
|
message += `\n - Set ${path} to ${change.rhs}`;
|
|
102
102
|
}
|
|
103
|
+
else if (change.kind === 'D') {
|
|
104
|
+
message += `\n - Remove ${path}`;
|
|
105
|
+
}
|
|
106
|
+
else if (change.kind === 'N') {
|
|
107
|
+
message += `\n - Add ${path} and set it to ${change.rhs}`;
|
|
108
|
+
}
|
|
103
109
|
}
|
|
104
110
|
}
|
|
105
111
|
else if (((_f = diff[0]) === null || _f === void 0 ? void 0 : _f.kind) === 'D') {
|
|
@@ -143,7 +149,7 @@ async function apply(snapshotPath, options) {
|
|
|
143
149
|
}
|
|
144
150
|
}
|
|
145
151
|
}
|
|
146
|
-
message
|
|
152
|
+
message = 'The following changes will be applied:\n\n' + chalk_1.default.black(message);
|
|
147
153
|
if (dryRun) {
|
|
148
154
|
logger_1.default.info(message);
|
|
149
155
|
process.exit(0);
|
|
@@ -126,6 +126,11 @@ router.get('/:pk',
|
|
|
126
126
|
res.setHeader('Content-Type', file.type);
|
|
127
127
|
res.setHeader('Accept-Ranges', 'bytes');
|
|
128
128
|
res.setHeader('Cache-Control', `${access}, max-age=${(0, ms_1.default)(env_1.default.ASSETS_CACHE_TTL) / 1000}`);
|
|
129
|
+
const unixTime = Date.parse(file.modified_on);
|
|
130
|
+
if (!Number.isNaN(unixTime)) {
|
|
131
|
+
const lastModifiedDate = new Date(unixTime);
|
|
132
|
+
res.setHeader('Last-Modified', lastModifiedDate.toUTCString());
|
|
133
|
+
}
|
|
129
134
|
if (range) {
|
|
130
135
|
res.setHeader('Content-Range', `bytes ${range.start}-${range.end || stat.size - 1}/${stat.size}`);
|
|
131
136
|
res.status(206);
|
|
@@ -3,7 +3,9 @@ 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
|
+
exports.multipartHandler = void 0;
|
|
6
7
|
const format_title_1 = __importDefault(require("@directus/format-title"));
|
|
8
|
+
const utils_1 = require("@directus/shared/utils");
|
|
7
9
|
const busboy_1 = __importDefault(require("busboy"));
|
|
8
10
|
const express_1 = __importDefault(require("express"));
|
|
9
11
|
const joi_1 = __importDefault(require("joi"));
|
|
@@ -15,10 +17,9 @@ const use_collection_1 = __importDefault(require("../middleware/use-collection")
|
|
|
15
17
|
const validate_batch_1 = require("../middleware/validate-batch");
|
|
16
18
|
const services_1 = require("../services");
|
|
17
19
|
const async_handler_1 = __importDefault(require("../utils/async-handler"));
|
|
18
|
-
const utils_1 = require("@directus/shared/utils");
|
|
19
20
|
const router = express_1.default.Router();
|
|
20
21
|
router.use((0, use_collection_1.default)('directus_files'));
|
|
21
|
-
const multipartHandler = (
|
|
22
|
+
const multipartHandler = (req, res, next) => {
|
|
22
23
|
if (req.is('multipart/form-data') === false)
|
|
23
24
|
return next();
|
|
24
25
|
let headers;
|
|
@@ -57,6 +58,9 @@ const multipartHandler = (0, async_handler_1.default)(async (req, res, next) =>
|
|
|
57
58
|
payload[fieldname] = fieldValue;
|
|
58
59
|
});
|
|
59
60
|
busboy.on('file', async (fieldname, fileStream, filename, encoding, mimetype) => {
|
|
61
|
+
if (!filename) {
|
|
62
|
+
return busboy.emit('error', new exceptions_1.InvalidPayloadException(`File is missing filename`));
|
|
63
|
+
}
|
|
60
64
|
fileCount++;
|
|
61
65
|
if (!payload.title) {
|
|
62
66
|
payload.title = (0, format_title_1.default)(path_1.default.parse(filename).name);
|
|
@@ -87,12 +91,16 @@ const multipartHandler = (0, async_handler_1.default)(async (req, res, next) =>
|
|
|
87
91
|
req.pipe(busboy);
|
|
88
92
|
function tryDone() {
|
|
89
93
|
if (savedFiles.length === fileCount) {
|
|
94
|
+
if (fileCount === 0) {
|
|
95
|
+
return next(new exceptions_1.InvalidPayloadException(`No files where included in the body`));
|
|
96
|
+
}
|
|
90
97
|
res.locals.savedFiles = savedFiles;
|
|
91
98
|
return next();
|
|
92
99
|
}
|
|
93
100
|
}
|
|
94
|
-
}
|
|
95
|
-
|
|
101
|
+
};
|
|
102
|
+
exports.multipartHandler = multipartHandler;
|
|
103
|
+
router.post('/', (0, async_handler_1.default)(exports.multipartHandler), (0, async_handler_1.default)(async (req, res, next) => {
|
|
96
104
|
if (req.is('multipart/form-data') === false) {
|
|
97
105
|
throw new exceptions_1.UnsupportedMediaTypeException(`Unsupported Content-Type header`);
|
|
98
106
|
}
|
|
@@ -214,7 +222,7 @@ router.patch('/', (0, validate_batch_1.validateBatch)('update'), (0, async_handl
|
|
|
214
222
|
}
|
|
215
223
|
return next();
|
|
216
224
|
}), respond_1.respond);
|
|
217
|
-
router.patch('/:pk', multipartHandler, (0, async_handler_1.default)(async (req, res, next) => {
|
|
225
|
+
router.patch('/:pk', (0, async_handler_1.default)(exports.multipartHandler), (0, async_handler_1.default)(async (req, res, next) => {
|
|
218
226
|
const service = new services_1.FilesService({
|
|
219
227
|
accountability: req.accountability,
|
|
220
228
|
schema: req.schema,
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DateHelperMSSQL = void 0;
|
|
4
|
+
const types_1 = require("../types");
|
|
5
|
+
const date_fns_1 = require("date-fns");
|
|
6
|
+
class DateHelperMSSQL extends types_1.DateHelper {
|
|
7
|
+
writeTimestamp(date) {
|
|
8
|
+
const parsedDate = (0, date_fns_1.parseISO)(date);
|
|
9
|
+
return new Date(parsedDate.getTime() + parsedDate.getTimezoneOffset() * 60000);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
exports.DateHelperMSSQL = DateHelperMSSQL;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DateHelperMySQL = void 0;
|
|
4
|
+
const types_1 = require("../types");
|
|
5
|
+
const date_fns_1 = require("date-fns");
|
|
6
|
+
class DateHelperMySQL extends types_1.DateHelper {
|
|
7
|
+
readTimestampString(date) {
|
|
8
|
+
const parsedDate = new Date(date);
|
|
9
|
+
return new Date(parsedDate.getTime() - parsedDate.getTimezoneOffset() * 60000).toISOString();
|
|
10
|
+
}
|
|
11
|
+
writeTimestamp(date) {
|
|
12
|
+
const parsedDate = (0, date_fns_1.parseISO)(date);
|
|
13
|
+
return new Date(parsedDate.getTime() + parsedDate.getTimezoneOffset() * 60000);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
exports.DateHelperMySQL = DateHelperMySQL;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DateHelperOracle = void 0;
|
|
4
|
+
const types_1 = require("../types");
|
|
5
|
+
class DateHelperOracle extends types_1.DateHelper {
|
|
6
|
+
fieldFlagForField(fieldType) {
|
|
7
|
+
switch (fieldType) {
|
|
8
|
+
case 'dateTime':
|
|
9
|
+
return 'cast-datetime';
|
|
10
|
+
default:
|
|
11
|
+
return '';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
exports.DateHelperOracle = DateHelperOracle;
|
|
@@ -7,5 +7,13 @@ class DateHelperSQLite extends types_1.DateHelper {
|
|
|
7
7
|
const newDate = new Date(date);
|
|
8
8
|
return (newDate.getTime() - newDate.getTimezoneOffset() * 60 * 1000).toString();
|
|
9
9
|
}
|
|
10
|
+
fieldFlagForField(fieldType) {
|
|
11
|
+
switch (fieldType) {
|
|
12
|
+
case 'timestamp':
|
|
13
|
+
return 'cast-timestamp';
|
|
14
|
+
default:
|
|
15
|
+
return '';
|
|
16
|
+
}
|
|
17
|
+
}
|
|
10
18
|
}
|
|
11
19
|
exports.DateHelperSQLite = DateHelperSQLite;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { DateHelperDefault as postgres } from './dialects/default';
|
|
2
2
|
export { DateHelperDefault as redshift } from './dialects/default';
|
|
3
3
|
export { DateHelperDefault as cockroachdb } from './dialects/default';
|
|
4
|
-
export {
|
|
5
|
-
export {
|
|
6
|
-
export {
|
|
4
|
+
export { DateHelperOracle as oracle } from './dialects/oracle';
|
|
5
|
+
export { DateHelperMySQL as mysql } from './dialects/mysql';
|
|
6
|
+
export { DateHelperMSSQL as mssql } from './dialects/mssql';
|
|
7
7
|
export { DateHelperSQLite as sqlite } from './dialects/sqlite';
|
|
@@ -7,11 +7,11 @@ var default_2 = require("./dialects/default");
|
|
|
7
7
|
Object.defineProperty(exports, "redshift", { enumerable: true, get: function () { return default_2.DateHelperDefault; } });
|
|
8
8
|
var default_3 = require("./dialects/default");
|
|
9
9
|
Object.defineProperty(exports, "cockroachdb", { enumerable: true, get: function () { return default_3.DateHelperDefault; } });
|
|
10
|
-
var
|
|
11
|
-
Object.defineProperty(exports, "oracle", { enumerable: true, get: function () { return
|
|
12
|
-
var
|
|
13
|
-
Object.defineProperty(exports, "mysql", { enumerable: true, get: function () { return
|
|
14
|
-
var
|
|
15
|
-
Object.defineProperty(exports, "mssql", { enumerable: true, get: function () { return
|
|
10
|
+
var oracle_1 = require("./dialects/oracle");
|
|
11
|
+
Object.defineProperty(exports, "oracle", { enumerable: true, get: function () { return oracle_1.DateHelperOracle; } });
|
|
12
|
+
var mysql_1 = require("./dialects/mysql");
|
|
13
|
+
Object.defineProperty(exports, "mysql", { enumerable: true, get: function () { return mysql_1.DateHelperMySQL; } });
|
|
14
|
+
var mssql_1 = require("./dialects/mssql");
|
|
15
|
+
Object.defineProperty(exports, "mssql", { enumerable: true, get: function () { return mssql_1.DateHelperMSSQL; } });
|
|
16
16
|
var sqlite_1 = require("./dialects/sqlite");
|
|
17
17
|
Object.defineProperty(exports, "sqlite", { enumerable: true, get: function () { return sqlite_1.DateHelperSQLite; } });
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { DatabaseHelper } from '../types';
|
|
2
2
|
export declare abstract class DateHelper extends DatabaseHelper {
|
|
3
3
|
parse(date: string): string;
|
|
4
|
+
readTimestampString(date: string): string;
|
|
5
|
+
writeTimestamp(date: string): Date;
|
|
6
|
+
fieldFlagForField(_fieldType: string): string;
|
|
4
7
|
}
|
|
@@ -2,9 +2,19 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.DateHelper = void 0;
|
|
4
4
|
const types_1 = require("../types");
|
|
5
|
+
const date_fns_1 = require("date-fns");
|
|
5
6
|
class DateHelper extends types_1.DatabaseHelper {
|
|
6
7
|
parse(date) {
|
|
7
8
|
return date;
|
|
8
9
|
}
|
|
10
|
+
readTimestampString(date) {
|
|
11
|
+
return date;
|
|
12
|
+
}
|
|
13
|
+
writeTimestamp(date) {
|
|
14
|
+
return (0, date_fns_1.parseISO)(date);
|
|
15
|
+
}
|
|
16
|
+
fieldFlagForField(_fieldType) {
|
|
17
|
+
return '';
|
|
18
|
+
}
|
|
9
19
|
}
|
|
10
20
|
exports.DateHelper = DateHelper;
|
|
@@ -31,7 +31,11 @@ class FnHelperPostgres extends types_1.FnHelper {
|
|
|
31
31
|
var _a, _b, _c, _d, _e;
|
|
32
32
|
const type = (_e = (_d = (_c = (_b = (_a = this.schema.collections) === null || _a === void 0 ? void 0 : _a[table]) === null || _b === void 0 ? void 0 : _b.fields) === null || _c === void 0 ? void 0 : _c[column]) === null || _d === void 0 ? void 0 : _d.type) !== null && _e !== void 0 ? _e : 'unknown';
|
|
33
33
|
if (type === 'json') {
|
|
34
|
-
|
|
34
|
+
const { dbType } = this.schema.collections[table].fields[column];
|
|
35
|
+
return this.knex.raw(dbType === 'jsonb' ? 'jsonb_array_length(??.??)' : 'json_array_length(??.??)', [
|
|
36
|
+
table,
|
|
37
|
+
column,
|
|
38
|
+
]);
|
|
35
39
|
}
|
|
36
40
|
if (type === 'alias') {
|
|
37
41
|
return this._relationalCount(table, column);
|
|
@@ -5,7 +5,7 @@ import * as fnHelpers from './fn';
|
|
|
5
5
|
import * as geometryHelpers from './geometry';
|
|
6
6
|
import * as schemaHelpers from './schema';
|
|
7
7
|
export declare function getHelpers(database: Knex): {
|
|
8
|
-
date: dateHelpers.postgres | dateHelpers.sqlite;
|
|
8
|
+
date: dateHelpers.postgres | dateHelpers.oracle | dateHelpers.mysql | dateHelpers.mssql | dateHelpers.sqlite;
|
|
9
9
|
st: geometryHelpers.postgres | geometryHelpers.redshift | geometryHelpers.oracle | geometryHelpers.sqlite | geometryHelpers.mysql | geometryHelpers.mssql;
|
|
10
10
|
schema: schemaHelpers.postgres | schemaHelpers.cockroachdb | schemaHelpers.oracle;
|
|
11
11
|
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.down = exports.up = void 0;
|
|
4
|
+
const helpers_1 = require("../helpers");
|
|
5
|
+
async function up(knex) {
|
|
6
|
+
const helper = (0, helpers_1.getHelpers)(knex).schema;
|
|
7
|
+
await helper.changeToString('directus_panels', 'icon', {
|
|
8
|
+
nullable: true,
|
|
9
|
+
default: null,
|
|
10
|
+
length: 30,
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
exports.up = up;
|
|
14
|
+
async function down(knex) {
|
|
15
|
+
const helper = (0, helpers_1.getHelpers)(knex).schema;
|
|
16
|
+
await helper.changeToString('directus_panels', 'icon', {
|
|
17
|
+
nullable: true,
|
|
18
|
+
default: 'insert_chart',
|
|
19
|
+
length: 30,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
exports.down = down;
|
package/dist/database/run-ast.js
CHANGED
|
@@ -107,7 +107,7 @@ async function parseCurrentLevel(schema, collection, children, query) {
|
|
|
107
107
|
if (!child.relation)
|
|
108
108
|
continue;
|
|
109
109
|
if (child.type === 'm2o') {
|
|
110
|
-
columnsToSelectInternal.push(child.
|
|
110
|
+
columnsToSelectInternal.push(child.relation.field);
|
|
111
111
|
}
|
|
112
112
|
if (child.type === 'a2o') {
|
|
113
113
|
columnsToSelectInternal.push(child.relation.field);
|
|
@@ -126,7 +126,7 @@ async function parseCurrentLevel(schema, collection, children, query) {
|
|
|
126
126
|
const columnsToSelect = [...new Set(columnsToSelectInternal)];
|
|
127
127
|
const fieldNodes = columnsToSelect.map((column) => {
|
|
128
128
|
var _a;
|
|
129
|
-
return (_a = children.find((childNode) => childNode.type === 'field' && childNode.fieldKey === column)) !== null && _a !== void 0 ? _a : {
|
|
129
|
+
return (_a = children.find((childNode) => childNode.type === 'field' && (childNode.fieldKey === column || childNode.name === column))) !== null && _a !== void 0 ? _a : {
|
|
130
130
|
type: 'field',
|
|
131
131
|
name: column,
|
|
132
132
|
fieldKey: column,
|
|
@@ -221,7 +221,7 @@ function mergeWithParentItems(schema, nestedItem, parentItem, nestedNode) {
|
|
|
221
221
|
for (const parentItem of parentItems) {
|
|
222
222
|
const itemChild = nestedItems.find((nestedItem) => {
|
|
223
223
|
return (nestedItem[schema.collections[nestedNode.relation.related_collection].primary] ==
|
|
224
|
-
parentItem[nestedNode.
|
|
224
|
+
parentItem[nestedNode.relation.field]);
|
|
225
225
|
});
|
|
226
226
|
parentItem[nestedNode.fieldKey] = itemChild || null;
|
|
227
227
|
}
|
|
@@ -72,6 +72,8 @@ fields:
|
|
|
72
72
|
- field: language
|
|
73
73
|
interface: system-language
|
|
74
74
|
width: half
|
|
75
|
+
options:
|
|
76
|
+
includeProjectDefault: true
|
|
75
77
|
|
|
76
78
|
- field: theme
|
|
77
79
|
interface: select-dropdown
|
|
@@ -152,6 +154,7 @@ fields:
|
|
|
152
154
|
- field: last_access
|
|
153
155
|
width: half
|
|
154
156
|
display: datetime
|
|
157
|
+
readonly: true
|
|
155
158
|
display_options:
|
|
156
159
|
relative: true
|
|
157
160
|
|
package/dist/env.js
CHANGED
|
@@ -14,7 +14,7 @@ const lodash_1 = require("lodash");
|
|
|
14
14
|
const path_1 = __importDefault(require("path"));
|
|
15
15
|
const require_yaml_1 = require("./utils/require-yaml");
|
|
16
16
|
const utils_1 = require("@directus/shared/utils");
|
|
17
|
-
const acceptedEnvTypes = ['string', 'number', 'regex', 'array'];
|
|
17
|
+
const acceptedEnvTypes = ['string', 'number', 'regex', 'array', 'json'];
|
|
18
18
|
const defaults = {
|
|
19
19
|
CONFIG_PATH: path_1.default.resolve(process.cwd(), '.env'),
|
|
20
20
|
HOST: '0.0.0.0',
|
|
@@ -69,6 +69,8 @@ const defaults = {
|
|
|
69
69
|
SERVE_APP: true,
|
|
70
70
|
RELATIONAL_BATCH_SIZE: 25000,
|
|
71
71
|
EXPORT_BATCH_SIZE: 5000,
|
|
72
|
+
FILE_METADATA_ALLOW_LIST: 'ifd0.Make,ifd0.Model,exif.FNumber,exif.ExposureTime,exif.FocalLength,exif.ISO',
|
|
73
|
+
GRAPHQL_INTROSPECTION: true,
|
|
72
74
|
};
|
|
73
75
|
// Allows us to force certain environment variable into a type, instead of relying
|
|
74
76
|
// on the auto-parsed type in processValues. ref #3705
|
|
@@ -82,11 +84,13 @@ const typeMap = {
|
|
|
82
84
|
DB_PORT: 'number',
|
|
83
85
|
DB_EXCLUDE_TABLES: 'array',
|
|
84
86
|
IMPORT_IP_DENY_LIST: 'array',
|
|
87
|
+
FILE_METADATA_ALLOW_LIST: 'array',
|
|
88
|
+
GRAPHQL_INTROSPECTION: 'boolean',
|
|
85
89
|
};
|
|
86
90
|
let env = {
|
|
87
91
|
...defaults,
|
|
88
|
-
...getEnv(),
|
|
89
92
|
...process.env,
|
|
93
|
+
...getEnv(),
|
|
90
94
|
};
|
|
91
95
|
process.env = env;
|
|
92
96
|
env = processValues(env);
|
|
@@ -98,8 +102,8 @@ exports.default = env;
|
|
|
98
102
|
function refreshEnv() {
|
|
99
103
|
env = {
|
|
100
104
|
...defaults,
|
|
101
|
-
...getEnv(),
|
|
102
105
|
...process.env,
|
|
106
|
+
...getEnv(),
|
|
103
107
|
};
|
|
104
108
|
process.env = env;
|
|
105
109
|
env = processValues(env);
|
|
@@ -207,6 +211,8 @@ function processValues(env) {
|
|
|
207
211
|
case 'json':
|
|
208
212
|
env[key] = tryJSON(value);
|
|
209
213
|
break;
|
|
214
|
+
case 'boolean':
|
|
215
|
+
env[key] = value === 'true' || value === true || value === '1' || value === 1;
|
|
210
216
|
}
|
|
211
217
|
continue;
|
|
212
218
|
}
|
|
@@ -13,6 +13,7 @@ export * from './method-not-allowed';
|
|
|
13
13
|
export * from './range-not-satisfiable';
|
|
14
14
|
export * from './route-not-found';
|
|
15
15
|
export * from './service-unavailable';
|
|
16
|
+
export * from './token-expired';
|
|
16
17
|
export * from './unprocessable-entity';
|
|
17
18
|
export * from './unsupported-media-type';
|
|
18
19
|
export * from './user-suspended';
|