directus 9.21.2 → 9.22.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/app.js +9 -5
- package/dist/app.test.d.ts +1 -0
- package/dist/cli/commands/bootstrap/index.js +2 -2
- package/dist/cli/commands/security/secret.js +2 -2
- package/dist/cli/utils/create-env/env-stub.liquid +3 -3
- package/dist/cli/utils/create-env/index.js +5 -8
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +2 -1
- package/dist/controllers/assets.js +9 -7
- package/dist/controllers/files.js +2 -1
- package/dist/controllers/utils.js +2 -2
- package/dist/database/helpers/fn/dialects/mssql.js +2 -1
- package/dist/database/helpers/fn/dialects/mysql.js +2 -1
- package/dist/database/helpers/fn/dialects/oracle.js +2 -1
- package/dist/database/helpers/fn/dialects/postgres.js +2 -1
- package/dist/database/helpers/fn/dialects/sqlite.js +2 -1
- package/dist/database/helpers/fn/types.d.ts +1 -0
- package/dist/database/helpers/fn/types.js +5 -4
- package/dist/database/helpers/index.d.ts +1 -1
- package/dist/database/helpers/schema/dialects/mssql.d.ts +6 -0
- package/dist/database/helpers/schema/dialects/mssql.js +14 -0
- package/dist/database/helpers/schema/dialects/mysql.d.ts +5 -0
- package/dist/database/helpers/schema/dialects/mysql.js +19 -0
- package/dist/database/helpers/schema/dialects/oracle.d.ts +1 -0
- package/dist/database/helpers/schema/dialects/oracle.js +3 -0
- package/dist/database/helpers/schema/index.d.ts +2 -2
- package/dist/database/helpers/schema/index.js +4 -4
- package/dist/database/helpers/schema/types.d.ts +5 -0
- package/dist/database/helpers/schema/types.js +13 -0
- package/dist/database/index.d.ts +6 -0
- package/dist/database/index.js +20 -1
- package/dist/database/migrations/20211007A-update-presets.js +2 -2
- package/dist/database/migrations/run.js +7 -31
- package/dist/database/run-ast.js +132 -6
- package/dist/database/system-data/fields/index.js +2 -1
- package/dist/env.js +3 -2
- package/dist/exceptions/range-not-satisfiable.d.ts +1 -1
- package/dist/extensions.d.ts +7 -1
- package/dist/extensions.js +41 -15
- package/dist/logger.js +27 -1
- package/dist/operations/request/index.d.ts +1 -2
- package/dist/operations/request/index.js +2 -2
- package/dist/services/assets.d.ts +4 -3
- package/dist/services/assets.js +13 -11
- package/dist/services/authentication.js +4 -3
- package/dist/services/authorization.js +1 -1
- package/dist/services/files.d.ts +4 -3
- package/dist/services/files.js +92 -68
- package/dist/services/flows.d.ts +0 -2
- package/dist/services/flows.js +0 -14
- package/dist/services/flows.test.d.ts +1 -0
- package/dist/services/graphql/index.d.ts +5 -1
- package/dist/services/graphql/index.js +29 -31
- package/dist/services/import-export.d.ts +4 -3
- package/dist/services/items.js +7 -1
- package/dist/services/meta.js +2 -2
- package/dist/services/operations.d.ts +0 -2
- package/dist/services/operations.js +0 -12
- package/dist/services/operations.test.d.ts +1 -0
- package/dist/services/permissions.d.ts +0 -5
- package/dist/services/permissions.js +0 -25
- package/dist/services/permissions.test.d.ts +1 -0
- package/dist/services/roles.js +0 -3
- package/dist/services/roles.test.d.ts +1 -0
- package/dist/services/server.js +8 -6
- package/dist/services/shares.js +2 -2
- package/dist/services/specifications.js +12 -1
- package/dist/services/webhooks.d.ts +0 -2
- package/dist/services/webhooks.js +0 -10
- package/dist/services/webhooks.test.d.ts +1 -0
- package/dist/storage/get-storage-driver.d.ts +3 -0
- package/dist/storage/get-storage-driver.js +20 -0
- package/dist/storage/get-storage-driver.test.d.ts +1 -0
- package/dist/storage/index.d.ts +5 -0
- package/dist/storage/index.js +20 -0
- package/dist/storage/index.test.d.ts +1 -0
- package/dist/storage/register-drivers.d.ts +2 -0
- package/dist/storage/register-drivers.js +22 -0
- package/dist/storage/register-drivers.test.d.ts +1 -0
- package/dist/storage/register-locations.d.ts +2 -0
- package/dist/storage/register-locations.js +17 -0
- package/dist/storage/register-locations.test.d.ts +1 -0
- package/dist/utils/apply-query.d.ts +27 -3
- package/dist/utils/apply-query.js +180 -127
- package/dist/utils/dynamic-import.d.ts +1 -0
- package/dist/utils/dynamic-import.js +7 -0
- package/dist/utils/get-collection-from-alias.d.ts +6 -0
- package/dist/utils/get-collection-from-alias.js +15 -0
- package/dist/utils/get-collection-from-alias.test.d.ts +1 -0
- package/dist/utils/get-column-path.d.ts +14 -8
- package/dist/utils/get-column-path.js +24 -7
- package/dist/utils/get-column.d.ts +8 -1
- package/dist/utils/get-column.js +10 -3
- package/dist/utils/get-config-from-env.js +3 -2
- package/dist/utils/get-default-value.d.ts +1 -1
- package/dist/utils/parse-image-metadata.d.ts +3 -0
- package/dist/utils/parse-image-metadata.js +73 -0
- package/dist/utils/track.js +2 -2
- package/dist/utils/validate-env.js +3 -2
- package/dist/webhooks.js +2 -2
- package/package.json +17 -11
- package/dist/storage.d.ts +0 -3
- package/dist/storage.js +0 -61
package/dist/app.js
CHANGED
|
@@ -31,7 +31,6 @@ const express_1 = __importDefault(require("express"));
|
|
|
31
31
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
32
32
|
const path_1 = __importDefault(require("path"));
|
|
33
33
|
const qs_1 = __importDefault(require("qs"));
|
|
34
|
-
const helmet_1 = __importDefault(require("helmet"));
|
|
35
34
|
const activity_1 = __importDefault(require("./controllers/activity"));
|
|
36
35
|
const assets_1 = __importDefault(require("./controllers/assets"));
|
|
37
36
|
const auth_1 = __importDefault(require("./controllers/auth"));
|
|
@@ -87,6 +86,7 @@ const url_1 = require("./utils/url");
|
|
|
87
86
|
const get_config_from_env_1 = require("./utils/get-config-from-env");
|
|
88
87
|
const lodash_1 = require("lodash");
|
|
89
88
|
async function createApp() {
|
|
89
|
+
const helmet = await import('helmet');
|
|
90
90
|
(0, validate_env_1.validateEnv)(['KEY', 'SECRET']);
|
|
91
91
|
if (!new url_1.Url(env_1.default.PUBLIC_URL).isAbsolute()) {
|
|
92
92
|
logger_1.default.warn('PUBLIC_URL should be a full URL');
|
|
@@ -111,7 +111,7 @@ async function createApp() {
|
|
|
111
111
|
app.disable('x-powered-by');
|
|
112
112
|
app.set('trust proxy', env_1.default.IP_TRUST_PROXY);
|
|
113
113
|
app.set('query parser', (str) => qs_1.default.parse(str, { depth: 10 }));
|
|
114
|
-
app.use(
|
|
114
|
+
app.use(helmet.contentSecurityPolicy((0, lodash_1.merge)({
|
|
115
115
|
useDefaults: true,
|
|
116
116
|
directives: {
|
|
117
117
|
// Unsafe-eval is required for vue3 / vue-i18n / app extensions
|
|
@@ -130,7 +130,7 @@ async function createApp() {
|
|
|
130
130
|
},
|
|
131
131
|
}, (0, get_config_from_env_1.getConfigFromEnv)('CONTENT_SECURITY_POLICY_'))));
|
|
132
132
|
if (env_1.default.HSTS_ENABLED) {
|
|
133
|
-
app.use(
|
|
133
|
+
app.use(helmet.hsts((0, get_config_from_env_1.getConfigFromEnv)('HSTS_', ['HSTS_ENABLED'])));
|
|
134
134
|
}
|
|
135
135
|
await emitter_1.default.emitInit('app.before', { app });
|
|
136
136
|
await emitter_1.default.emitInit('middlewares.before', { app });
|
|
@@ -170,13 +170,17 @@ async function createApp() {
|
|
|
170
170
|
if (env_1.default.SERVE_APP) {
|
|
171
171
|
const adminPath = require.resolve('@directus/app');
|
|
172
172
|
const adminUrl = new url_1.Url(env_1.default.PUBLIC_URL).addPath('admin');
|
|
173
|
+
const embeds = extensionManager.getEmbeds();
|
|
173
174
|
// Set the App's base path according to the APIs public URL
|
|
174
175
|
const html = await fs_extra_1.default.readFile(adminPath, 'utf8');
|
|
175
|
-
const
|
|
176
|
+
const htmlWithVars = html
|
|
177
|
+
.replace(/<base \/>/, `<base href="${adminUrl.toString({ rootRelative: true })}/" />`)
|
|
178
|
+
.replace(/<embed-head \/>/, embeds.head)
|
|
179
|
+
.replace(/<embed-body \/>/, embeds.body);
|
|
176
180
|
const sendHtml = (_req, res) => {
|
|
177
181
|
res.setHeader('Cache-Control', 'no-cache');
|
|
178
182
|
res.setHeader('Vary', 'Origin, Cache-Control');
|
|
179
|
-
res.send(
|
|
183
|
+
res.send(htmlWithVars);
|
|
180
184
|
};
|
|
181
185
|
const setStaticHeaders = (res) => {
|
|
182
186
|
res.setHeader('Cache-Control', 'max-age=31536000, immutable');
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -26,7 +26,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
26
26
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
27
|
};
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
-
const nanoid_1 = require("nanoid");
|
|
30
29
|
const run_1 = __importDefault(require("../../../database/migrations/run"));
|
|
31
30
|
const run_2 = __importDefault(require("../../../database/seeds/run"));
|
|
32
31
|
const env_1 = __importDefault(require("../../../env"));
|
|
@@ -78,6 +77,7 @@ async function waitForDatabase(database) {
|
|
|
78
77
|
await (0, database_1.validateDatabaseConnection)(database);
|
|
79
78
|
}
|
|
80
79
|
async function createDefaultAdmin(schema) {
|
|
80
|
+
const { nanoid } = await import('nanoid');
|
|
81
81
|
logger_1.default.info('Setting up first admin role...');
|
|
82
82
|
const rolesService = new services_1.RolesService({ schema });
|
|
83
83
|
const role = await rolesService.createOne(defaults_1.defaultAdminRole);
|
|
@@ -90,7 +90,7 @@ async function createDefaultAdmin(schema) {
|
|
|
90
90
|
}
|
|
91
91
|
let adminPassword = env_1.default.ADMIN_PASSWORD;
|
|
92
92
|
if (!adminPassword) {
|
|
93
|
-
adminPassword =
|
|
93
|
+
adminPassword = nanoid(12);
|
|
94
94
|
logger_1.default.info(`No admin password provided. Defaulting to "${adminPassword}"`);
|
|
95
95
|
}
|
|
96
96
|
await usersService.createOne({ email: adminEmail, password: adminPassword, role, ...defaults_1.defaultAdminUser });
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const nanoid_1 = require("nanoid");
|
|
4
3
|
async function generateSecret() {
|
|
5
|
-
|
|
4
|
+
const { nanoid } = await import('nanoid');
|
|
5
|
+
process.stdout.write(nanoid(32));
|
|
6
6
|
process.exit(0);
|
|
7
7
|
}
|
|
8
8
|
exports.default = generateSecret;
|
|
@@ -149,8 +149,8 @@ CACHE_ENABLED=false
|
|
|
149
149
|
# memory | redis | memcache
|
|
150
150
|
CACHE_STORE=memory
|
|
151
151
|
|
|
152
|
-
# How long assets will be cached for in the browser. Sets the max-age value of the Cache-Control header ["
|
|
153
|
-
ASSETS_CACHE_TTL="
|
|
152
|
+
# How long assets will be cached for in the browser. Sets the max-age value of the Cache-Control header ["30d"]
|
|
153
|
+
ASSETS_CACHE_TTL="30d"
|
|
154
154
|
|
|
155
155
|
# CACHE_REDIS="redis://@127.0.0.1:5105"
|
|
156
156
|
# CACHE_MEMCACHE="localhost:5109"
|
|
@@ -227,7 +227,7 @@ CORS_METHODS=GET,POST,PATCH,DELETE
|
|
|
227
227
|
# Value for the Access-Control-Allow-Headers header [Content-Type,Authorization]
|
|
228
228
|
CORS_ALLOWED_HEADERS=Content-Type,Authorization
|
|
229
229
|
|
|
230
|
-
# Value for the Access-Control-Expose-Headers header [Content-
|
|
230
|
+
# Value for the Access-Control-Expose-Headers header [Content-Range]
|
|
231
231
|
CORS_EXPOSED_HEADERS=Content-Range
|
|
232
232
|
|
|
233
233
|
# Whether or not to send the Access-Control-Allow-Credentials header [true]
|
|
@@ -5,7 +5,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const fs_1 = __importDefault(require("fs"));
|
|
7
7
|
const liquidjs_1 = require("liquidjs");
|
|
8
|
-
const nanoid_1 = require("nanoid");
|
|
9
8
|
const path_1 = __importDefault(require("path"));
|
|
10
9
|
const util_1 = require("util");
|
|
11
10
|
const uuid_1 = require("uuid");
|
|
@@ -16,15 +15,13 @@ const open = (0, util_1.promisify)(fs_1.default.open);
|
|
|
16
15
|
const liquidEngine = new liquidjs_1.Liquid({
|
|
17
16
|
extname: '.liquid',
|
|
18
17
|
});
|
|
19
|
-
const defaults = {
|
|
20
|
-
security: {
|
|
21
|
-
KEY: (0, uuid_1.v4)(),
|
|
22
|
-
SECRET: (0, nanoid_1.nanoid)(32),
|
|
23
|
-
},
|
|
24
|
-
};
|
|
25
18
|
async function createEnv(client, credentials, directory) {
|
|
19
|
+
const { nanoid } = await import('nanoid');
|
|
26
20
|
const config = {
|
|
27
|
-
|
|
21
|
+
security: {
|
|
22
|
+
KEY: (0, uuid_1.v4)(),
|
|
23
|
+
SECRET: nanoid(32),
|
|
24
|
+
},
|
|
28
25
|
database: {
|
|
29
26
|
DB_CLIENT: client,
|
|
30
27
|
},
|
package/dist/constants.d.ts
CHANGED
package/dist/constants.js
CHANGED
|
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
var _a;
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.ROBOTSTXT = exports.COOKIE_OPTIONS = exports.UUID_REGEX = exports.GENERATE_SPECIAL = exports.COLUMN_TRANSFORMS = exports.DEFAULT_AUTH_PROVIDER = exports.ALIAS_TYPES = exports.FILTER_VARIABLES = exports.ASSET_TRANSFORM_QUERY_KEYS = exports.SYSTEM_ASSET_ALLOW_LIST = void 0;
|
|
7
|
+
exports.OAS_REQUIRED_SCHEMAS = exports.ROBOTSTXT = exports.COOKIE_OPTIONS = exports.UUID_REGEX = exports.GENERATE_SPECIAL = exports.COLUMN_TRANSFORMS = exports.DEFAULT_AUTH_PROVIDER = exports.ALIAS_TYPES = exports.FILTER_VARIABLES = exports.ASSET_TRANSFORM_QUERY_KEYS = exports.SYSTEM_ASSET_ALLOW_LIST = void 0;
|
|
8
8
|
const env_1 = __importDefault(require("./env"));
|
|
9
9
|
const ms_1 = __importDefault(require("ms"));
|
|
10
10
|
exports.SYSTEM_ASSET_ALLOW_LIST = [
|
|
@@ -60,3 +60,4 @@ exports.ROBOTSTXT = `
|
|
|
60
60
|
User-agent: *
|
|
61
61
|
Disallow: /
|
|
62
62
|
`.trim();
|
|
63
|
+
exports.OAS_REQUIRED_SCHEMAS = ['Query', 'x-metadata'];
|
|
@@ -5,7 +5,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const utils_1 = require("@directus/shared/utils");
|
|
7
7
|
const express_1 = require("express");
|
|
8
|
-
const helmet_1 = __importDefault(require("helmet"));
|
|
9
8
|
const lodash_1 = require("lodash");
|
|
10
9
|
const ms_1 = __importDefault(require("ms"));
|
|
11
10
|
const constants_1 = require("../constants");
|
|
@@ -92,12 +91,15 @@ router.get('/:pk/:filename?',
|
|
|
92
91
|
return next();
|
|
93
92
|
throw new exceptions_1.InvalidQueryException(`Dynamic asset generation has been disabled for this project.`);
|
|
94
93
|
}
|
|
95
|
-
}),
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
94
|
+
}), (0, async_handler_1.default)(async (req, res, next) => {
|
|
95
|
+
const helmet = await import('helmet');
|
|
96
|
+
return helmet.contentSecurityPolicy((0, lodash_1.merge)({
|
|
97
|
+
useDefaults: false,
|
|
98
|
+
directives: {
|
|
99
|
+
defaultSrc: ['none'],
|
|
100
|
+
},
|
|
101
|
+
}, (0, get_config_from_env_1.getConfigFromEnv)('ASSETS_CONTENT_SECURITY_POLICY')))(req, res, next);
|
|
102
|
+
}),
|
|
101
103
|
// Return file
|
|
102
104
|
(0, async_handler_1.default)(async (req, res) => {
|
|
103
105
|
var _a, _b, _c;
|
|
@@ -4,7 +4,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.multipartHandler = void 0;
|
|
7
|
-
const format_title_1 = __importDefault(require("@directus/format-title"));
|
|
8
7
|
const utils_1 = require("@directus/shared/utils");
|
|
9
8
|
const busboy_1 = __importDefault(require("busboy"));
|
|
10
9
|
const express_1 = __importDefault(require("express"));
|
|
@@ -17,6 +16,8 @@ const use_collection_1 = __importDefault(require("../middleware/use-collection")
|
|
|
17
16
|
const validate_batch_1 = require("../middleware/validate-batch");
|
|
18
17
|
const services_1 = require("../services");
|
|
19
18
|
const async_handler_1 = __importDefault(require("../utils/async-handler"));
|
|
19
|
+
// @ts-ignore
|
|
20
|
+
const format_title_1 = __importDefault(require("@directus/format-title"));
|
|
20
21
|
const router = express_1.default.Router();
|
|
21
22
|
router.use((0, use_collection_1.default)('directus_files'));
|
|
22
23
|
const multipartHandler = (req, res, next) => {
|
|
@@ -6,7 +6,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
const argon2_1 = __importDefault(require("argon2"));
|
|
7
7
|
const express_1 = require("express");
|
|
8
8
|
const joi_1 = __importDefault(require("joi"));
|
|
9
|
-
const nanoid_1 = require("nanoid");
|
|
10
9
|
const exceptions_1 = require("../exceptions");
|
|
11
10
|
const collection_exists_1 = __importDefault(require("../middleware/collection-exists"));
|
|
12
11
|
const respond_1 = require("../middleware/respond");
|
|
@@ -18,9 +17,10 @@ const generate_hash_1 = require("../utils/generate-hash");
|
|
|
18
17
|
const router = (0, express_1.Router)();
|
|
19
18
|
router.get('/random/string', (0, async_handler_1.default)(async (req, res) => {
|
|
20
19
|
var _a;
|
|
20
|
+
const { nanoid } = await import('nanoid');
|
|
21
21
|
if (req.query && req.query.length && Number(req.query.length) > 500)
|
|
22
22
|
throw new exceptions_1.InvalidQueryException(`"length" can't be more than 500 characters`);
|
|
23
|
-
const string =
|
|
23
|
+
const string = nanoid(((_a = req.query) === null || _a === void 0 ? void 0 : _a.length) ? Number(req.query.length) : 32);
|
|
24
24
|
return res.json({ data: string });
|
|
25
25
|
}));
|
|
26
26
|
router.post('/hash/generate', (0, async_handler_1.default)(async (req, res) => {
|
|
@@ -35,7 +35,8 @@ class FnHelperMSSQL extends types_1.FnHelper {
|
|
|
35
35
|
}
|
|
36
36
|
count(table, column, options) {
|
|
37
37
|
var _a, _b, _c, _d, _e;
|
|
38
|
-
const
|
|
38
|
+
const collectionName = (options === null || options === void 0 ? void 0 : options.originalCollectionName) || table;
|
|
39
|
+
const type = (_e = (_d = (_c = (_b = (_a = this.schema.collections) === null || _a === void 0 ? void 0 : _a[collectionName]) === 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';
|
|
39
40
|
if (type === 'json') {
|
|
40
41
|
return this.knex.raw(`(SELECT COUNT(*) FROM OPENJSON(??.??, '$'))`, [table, column]);
|
|
41
42
|
}
|
|
@@ -35,7 +35,8 @@ class FnHelperMySQL extends types_1.FnHelper {
|
|
|
35
35
|
}
|
|
36
36
|
count(table, column, options) {
|
|
37
37
|
var _a, _b, _c, _d, _e;
|
|
38
|
-
const
|
|
38
|
+
const collectionName = (options === null || options === void 0 ? void 0 : options.originalCollectionName) || table;
|
|
39
|
+
const type = (_e = (_d = (_c = (_b = (_a = this.schema.collections) === null || _a === void 0 ? void 0 : _a[collectionName]) === 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';
|
|
39
40
|
if (type === 'json') {
|
|
40
41
|
return this.knex.raw('JSON_LENGTH(??.??)', [table, column]);
|
|
41
42
|
}
|
|
@@ -35,7 +35,8 @@ class FnHelperOracle extends types_1.FnHelper {
|
|
|
35
35
|
}
|
|
36
36
|
count(table, column, options) {
|
|
37
37
|
var _a, _b, _c, _d, _e;
|
|
38
|
-
const
|
|
38
|
+
const collectionName = (options === null || options === void 0 ? void 0 : options.originalCollectionName) || table;
|
|
39
|
+
const type = (_e = (_d = (_c = (_b = (_a = this.schema.collections) === null || _a === void 0 ? void 0 : _a[collectionName]) === 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';
|
|
39
40
|
if (type === 'json') {
|
|
40
41
|
return this.knex.raw("json_value(??.??, '$.size()')", [table, column]);
|
|
41
42
|
}
|
|
@@ -35,7 +35,8 @@ class FnHelperPostgres extends types_1.FnHelper {
|
|
|
35
35
|
}
|
|
36
36
|
count(table, column, options) {
|
|
37
37
|
var _a, _b, _c, _d, _e;
|
|
38
|
-
const
|
|
38
|
+
const collectionName = (options === null || options === void 0 ? void 0 : options.originalCollectionName) || table;
|
|
39
|
+
const type = (_e = (_d = (_c = (_b = (_a = this.schema.collections) === null || _a === void 0 ? void 0 : _a[collectionName]) === 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';
|
|
39
40
|
if (type === 'json') {
|
|
40
41
|
const { dbType } = this.schema.collections[table].fields[column];
|
|
41
42
|
return this.knex.raw(dbType === 'jsonb' ? 'jsonb_array_length(??.??)' : 'json_array_length(??.??)', [
|
|
@@ -59,7 +59,8 @@ class FnHelperSQLite extends types_1.FnHelper {
|
|
|
59
59
|
}
|
|
60
60
|
count(table, column, options) {
|
|
61
61
|
var _a, _b, _c, _d, _e;
|
|
62
|
-
const
|
|
62
|
+
const collectionName = (options === null || options === void 0 ? void 0 : options.originalCollectionName) || table;
|
|
63
|
+
const type = (_e = (_d = (_c = (_b = (_a = this.schema.collections) === null || _a === void 0 ? void 0 : _a[collectionName]) === 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';
|
|
63
64
|
if (type === 'json') {
|
|
64
65
|
return this.knex.raw(`json_array_length(??.??, '$')`, [table, column]);
|
|
65
66
|
}
|
|
@@ -12,17 +12,18 @@ class FnHelper extends types_1.DatabaseHelper {
|
|
|
12
12
|
}
|
|
13
13
|
_relationalCount(table, column, options) {
|
|
14
14
|
var _a;
|
|
15
|
-
const
|
|
16
|
-
const
|
|
15
|
+
const collectionName = (options === null || options === void 0 ? void 0 : options.originalCollectionName) || table;
|
|
16
|
+
const relation = this.schema.relations.find((relation) => { var _a; return relation.related_collection === collectionName && ((_a = relation === null || relation === void 0 ? void 0 : relation.meta) === null || _a === void 0 ? void 0 : _a.one_field) === column; });
|
|
17
|
+
const currentPrimary = this.schema.collections[collectionName].primary;
|
|
17
18
|
if (!relation) {
|
|
18
|
-
throw new Error(`Field ${
|
|
19
|
+
throw new Error(`Field ${collectionName}.${column} isn't a nested relational collection`);
|
|
19
20
|
}
|
|
20
21
|
let countQuery = this.knex
|
|
21
22
|
.count('*')
|
|
22
23
|
.from(relation.collection)
|
|
23
24
|
.where(relation.field, '=', this.knex.raw(`??.??`, [table, currentPrimary]));
|
|
24
25
|
if ((_a = options === null || options === void 0 ? void 0 : options.query) === null || _a === void 0 ? void 0 : _a.filter) {
|
|
25
|
-
countQuery = (0, apply_query_1.applyFilter)(this.knex, this.schema, countQuery, options.query.filter, relation.collection,
|
|
26
|
+
countQuery = (0, apply_query_1.applyFilter)(this.knex, this.schema, countQuery, options.query.filter, relation.collection, {}).query;
|
|
26
27
|
}
|
|
27
28
|
return this.knex.raw('(' + countQuery.toQuery() + ')');
|
|
28
29
|
}
|
|
@@ -7,7 +7,7 @@ import * as schemaHelpers from './schema';
|
|
|
7
7
|
export declare function getHelpers(database: Knex): {
|
|
8
8
|
date: dateHelpers.postgres | dateHelpers.oracle | dateHelpers.mysql | dateHelpers.mssql | dateHelpers.sqlite;
|
|
9
9
|
st: geometryHelpers.sqlite | geometryHelpers.postgres | geometryHelpers.mysql | geometryHelpers.oracle | geometryHelpers.mssql | geometryHelpers.redshift;
|
|
10
|
-
schema: schemaHelpers.sqlite | schemaHelpers.postgres | schemaHelpers.cockroachdb | schemaHelpers.oracle;
|
|
10
|
+
schema: schemaHelpers.sqlite | schemaHelpers.postgres | schemaHelpers.mysql | schemaHelpers.cockroachdb | schemaHelpers.oracle | schemaHelpers.mssql;
|
|
11
11
|
};
|
|
12
12
|
export declare function getFunctions(database: Knex, schema: SchemaOverview): fnHelpers.sqlite | fnHelpers.postgres | fnHelpers.mysql | fnHelpers.oracle | fnHelpers.mssql;
|
|
13
13
|
export type Helpers = ReturnType<typeof getHelpers>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SchemaHelperMSSQL = void 0;
|
|
4
|
+
const types_1 = require("../types");
|
|
5
|
+
class SchemaHelperMSSQL extends types_1.SchemaHelper {
|
|
6
|
+
applyOffset(rootQuery, offset) {
|
|
7
|
+
rootQuery.offset(offset);
|
|
8
|
+
rootQuery.orderBy(1);
|
|
9
|
+
}
|
|
10
|
+
formatUUID(uuid) {
|
|
11
|
+
return uuid.toUpperCase();
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
exports.SchemaHelperMSSQL = SchemaHelperMSSQL;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Knex } from 'knex';
|
|
2
|
+
import { SchemaHelper } from '../types';
|
|
3
|
+
export declare class SchemaHelperMySQL extends SchemaHelper {
|
|
4
|
+
applyMultiRelationalSort(knex: Knex, dbQuery: Knex.QueryBuilder, table: string, primaryKey: string, orderByString: string, orderByFields: Knex.Raw[]): Knex.QueryBuilder;
|
|
5
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SchemaHelperMySQL = void 0;
|
|
4
|
+
const database_1 = require("../../../../database");
|
|
5
|
+
const types_1 = require("../types");
|
|
6
|
+
class SchemaHelperMySQL extends types_1.SchemaHelper {
|
|
7
|
+
applyMultiRelationalSort(knex, dbQuery, table, primaryKey, orderByString, orderByFields) {
|
|
8
|
+
var _a;
|
|
9
|
+
if ((_a = (0, database_1.getDatabaseVersion)()) === null || _a === void 0 ? void 0 : _a.startsWith('5.7')) {
|
|
10
|
+
dbQuery.orderByRaw(`?? asc, ${orderByString.slice(9)}`, [`${table}.${primaryKey}`, ...orderByFields]);
|
|
11
|
+
dbQuery = knex
|
|
12
|
+
.select(knex.raw(`??, ( @rank := IF ( @cur_id = deep.${primaryKey}, @rank + 1, 1 ) ) AS directus_row_number, ( @cur_id := deep.${primaryKey} ) AS current_id`, 'deep.*'))
|
|
13
|
+
.from(knex.raw('? as ??, (SELECT @rank := 0, @cur_id := null) vars', [dbQuery, 'deep']));
|
|
14
|
+
return dbQuery;
|
|
15
|
+
}
|
|
16
|
+
return super.applyMultiRelationalSort(knex, dbQuery, table, primaryKey, orderByString, orderByFields);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
exports.SchemaHelperMySQL = SchemaHelperMySQL;
|
|
@@ -2,4 +2,5 @@ import { KNEX_TYPES } from '@directus/shared/constants';
|
|
|
2
2
|
import { Options, SchemaHelper } from '../types';
|
|
3
3
|
export declare class SchemaHelperOracle extends SchemaHelper {
|
|
4
4
|
changeToType(table: string, column: string, type: typeof KNEX_TYPES[number], options?: Options): Promise<void>;
|
|
5
|
+
castA2oPrimaryKey(): string;
|
|
5
6
|
}
|
|
@@ -6,5 +6,8 @@ class SchemaHelperOracle extends types_1.SchemaHelper {
|
|
|
6
6
|
async changeToType(table, column, type, options = {}) {
|
|
7
7
|
await this.changeToTypeByCopy(table, column, type, options);
|
|
8
8
|
}
|
|
9
|
+
castA2oPrimaryKey() {
|
|
10
|
+
return 'CAST(?? AS VARCHAR2(255))';
|
|
11
|
+
}
|
|
9
12
|
}
|
|
10
13
|
exports.SchemaHelperOracle = SchemaHelperOracle;
|
|
@@ -3,5 +3,5 @@ export { SchemaHelperCockroachDb as cockroachdb } from './dialects/cockroachdb';
|
|
|
3
3
|
export { SchemaHelperDefault as redshift } from './dialects/default';
|
|
4
4
|
export { SchemaHelperOracle as oracle } from './dialects/oracle';
|
|
5
5
|
export { SchemaHelperSQLite as sqlite } from './dialects/sqlite';
|
|
6
|
-
export {
|
|
7
|
-
export {
|
|
6
|
+
export { SchemaHelperMySQL as mysql } from './dialects/mysql';
|
|
7
|
+
export { SchemaHelperMSSQL as mssql } from './dialects/mssql';
|
|
@@ -11,7 +11,7 @@ var oracle_1 = require("./dialects/oracle");
|
|
|
11
11
|
Object.defineProperty(exports, "oracle", { enumerable: true, get: function () { return oracle_1.SchemaHelperOracle; } });
|
|
12
12
|
var sqlite_1 = require("./dialects/sqlite");
|
|
13
13
|
Object.defineProperty(exports, "sqlite", { enumerable: true, get: function () { return sqlite_1.SchemaHelperSQLite; } });
|
|
14
|
-
var
|
|
15
|
-
Object.defineProperty(exports, "mysql", { enumerable: true, get: function () { return
|
|
16
|
-
var
|
|
17
|
-
Object.defineProperty(exports, "mssql", { enumerable: true, get: function () { return
|
|
14
|
+
var mysql_1 = require("./dialects/mysql");
|
|
15
|
+
Object.defineProperty(exports, "mysql", { enumerable: true, get: function () { return mysql_1.SchemaHelperMySQL; } });
|
|
16
|
+
var mssql_1 = require("./dialects/mssql");
|
|
17
|
+
Object.defineProperty(exports, "mssql", { enumerable: true, get: function () { return mssql_1.SchemaHelperMSSQL; } });
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { DatabaseHelper } from '../types';
|
|
2
2
|
import { KNEX_TYPES } from '@directus/shared/constants';
|
|
3
|
+
import { Knex } from 'knex';
|
|
3
4
|
type Clients = 'mysql' | 'postgres' | 'cockroachdb' | 'sqlite' | 'oracle' | 'mssql' | 'redshift';
|
|
4
5
|
export type Options = {
|
|
5
6
|
nullable?: boolean;
|
|
@@ -14,5 +15,9 @@ export declare abstract class SchemaHelper extends DatabaseHelper {
|
|
|
14
15
|
preColumnChange(): Promise<boolean>;
|
|
15
16
|
postColumnChange(): Promise<void>;
|
|
16
17
|
constraintName(existingName: string): string;
|
|
18
|
+
applyOffset(rootQuery: Knex.QueryBuilder, offset: number): void;
|
|
19
|
+
castA2oPrimaryKey(): string;
|
|
20
|
+
applyMultiRelationalSort(knex: Knex, dbQuery: Knex.QueryBuilder, table: string, primaryKey: string, orderByString: string, orderByFields: Knex.Raw[]): Knex.QueryBuilder;
|
|
21
|
+
formatUUID(uuid: string): string;
|
|
17
22
|
}
|
|
18
23
|
export {};
|
|
@@ -67,5 +67,18 @@ class SchemaHelper extends types_1.DatabaseHelper {
|
|
|
67
67
|
// reference issue #14873
|
|
68
68
|
return existingName;
|
|
69
69
|
}
|
|
70
|
+
applyOffset(rootQuery, offset) {
|
|
71
|
+
rootQuery.offset(offset);
|
|
72
|
+
}
|
|
73
|
+
castA2oPrimaryKey() {
|
|
74
|
+
return 'CAST(?? AS CHAR(255))';
|
|
75
|
+
}
|
|
76
|
+
applyMultiRelationalSort(knex, dbQuery, table, primaryKey, orderByString, orderByFields) {
|
|
77
|
+
dbQuery.rowNumber(knex.ref('directus_row_number').toQuery(), knex.raw(`partition by ??${orderByString}`, [`${table}.${primaryKey}`, ...orderByFields]));
|
|
78
|
+
return dbQuery;
|
|
79
|
+
}
|
|
80
|
+
formatUUID(uuid) {
|
|
81
|
+
return uuid; // no-op by defaut
|
|
82
|
+
}
|
|
70
83
|
}
|
|
71
84
|
exports.SchemaHelper = SchemaHelper;
|
package/dist/database/index.d.ts
CHANGED
|
@@ -2,6 +2,12 @@ import SchemaInspector from '@directus/schema';
|
|
|
2
2
|
import { Knex } from 'knex';
|
|
3
3
|
export default function getDatabase(): Knex;
|
|
4
4
|
export declare function getSchemaInspector(): ReturnType<typeof SchemaInspector>;
|
|
5
|
+
/**
|
|
6
|
+
* Get database version. Value currently exists for MySQL only.
|
|
7
|
+
*
|
|
8
|
+
* @returns Cached database version
|
|
9
|
+
*/
|
|
10
|
+
export declare function getDatabaseVersion(): string | null;
|
|
5
11
|
export declare function hasDatabaseConnection(database?: Knex): Promise<boolean>;
|
|
6
12
|
export declare function validateDatabaseConnection(database?: Knex): Promise<void>;
|
|
7
13
|
export declare function getDatabaseClient(database?: Knex): 'mysql' | 'postgres' | 'cockroachdb' | 'sqlite' | 'oracle' | 'mssql' | 'redshift';
|
package/dist/database/index.js
CHANGED
|
@@ -3,7 +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
|
-
exports.validateDatabaseExtensions = exports.validateMigrations = exports.isInstalled = exports.getDatabaseClient = exports.validateDatabaseConnection = exports.hasDatabaseConnection = exports.getSchemaInspector = void 0;
|
|
6
|
+
exports.validateDatabaseExtensions = exports.validateMigrations = exports.isInstalled = exports.getDatabaseClient = exports.validateDatabaseConnection = exports.hasDatabaseConnection = exports.getDatabaseVersion = exports.getSchemaInspector = void 0;
|
|
7
7
|
const schema_1 = __importDefault(require("@directus/schema"));
|
|
8
8
|
const knex_1 = require("knex");
|
|
9
9
|
const perf_hooks_1 = require("perf_hooks");
|
|
@@ -18,6 +18,7 @@ const util_1 = require("util");
|
|
|
18
18
|
const helpers_1 = require("./helpers");
|
|
19
19
|
let database = null;
|
|
20
20
|
let inspector = null;
|
|
21
|
+
let databaseVersion = null;
|
|
21
22
|
function getDatabase() {
|
|
22
23
|
if (database) {
|
|
23
24
|
return database;
|
|
@@ -95,6 +96,15 @@ function getDatabase() {
|
|
|
95
96
|
callback(null, conn);
|
|
96
97
|
};
|
|
97
98
|
}
|
|
99
|
+
if (client === 'mysql') {
|
|
100
|
+
poolConfig.afterCreate = async (conn, callback) => {
|
|
101
|
+
logger_1.default.trace('Retrieving database version');
|
|
102
|
+
const run = (0, util_1.promisify)(conn.query.bind(conn));
|
|
103
|
+
const version = await run('SELECT @@version;');
|
|
104
|
+
databaseVersion = version[0]['@@version'];
|
|
105
|
+
callback(null, conn);
|
|
106
|
+
};
|
|
107
|
+
}
|
|
98
108
|
if (client === 'mssql') {
|
|
99
109
|
// This brings MS SQL in line with the other DB vendors. We shouldn't do any automatic
|
|
100
110
|
// timezone conversion on the database level, especially not when other database vendors don't
|
|
@@ -125,6 +135,15 @@ function getSchemaInspector() {
|
|
|
125
135
|
return inspector;
|
|
126
136
|
}
|
|
127
137
|
exports.getSchemaInspector = getSchemaInspector;
|
|
138
|
+
/**
|
|
139
|
+
* Get database version. Value currently exists for MySQL only.
|
|
140
|
+
*
|
|
141
|
+
* @returns Cached database version
|
|
142
|
+
*/
|
|
143
|
+
function getDatabaseVersion() {
|
|
144
|
+
return databaseVersion;
|
|
145
|
+
}
|
|
146
|
+
exports.getDatabaseVersion = getDatabaseVersion;
|
|
128
147
|
async function hasDatabaseConnection(database) {
|
|
129
148
|
database = database !== null && database !== void 0 ? database : getDatabase();
|
|
130
149
|
try {
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.down = exports.up = void 0;
|
|
4
4
|
const utils_1 = require("@directus/shared/utils");
|
|
5
|
-
const nanoid_1 = require("nanoid");
|
|
6
5
|
async function up(knex) {
|
|
7
6
|
var _a;
|
|
8
7
|
await knex.schema.alterTable('directus_presets', (table) => {
|
|
@@ -54,6 +53,7 @@ async function up(knex) {
|
|
|
54
53
|
exports.up = up;
|
|
55
54
|
async function down(knex) {
|
|
56
55
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
56
|
+
const { nanoid } = await import('nanoid');
|
|
57
57
|
await knex.schema.alterTable('directus_presets', (table) => {
|
|
58
58
|
table.json('filters');
|
|
59
59
|
});
|
|
@@ -73,7 +73,7 @@ async function down(knex) {
|
|
|
73
73
|
if (!field || !operator || !value)
|
|
74
74
|
continue;
|
|
75
75
|
oldFilters.push({
|
|
76
|
-
key:
|
|
76
|
+
key: nanoid(),
|
|
77
77
|
field,
|
|
78
78
|
operator: operator.substring(1),
|
|
79
79
|
value,
|