@directus/api 9.25.0 → 9.25.2
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 +0 -2
- package/dist/controllers/assets.js +11 -5
- package/dist/database/migrations/run.js +8 -0
- package/dist/extensions.js +37 -38
- package/dist/utils/get-cache-key.js +2 -0
- package/package.json +16 -17
- package/README.md +0 -89
package/dist/app.js
CHANGED
|
@@ -6,7 +6,6 @@ import { createRequire } from 'node:module';
|
|
|
6
6
|
import path from 'path';
|
|
7
7
|
import qs from 'qs';
|
|
8
8
|
import { registerAuthProviders } from './auth.js';
|
|
9
|
-
import { flushCaches } from './cache.js';
|
|
10
9
|
import activityRouter from './controllers/activity.js';
|
|
11
10
|
import assetsRouter from './controllers/assets.js';
|
|
12
11
|
import authRouter from './controllers/auth.js';
|
|
@@ -76,7 +75,6 @@ export default async function createApp() {
|
|
|
76
75
|
if ((await validateMigrations()) === false) {
|
|
77
76
|
logger.warn(`Database migrations have not all been run`);
|
|
78
77
|
}
|
|
79
|
-
await flushCaches();
|
|
80
78
|
await registerAuthProviders();
|
|
81
79
|
const extensionManager = getExtensionManager();
|
|
82
80
|
const flowManager = getFlowManager();
|
|
@@ -105,18 +105,23 @@ asyncHandler(async (req, res) => {
|
|
|
105
105
|
accountability: req.accountability,
|
|
106
106
|
schema: req.schema,
|
|
107
107
|
});
|
|
108
|
+
const vary = ['Origin', 'Cache-Control'];
|
|
108
109
|
const transformation = res.locals['transformation'].key
|
|
109
110
|
? res.locals['shortcuts'].find((transformation) => transformation['key'] === res.locals['transformation'].key)
|
|
110
111
|
: res.locals['transformation'];
|
|
111
|
-
if (transformation.format === 'auto'
|
|
112
|
-
let format
|
|
113
|
-
if (req.headers.accept
|
|
112
|
+
if (transformation.format === 'auto') {
|
|
113
|
+
let format;
|
|
114
|
+
if (req.headers.accept?.includes('image/avif')) {
|
|
115
|
+
format = 'avif';
|
|
116
|
+
}
|
|
117
|
+
else if (req.headers.accept?.includes('image/webp')) {
|
|
114
118
|
format = 'webp';
|
|
115
119
|
}
|
|
116
|
-
else
|
|
117
|
-
format = '
|
|
120
|
+
else {
|
|
121
|
+
format = 'jpg';
|
|
118
122
|
}
|
|
119
123
|
transformation.format = format;
|
|
124
|
+
vary.push('Accept');
|
|
120
125
|
}
|
|
121
126
|
let range = undefined;
|
|
122
127
|
if (req.headers.range) {
|
|
@@ -141,6 +146,7 @@ asyncHandler(async (req, res) => {
|
|
|
141
146
|
res.setHeader('Content-Type', file.type);
|
|
142
147
|
res.setHeader('Accept-Ranges', 'bytes');
|
|
143
148
|
res.setHeader('Cache-Control', getCacheControlHeader(req, getMilliseconds(env['ASSETS_CACHE_TTL']), false, true));
|
|
149
|
+
res.setHeader('Vary', vary.join(', '));
|
|
144
150
|
const unixTime = Date.parse(file.modified_on);
|
|
145
151
|
if (!Number.isNaN(unixTime)) {
|
|
146
152
|
const lastModifiedDate = new Date(unixTime);
|
|
@@ -4,6 +4,7 @@ import { orderBy } from 'lodash-es';
|
|
|
4
4
|
import { dirname } from 'node:path';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
6
|
import path from 'path';
|
|
7
|
+
import { flushCaches } from '../../cache.js';
|
|
7
8
|
import env from '../../env.js';
|
|
8
9
|
import logger from '../../logger.js';
|
|
9
10
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
@@ -59,6 +60,7 @@ export default async function run(database, direction, log = true) {
|
|
|
59
60
|
}
|
|
60
61
|
await up(database);
|
|
61
62
|
await database.insert({ version: nextVersion.version, name: nextVersion.name }).into('directus_migrations');
|
|
63
|
+
await flushCaches(true);
|
|
62
64
|
}
|
|
63
65
|
async function down() {
|
|
64
66
|
const lastAppliedMigration = orderBy(completedMigrations, ['timestamp', 'version'], ['desc', 'desc'])[0];
|
|
@@ -75,10 +77,13 @@ export default async function run(database, direction, log = true) {
|
|
|
75
77
|
}
|
|
76
78
|
await down(database);
|
|
77
79
|
await database('directus_migrations').delete().where({ version: migration.version });
|
|
80
|
+
await flushCaches(true);
|
|
78
81
|
}
|
|
79
82
|
async function latest() {
|
|
83
|
+
let needsCacheFlush = false;
|
|
80
84
|
for (const migration of migrations) {
|
|
81
85
|
if (migration.completed === false) {
|
|
86
|
+
needsCacheFlush = true;
|
|
82
87
|
const { up } = await import(`file://${migration.file}`);
|
|
83
88
|
if (log) {
|
|
84
89
|
logger.info(`Applying ${migration.name}...`);
|
|
@@ -87,5 +92,8 @@ export default async function run(database, direction, log = true) {
|
|
|
87
92
|
await database.insert({ version: migration.version, name: migration.name }).into('directus_migrations');
|
|
88
93
|
}
|
|
89
94
|
}
|
|
95
|
+
if (needsCacheFlush) {
|
|
96
|
+
await flushCaches(true);
|
|
97
|
+
}
|
|
90
98
|
}
|
|
91
99
|
}
|
package/dist/extensions.js
CHANGED
|
@@ -7,7 +7,6 @@ import nodeResolveDefault from '@rollup/plugin-node-resolve';
|
|
|
7
7
|
import virtualDefault from '@rollup/plugin-virtual';
|
|
8
8
|
import chokidar from 'chokidar';
|
|
9
9
|
import express, { Router } from 'express';
|
|
10
|
-
import globby from 'globby';
|
|
11
10
|
import { clone, escapeRegExp } from 'lodash-es';
|
|
12
11
|
import { schedule, validate } from 'node-cron';
|
|
13
12
|
import { readdir } from 'node:fs/promises';
|
|
@@ -67,15 +66,15 @@ class ExtensionManager {
|
|
|
67
66
|
this.appExtensionChunks = new Map();
|
|
68
67
|
}
|
|
69
68
|
async initialize(options = {}) {
|
|
70
|
-
const prevOptions = this.options;
|
|
71
69
|
this.options = {
|
|
72
70
|
...defaultOptions,
|
|
73
71
|
...options,
|
|
74
72
|
};
|
|
75
|
-
|
|
73
|
+
const wasWatcherInitialized = this.watcher !== null;
|
|
74
|
+
if (this.options.watch && !wasWatcherInitialized) {
|
|
76
75
|
this.initializeWatcher();
|
|
77
76
|
}
|
|
78
|
-
else if (
|
|
77
|
+
else if (!this.options.watch && wasWatcherInitialized) {
|
|
79
78
|
await this.closeWatcher();
|
|
80
79
|
}
|
|
81
80
|
if (!this.isLoaded) {
|
|
@@ -85,7 +84,7 @@ class ExtensionManager {
|
|
|
85
84
|
logger.info(`Loaded extensions: ${loadedExtensions.map((ext) => ext.name).join(', ')}`);
|
|
86
85
|
}
|
|
87
86
|
}
|
|
88
|
-
if (
|
|
87
|
+
if (this.options.watch && !wasWatcherInitialized) {
|
|
89
88
|
this.updateWatchedExtensions(this.extensions);
|
|
90
89
|
}
|
|
91
90
|
}
|
|
@@ -198,29 +197,32 @@ class ExtensionManager {
|
|
|
198
197
|
this.isLoaded = false;
|
|
199
198
|
}
|
|
200
199
|
initializeWatcher() {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
200
|
+
logger.info('Watching extensions for changes...');
|
|
201
|
+
const localExtensionPaths = NESTED_EXTENSION_TYPES.flatMap((type) => {
|
|
202
|
+
const typeDir = path.posix.join(pathToRelativeUrl(env['EXTENSIONS_PATH']), pluralize(type));
|
|
203
|
+
const fileExts = ['js', 'mjs', 'cjs'];
|
|
204
|
+
if (isIn(type, HYBRID_EXTENSION_TYPES)) {
|
|
205
|
+
return [
|
|
206
|
+
path.posix.join(typeDir, '*', `app.{${fileExts.join()}}`),
|
|
207
|
+
path.posix.join(typeDir, '*', `api.{${fileExts.join()}}`),
|
|
208
|
+
];
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
return path.posix.join(typeDir, '*', `index.{${fileExts.join()}}`);
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
this.watcher = chokidar.watch([path.resolve('package.json'), ...localExtensionPaths], {
|
|
215
|
+
ignoreInitial: true,
|
|
216
|
+
});
|
|
217
|
+
this.watcher
|
|
218
|
+
.on('add', () => this.reload())
|
|
219
|
+
.on('change', () => this.reload())
|
|
220
|
+
.on('unlink', () => this.reload());
|
|
220
221
|
}
|
|
221
222
|
async closeWatcher() {
|
|
222
223
|
if (this.watcher) {
|
|
223
224
|
await this.watcher.close();
|
|
225
|
+
this.watcher = null;
|
|
224
226
|
}
|
|
225
227
|
}
|
|
226
228
|
updateWatchedExtensions(added, removed = []) {
|
|
@@ -295,7 +297,7 @@ class ExtensionManager {
|
|
|
295
297
|
for (const hook of hooks) {
|
|
296
298
|
try {
|
|
297
299
|
const hookPath = path.resolve(hook.path, hook.entrypoint);
|
|
298
|
-
const hookInstance = await import(
|
|
300
|
+
const hookInstance = await import(`./${pathToRelativeUrl(hookPath, __dirname)}?t=${Date.now()}`);
|
|
299
301
|
const config = getModuleDefault(hookInstance);
|
|
300
302
|
this.registerHook(config);
|
|
301
303
|
this.apiExtensions.push({ path: hookPath });
|
|
@@ -311,7 +313,7 @@ class ExtensionManager {
|
|
|
311
313
|
for (const endpoint of endpoints) {
|
|
312
314
|
try {
|
|
313
315
|
const endpointPath = path.resolve(endpoint.path, endpoint.entrypoint);
|
|
314
|
-
const endpointInstance = await import(
|
|
316
|
+
const endpointInstance = await import(`./${pathToRelativeUrl(endpointPath, __dirname)}?t=${Date.now()}`);
|
|
315
317
|
const config = getModuleDefault(endpointInstance);
|
|
316
318
|
this.registerEndpoint(config, endpoint.name);
|
|
317
319
|
this.apiExtensions.push({ path: endpointPath });
|
|
@@ -323,20 +325,17 @@ class ExtensionManager {
|
|
|
323
325
|
}
|
|
324
326
|
}
|
|
325
327
|
async registerOperations() {
|
|
326
|
-
const
|
|
327
|
-
const
|
|
328
|
-
const
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
entrypoint: { api: dirs[dirs.length - 1] },
|
|
333
|
-
};
|
|
334
|
-
});
|
|
328
|
+
const internalOperations = await readdir(path.join(__dirname, 'operations'));
|
|
329
|
+
for (const operation of internalOperations) {
|
|
330
|
+
const operationInstance = await import(`./operations/${operation}/index.js`);
|
|
331
|
+
const config = getModuleDefault(operationInstance);
|
|
332
|
+
this.registerOperation(config);
|
|
333
|
+
}
|
|
335
334
|
const operations = this.extensions.filter((extension) => extension.type === 'operation');
|
|
336
|
-
for (const operation of
|
|
335
|
+
for (const operation of operations) {
|
|
337
336
|
try {
|
|
338
337
|
const operationPath = path.resolve(operation.path, operation.entrypoint.api);
|
|
339
|
-
const operationInstance = await import(
|
|
338
|
+
const operationInstance = await import(`./${pathToRelativeUrl(operationPath, __dirname)}?t=${Date.now()}`);
|
|
340
339
|
const config = getModuleDefault(operationInstance);
|
|
341
340
|
this.registerOperation(config);
|
|
342
341
|
this.apiExtensions.push({ path: operationPath });
|
|
@@ -352,7 +351,7 @@ class ExtensionManager {
|
|
|
352
351
|
for (const bundle of bundles) {
|
|
353
352
|
try {
|
|
354
353
|
const bundlePath = path.resolve(bundle.path, bundle.entrypoint.api);
|
|
355
|
-
const bundleInstances = await import(
|
|
354
|
+
const bundleInstances = await import(`./${pathToRelativeUrl(bundlePath, __dirname)}?t=${Date.now()}`);
|
|
356
355
|
const configs = getModuleDefault(bundleInstances);
|
|
357
356
|
for (const { config } of configs.hooks) {
|
|
358
357
|
this.registerHook(config);
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import hash from 'object-hash';
|
|
2
2
|
import url from 'url';
|
|
3
3
|
import { getGraphqlQueryAndVariables } from './get-graphql-query-and-variables.js';
|
|
4
|
+
import { version } from './package.js';
|
|
4
5
|
export function getCacheKey(req) {
|
|
5
6
|
const path = url.parse(req.originalUrl).pathname;
|
|
6
7
|
const isGraphQl = path?.startsWith('/graphql');
|
|
7
8
|
const info = {
|
|
9
|
+
version,
|
|
8
10
|
user: req.accountability?.user || null,
|
|
9
11
|
path,
|
|
10
12
|
query: isGraphQl ? getGraphqlQueryAndVariables(req) : req.sanitizedQuery,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@directus/api",
|
|
3
|
-
"version": "9.25.
|
|
3
|
+
"version": "9.25.2",
|
|
4
4
|
"description": "Directus is a real-time API and App dashboard for managing SQL database content",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -95,7 +95,6 @@
|
|
|
95
95
|
"fast-redact": "3.1.2",
|
|
96
96
|
"flat": "5.0.2",
|
|
97
97
|
"fs-extra": "11.1.0",
|
|
98
|
-
"globby": "11.1.0",
|
|
99
98
|
"graphql": "16.6.0",
|
|
100
99
|
"graphql-compose": "9.0.10",
|
|
101
100
|
"helmet": "6.0.1",
|
|
@@ -144,20 +143,20 @@
|
|
|
144
143
|
"uuid-validate": "0.0.3",
|
|
145
144
|
"vm2": "3.9.16",
|
|
146
145
|
"wellknown": "0.5.0",
|
|
147
|
-
"@directus/app": "9.25.
|
|
148
|
-
"@directus/constants": "9.25.
|
|
149
|
-
"@directus/exceptions": "9.25.
|
|
150
|
-
"@directus/extensions-sdk": "9.25.
|
|
151
|
-
"@directus/schema": "9.25.
|
|
152
|
-
"@directus/specs": "9.25.
|
|
153
|
-
"@directus/storage": "9.25.
|
|
154
|
-
"@directus/storage-driver-azure": "9.25.
|
|
155
|
-
"@directus/storage-driver-cloudinary": "9.25.
|
|
156
|
-
"@directus/storage-driver-gcs": "9.25.
|
|
157
|
-
"@directus/storage-driver-local": "9.25.
|
|
158
|
-
"@directus/storage-driver-s3": "9.25.
|
|
159
|
-
"@directus/update-check": "9.25.
|
|
160
|
-
"@directus/utils": "9.25.
|
|
146
|
+
"@directus/app": "9.25.2",
|
|
147
|
+
"@directus/constants": "9.25.2",
|
|
148
|
+
"@directus/exceptions": "9.25.2",
|
|
149
|
+
"@directus/extensions-sdk": "9.25.2",
|
|
150
|
+
"@directus/schema": "9.25.2",
|
|
151
|
+
"@directus/specs": "9.25.2",
|
|
152
|
+
"@directus/storage": "9.25.2",
|
|
153
|
+
"@directus/storage-driver-azure": "9.25.2",
|
|
154
|
+
"@directus/storage-driver-cloudinary": "9.25.2",
|
|
155
|
+
"@directus/storage-driver-gcs": "9.25.2",
|
|
156
|
+
"@directus/storage-driver-local": "9.25.2",
|
|
157
|
+
"@directus/storage-driver-s3": "9.25.2",
|
|
158
|
+
"@directus/update-check": "9.25.2",
|
|
159
|
+
"@directus/utils": "9.25.2"
|
|
161
160
|
},
|
|
162
161
|
"devDependencies": {
|
|
163
162
|
"@directus/tsconfig": "0.0.6",
|
|
@@ -206,7 +205,7 @@
|
|
|
206
205
|
"supertest": "6.3.3",
|
|
207
206
|
"typescript": "4.9.5",
|
|
208
207
|
"vitest": "0.29.3",
|
|
209
|
-
"@directus/types": "9.25.
|
|
208
|
+
"@directus/types": "9.25.2"
|
|
210
209
|
},
|
|
211
210
|
"optionalDependencies": {
|
|
212
211
|
"@keyv/redis": "2.5.7",
|
package/README.md
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
<p> </p>
|
|
2
|
-
|
|
3
|
-
<a href="https://directus.io" target="_blank" rel="noopener noreferrer"><img width="250" alt="Logo" src="https://user-images.githubusercontent.com/9141017/88821768-0dc99800-d191-11ea-8c66-09c55ab451a2.png"></a>
|
|
4
|
-
|
|
5
|
-
<p> </p>
|
|
6
|
-
|
|
7
|
-
## Introduction
|
|
8
|
-
|
|
9
|
-
**Directus is a free and open-source data platform for headless content management**. It can be installed on top of any
|
|
10
|
-
new or existing SQL database, instantly providing a dynamic API (REST+GraphQL) and accompanying App for managing
|
|
11
|
-
content. Built entirely in TypeScript (in Node and Vue), Directus is completely modular and end-to-end extensible...
|
|
12
|
-
with absolutely no paywalls or artificial limitations.
|
|
13
|
-
|
|
14
|
-
Modern and intuitive, the Directus App enables no-code data discovery, allowing for even the most non-technical users to
|
|
15
|
-
view, author, and manage your raw database content. Our performant and flexible API is able to adapt to any relational
|
|
16
|
-
schema, and includes rule-based permissions, event/web hooks, custom endpoints, numerous auth options, configurable
|
|
17
|
-
storage adapters, and much more.
|
|
18
|
-
|
|
19
|
-
Current database support includes: PostgreSQL, MySQL, SQLite, MS-SQL Server, OracleDB, MariaDB, and variants such as AWS
|
|
20
|
-
Aurora/Redshift or Google Cloud Platform SQL.
|
|
21
|
-
|
|
22
|
-
Learn more at...
|
|
23
|
-
|
|
24
|
-
- [Website](https://directus.io/)
|
|
25
|
-
- [GitHub](https://github.com/directus/directus)
|
|
26
|
-
- [Documentation](https://docs.directus.io/)
|
|
27
|
-
- [Community](https://directus.chat/)
|
|
28
|
-
- [Twitter](https://twitter.com/directus)
|
|
29
|
-
- [Cloud](https://directus.cloud/)
|
|
30
|
-
- [Marketplace](https://directus.market/)
|
|
31
|
-
|
|
32
|
-
<p> </p>
|
|
33
|
-
|
|
34
|
-
## Installing
|
|
35
|
-
|
|
36
|
-
Directus requires NodeJS 10+. Create a new project with our simple CLI tool:
|
|
37
|
-
|
|
38
|
-
```
|
|
39
|
-
npm init directus-project my-project
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
Or using yarn:
|
|
43
|
-
|
|
44
|
-
```
|
|
45
|
-
yarn create directus-project my-project
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
The above command will create a directory with your project name, then walk you through the database configuration and
|
|
49
|
-
creation of your first admin user.
|
|
50
|
-
|
|
51
|
-
<p> </p>
|
|
52
|
-
|
|
53
|
-
## Updating
|
|
54
|
-
|
|
55
|
-
To update an existing Directus project, navigate to your project directory and run:
|
|
56
|
-
|
|
57
|
-
```
|
|
58
|
-
npm update
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
<p> </p>
|
|
62
|
-
|
|
63
|
-
## Contributing
|
|
64
|
-
|
|
65
|
-
Please report any and all issues [on our GitHub](https://github.com/directus/directus/issues/new).
|
|
66
|
-
|
|
67
|
-
Pull-requests are more than welcome, and always appreciated. Please be sure to read our
|
|
68
|
-
[Contributors Guide](https://docs.directus.io/contributing/introduction/) before starting work on a new feature/fix, or
|
|
69
|
-
reach out a member of the Core Team via [GitHub](https://github.com/directus/directus/discussions) or
|
|
70
|
-
[Discord](https://directus.chat) with any questions.
|
|
71
|
-
|
|
72
|
-
<p> </p>
|
|
73
|
-
|
|
74
|
-
## Supporting
|
|
75
|
-
|
|
76
|
-
Directus is a free and open-source project with development made possible by support from our passionate core team,
|
|
77
|
-
amazing contributors, and generous sponsors. It's not easy building premium open-source software; if you would like to
|
|
78
|
-
help ensure Directus stays free, please consider becoming a sponsor.
|
|
79
|
-
|
|
80
|
-
- [Support us through GitHub Sponsors](https://github.com/sponsors/directus)
|
|
81
|
-
- [One-time donation through PayPal](https://www.paypal.me/supportdirectus)
|
|
82
|
-
- [Backing Directus](https://docs.directus.io/getting-started/backing-directus/)
|
|
83
|
-
|
|
84
|
-
<p> </p>
|
|
85
|
-
|
|
86
|
-
## License
|
|
87
|
-
|
|
88
|
-
Directus is released under the [GPLv3 license](./license). Monospace Inc owns all Directus trademarks, logos, and
|
|
89
|
-
intellectual property on behalf of our project's community. Copyright © 2004-2020, Monospace Inc.
|