directus 9.10.0 → 9.12.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 +8 -1
- package/dist/auth/drivers/oauth2.d.ts +1 -1
- package/dist/auth/drivers/oauth2.js +14 -11
- package/dist/auth/drivers/openid.d.ts +1 -1
- package/dist/auth/drivers/openid.js +14 -11
- package/dist/cli/commands/schema/apply.js +4 -3
- package/dist/cli/utils/create-env/env-stub.liquid +266 -9
- package/dist/controllers/activity.js +1 -1
- package/dist/controllers/assets.js +8 -9
- package/dist/controllers/flows.d.ts +2 -0
- package/dist/controllers/flows.js +157 -0
- package/dist/controllers/folders.js +1 -1
- package/dist/controllers/notifications.js +1 -1
- package/dist/controllers/operations.d.ts +2 -0
- package/dist/controllers/operations.js +138 -0
- package/dist/database/helpers/date/dialects/sqlite.js +6 -2
- package/dist/database/index.js +15 -19
- package/dist/database/migrations/20210225A-add-relations-sort-field.js +2 -1
- package/dist/database/migrations/20210506A-rename-interfaces.js +2 -1
- package/dist/database/migrations/20210802A-replace-groups.js +2 -1
- package/dist/database/migrations/20210805A-update-groups.js +2 -1
- package/dist/database/migrations/20210805B-change-image-metadata-structure.js +3 -2
- package/dist/database/migrations/20211007A-update-presets.js +5 -4
- package/dist/database/migrations/20220429A-add-flows.d.ts +3 -0
- package/dist/database/migrations/20220429A-add-flows.js +83 -0
- package/dist/database/migrations/20220429B-add-color-to-insights-icon.d.ts +3 -0
- package/dist/database/migrations/20220429B-add-color-to-insights-icon.js +15 -0
- package/dist/database/migrations/20220429C-drop-non-null-from-ip-of-activity.d.ts +3 -0
- package/dist/database/migrations/20220429C-drop-non-null-from-ip-of-activity.js +15 -0
- package/dist/database/migrations/20220429D-drop-non-null-from-sender-of-notifications.d.ts +3 -0
- package/dist/database/migrations/20220429D-drop-non-null-from-sender-of-notifications.js +15 -0
- package/dist/database/run-ast.js +10 -14
- package/dist/database/seeds/05-activity.yaml +0 -1
- package/dist/database/system-data/collections/collections.yaml +4 -0
- package/dist/database/system-data/fields/activity.yaml +3 -0
- package/dist/database/system-data/fields/dashboards.yaml +3 -1
- package/dist/database/system-data/fields/flows.yaml +21 -0
- package/dist/database/system-data/fields/notifications.yaml +3 -1
- package/dist/database/system-data/fields/operations.yaml +19 -0
- package/dist/database/system-data/fields/panels.yaml +3 -1
- package/dist/database/system-data/fields/shares.yaml +3 -1
- package/dist/database/system-data/fields/users.yaml +2 -4
- package/dist/database/system-data/relations/relations.yaml +20 -0
- package/dist/env.d.ts +1 -1
- package/dist/env.js +167 -12
- package/dist/exceptions/index.d.ts +1 -0
- package/dist/exceptions/index.js +1 -0
- package/dist/exceptions/invalid-provider.d.ts +4 -0
- package/dist/exceptions/invalid-provider.js +10 -0
- package/dist/exceptions/range-not-satisfiable.d.ts +2 -2
- package/dist/exceptions/range-not-satisfiable.js +5 -1
- package/dist/extensions.d.ts +3 -0
- package/dist/extensions.js +73 -20
- package/dist/flows.d.ts +17 -0
- package/dist/flows.js +310 -0
- package/dist/messenger.d.ts +24 -0
- package/dist/messenger.js +64 -0
- package/dist/middleware/graphql.js +2 -1
- package/dist/operations/condition/index.d.ts +6 -0
- package/dist/operations/condition/index.js +15 -0
- package/dist/operations/item-create/index.d.ts +8 -0
- package/dist/operations/item-create/index.js +40 -0
- package/dist/operations/item-delete/index.d.ts +9 -0
- package/dist/operations/item-delete/index.js +45 -0
- package/dist/operations/item-read/index.d.ts +9 -0
- package/dist/operations/item-read/index.js +45 -0
- package/dist/operations/item-update/index.d.ts +10 -0
- package/dist/operations/item-update/index.js +50 -0
- package/dist/operations/log/index.d.ts +5 -0
- package/dist/operations/log/index.js +14 -0
- package/dist/operations/mail/index.d.ts +7 -0
- package/dist/operations/mail/index.js +16 -0
- package/dist/operations/notification/index.d.ts +8 -0
- package/dist/operations/notification/index.js +39 -0
- package/dist/operations/request/index.d.ts +9 -0
- package/dist/operations/request/index.js +14 -0
- package/dist/operations/sleep/index.d.ts +5 -0
- package/dist/operations/sleep/index.js +9 -0
- package/dist/operations/transform/index.d.ts +5 -0
- package/dist/operations/transform/index.js +10 -0
- package/dist/operations/trigger/index.d.ts +6 -0
- package/dist/operations/trigger/index.js +21 -0
- package/dist/services/activity.d.ts +1 -2
- package/dist/services/activity.js +10 -10
- package/dist/services/assets.js +27 -1
- package/dist/services/authentication.d.ts +2 -2
- package/dist/services/authentication.js +11 -8
- package/dist/services/authorization.js +12 -0
- package/dist/services/fields.js +15 -8
- package/dist/services/flows.d.ts +14 -0
- package/dist/services/flows.js +42 -0
- package/dist/services/graphql.js +56 -33
- package/dist/services/import-export.d.ts +1 -1
- package/dist/services/import-export.js +13 -12
- package/dist/services/index.d.ts +2 -0
- package/dist/services/index.js +2 -0
- package/dist/services/items.d.ts +3 -3
- package/dist/services/items.js +25 -2
- package/dist/services/mail/index.js +2 -1
- package/dist/services/notifications.d.ts +2 -1
- package/dist/services/notifications.js +4 -3
- package/dist/services/operations.d.ts +14 -0
- package/dist/services/operations.js +42 -0
- package/dist/services/payload.d.ts +2 -2
- package/dist/services/payload.js +8 -7
- package/dist/services/users.d.ts +4 -0
- package/dist/services/users.js +20 -0
- package/dist/services/webhooks.d.ts +2 -0
- package/dist/services/webhooks.js +8 -7
- package/dist/types/events.d.ts +18 -0
- package/dist/types/events.js +2 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.js +1 -1
- package/dist/utils/apply-query.js +31 -4
- package/dist/utils/apply-snapshot.d.ts +3 -3
- package/dist/utils/apply-snapshot.js +64 -49
- package/dist/utils/construct-flow-tree.d.ts +2 -0
- package/dist/utils/construct-flow-tree.js +31 -0
- package/dist/utils/get-accountability-for-role.d.ts +7 -0
- package/dist/utils/get-accountability-for-role.js +36 -0
- package/dist/utils/get-ast-from-query.js +1 -7
- package/dist/utils/get-default-value.js +4 -3
- package/dist/utils/get-permissions.d.ts +1 -1
- package/dist/utils/get-permissions.js +9 -8
- package/dist/utils/get-schema.js +2 -1
- package/dist/utils/get-snapshot.js +22 -4
- package/dist/utils/operation-options.d.ts +3 -0
- package/dist/utils/operation-options.js +45 -0
- package/dist/utils/parse-json.d.ts +5 -0
- package/dist/utils/parse-json.js +19 -0
- package/dist/utils/sanitize-query.d.ts +1 -2
- package/dist/utils/sanitize-query.js +6 -5
- package/dist/utils/validate-keys.d.ts +6 -0
- package/dist/utils/validate-keys.js +28 -0
- package/dist/utils/validate-query.js +1 -1
- package/dist/webhooks.d.ts +2 -0
- package/dist/webhooks.js +17 -2
- package/package.json +18 -14
- package/dist/types/activity.d.ts +0 -9
- package/dist/types/activity.js +0 -13
- package/example.env +0 -202
package/dist/app.js
CHANGED
|
@@ -36,12 +36,14 @@ const dashboards_1 = __importDefault(require("./controllers/dashboards"));
|
|
|
36
36
|
const extensions_1 = __importDefault(require("./controllers/extensions"));
|
|
37
37
|
const fields_1 = __importDefault(require("./controllers/fields"));
|
|
38
38
|
const files_1 = __importDefault(require("./controllers/files"));
|
|
39
|
+
const flows_1 = __importDefault(require("./controllers/flows"));
|
|
39
40
|
const folders_1 = __importDefault(require("./controllers/folders"));
|
|
40
41
|
const graphql_1 = __importDefault(require("./controllers/graphql"));
|
|
41
42
|
const items_1 = __importDefault(require("./controllers/items"));
|
|
42
43
|
const not_found_1 = __importDefault(require("./controllers/not-found"));
|
|
43
44
|
const panels_1 = __importDefault(require("./controllers/panels"));
|
|
44
45
|
const notifications_1 = __importDefault(require("./controllers/notifications"));
|
|
46
|
+
const operations_1 = __importDefault(require("./controllers/operations"));
|
|
45
47
|
const permissions_1 = __importDefault(require("./controllers/permissions"));
|
|
46
48
|
const presets_1 = __importDefault(require("./controllers/presets"));
|
|
47
49
|
const relations_1 = __importDefault(require("./controllers/relations"));
|
|
@@ -58,6 +60,7 @@ const emitter_1 = __importDefault(require("./emitter"));
|
|
|
58
60
|
const env_1 = __importDefault(require("./env"));
|
|
59
61
|
const exceptions_1 = require("./exceptions");
|
|
60
62
|
const extensions_2 = require("./extensions");
|
|
63
|
+
const flows_2 = require("./flows");
|
|
61
64
|
const logger_1 = __importStar(require("./logger"));
|
|
62
65
|
const authenticate_1 = __importDefault(require("./middleware/authenticate"));
|
|
63
66
|
const get_permissions_1 = __importDefault(require("./middleware/get-permissions"));
|
|
@@ -96,7 +99,9 @@ async function createApp() {
|
|
|
96
99
|
await (0, cache_2.flushCaches)();
|
|
97
100
|
await (0, auth_2.registerAuthProviders)();
|
|
98
101
|
const extensionManager = (0, extensions_2.getExtensionManager)();
|
|
102
|
+
const flowManager = (0, flows_2.getFlowManager)();
|
|
99
103
|
await extensionManager.initialize();
|
|
104
|
+
await flowManager.initialize();
|
|
100
105
|
const app = (0, express_1.default)();
|
|
101
106
|
app.disable('x-powered-by');
|
|
102
107
|
app.set('trust proxy', env_1.default.IP_TRUST_PROXY);
|
|
@@ -187,9 +192,11 @@ async function createApp() {
|
|
|
187
192
|
app.use('/extensions', extensions_1.default);
|
|
188
193
|
app.use('/fields', fields_1.default);
|
|
189
194
|
app.use('/files', files_1.default);
|
|
195
|
+
app.use('/flows', flows_1.default);
|
|
190
196
|
app.use('/folders', folders_1.default);
|
|
191
197
|
app.use('/items', items_1.default);
|
|
192
198
|
app.use('/notifications', notifications_1.default);
|
|
199
|
+
app.use('/operations', operations_1.default);
|
|
193
200
|
app.use('/panels', panels_1.default);
|
|
194
201
|
app.use('/permissions', permissions_1.default);
|
|
195
202
|
app.use('/presets', presets_1.default);
|
|
@@ -210,7 +217,7 @@ async function createApp() {
|
|
|
210
217
|
app.use(error_handler_1.default);
|
|
211
218
|
await emitter_1.default.emitInit('routes.after', { app });
|
|
212
219
|
// Register all webhooks
|
|
213
|
-
await (0, webhooks_2.
|
|
220
|
+
await (0, webhooks_2.init)();
|
|
214
221
|
(0, track_1.track)('serverStarted');
|
|
215
222
|
await emitter_1.default.emitInit('app.after', { app });
|
|
216
223
|
return app;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Router } from 'express';
|
|
2
2
|
import { Client } from 'openid-client';
|
|
3
|
-
import { LocalAuthDriver } from './local';
|
|
4
3
|
import { UsersService } from '../../services';
|
|
5
4
|
import { AuthDriverOptions, User } from '../../types';
|
|
5
|
+
import { LocalAuthDriver } from './local';
|
|
6
6
|
export declare class OAuth2AuthDriver extends LocalAuthDriver {
|
|
7
7
|
client: Client;
|
|
8
8
|
redirectUrl: string;
|
|
@@ -5,21 +5,22 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.createOAuth2AuthRouter = exports.OAuth2AuthDriver = void 0;
|
|
7
7
|
const express_1 = require("express");
|
|
8
|
-
const
|
|
8
|
+
const flat_1 = __importDefault(require("flat"));
|
|
9
9
|
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
|
10
10
|
const ms_1 = __importDefault(require("ms"));
|
|
11
|
-
const
|
|
12
|
-
const local_1 = require("./local");
|
|
11
|
+
const openid_client_1 = require("openid-client");
|
|
13
12
|
const auth_1 = require("../../auth");
|
|
14
13
|
const env_1 = __importDefault(require("../../env"));
|
|
15
|
-
const services_1 = require("../../services");
|
|
16
14
|
const exceptions_1 = require("../../exceptions");
|
|
15
|
+
const logger_1 = __importDefault(require("../../logger"));
|
|
17
16
|
const respond_1 = require("../../middleware/respond");
|
|
17
|
+
const services_1 = require("../../services");
|
|
18
18
|
const async_handler_1 = __importDefault(require("../../utils/async-handler"));
|
|
19
|
-
const url_1 = require("../../utils/url");
|
|
20
|
-
const logger_1 = __importDefault(require("../../logger"));
|
|
21
|
-
const get_ip_from_req_1 = require("../../utils/get-ip-from-req");
|
|
22
19
|
const get_config_from_env_1 = require("../../utils/get-config-from-env");
|
|
20
|
+
const get_ip_from_req_1 = require("../../utils/get-ip-from-req");
|
|
21
|
+
const parse_json_1 = require("../../utils/parse-json");
|
|
22
|
+
const url_1 = require("../../utils/url");
|
|
23
|
+
const local_1 = require("./local");
|
|
23
24
|
class OAuth2AuthDriver extends local_1.LocalAuthDriver {
|
|
24
25
|
constructor(options, config) {
|
|
25
26
|
super(options, config);
|
|
@@ -78,7 +79,6 @@ class OAuth2AuthDriver extends local_1.LocalAuthDriver {
|
|
|
78
79
|
return user === null || user === void 0 ? void 0 : user.id;
|
|
79
80
|
}
|
|
80
81
|
async getUserID(payload) {
|
|
81
|
-
var _a, _b;
|
|
82
82
|
if (!payload.code || !payload.codeVerifier) {
|
|
83
83
|
logger_1.default.trace('[OAuth2] No code or codeVerifier in payload');
|
|
84
84
|
throw new exceptions_1.InvalidCredentialsException();
|
|
@@ -95,9 +95,9 @@ class OAuth2AuthDriver extends local_1.LocalAuthDriver {
|
|
|
95
95
|
// Flatten response to support dot indexes
|
|
96
96
|
userInfo = (0, flat_1.default)(userInfo);
|
|
97
97
|
const { provider, emailKey, identifierKey, allowPublicRegistration } = this.config;
|
|
98
|
-
const email = userInfo[emailKey !== null && emailKey !== void 0 ? emailKey : 'email'];
|
|
98
|
+
const email = userInfo[emailKey !== null && emailKey !== void 0 ? emailKey : 'email'] ? String(userInfo[emailKey !== null && emailKey !== void 0 ? emailKey : 'email']) : undefined;
|
|
99
99
|
// Fallback to email if explicit identifier not found
|
|
100
|
-
const identifier =
|
|
100
|
+
const identifier = userInfo[identifierKey] ? String(userInfo[identifierKey]) : email;
|
|
101
101
|
if (!identifier) {
|
|
102
102
|
logger_1.default.warn(`[OAuth2] Failed to find user identifier for provider "${provider}"`);
|
|
103
103
|
throw new exceptions_1.InvalidCredentialsException();
|
|
@@ -135,7 +135,7 @@ class OAuth2AuthDriver extends local_1.LocalAuthDriver {
|
|
|
135
135
|
let authData = user.auth_data;
|
|
136
136
|
if (typeof authData === 'string') {
|
|
137
137
|
try {
|
|
138
|
-
authData =
|
|
138
|
+
authData = (0, parse_json_1.parseJSON)(authData);
|
|
139
139
|
}
|
|
140
140
|
catch {
|
|
141
141
|
logger_1.default.warn(`[OAuth2] Session data isn't valid JSON: ${authData}`);
|
|
@@ -243,6 +243,9 @@ function createOAuth2AuthRouter(providerName) {
|
|
|
243
243
|
else if (error instanceof exceptions_1.InvalidTokenException) {
|
|
244
244
|
reason = 'INVALID_TOKEN';
|
|
245
245
|
}
|
|
246
|
+
else if (error instanceof exceptions_1.InvalidProviderException) {
|
|
247
|
+
reason = 'INVALID_PROVIDER';
|
|
248
|
+
}
|
|
246
249
|
else {
|
|
247
250
|
logger_1.default.warn(error, `[OAuth2] Unexpected error during OAuth2 login`);
|
|
248
251
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Router } from 'express';
|
|
2
2
|
import { Client } from 'openid-client';
|
|
3
|
-
import { LocalAuthDriver } from './local';
|
|
4
3
|
import { UsersService } from '../../services';
|
|
5
4
|
import { AuthDriverOptions, User } from '../../types';
|
|
5
|
+
import { LocalAuthDriver } from './local';
|
|
6
6
|
export declare class OpenIDAuthDriver extends LocalAuthDriver {
|
|
7
7
|
client: Promise<Client>;
|
|
8
8
|
redirectUrl: string;
|
|
@@ -5,21 +5,22 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.createOpenIDAuthRouter = exports.OpenIDAuthDriver = void 0;
|
|
7
7
|
const express_1 = require("express");
|
|
8
|
-
const
|
|
8
|
+
const flat_1 = __importDefault(require("flat"));
|
|
9
9
|
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
|
10
10
|
const ms_1 = __importDefault(require("ms"));
|
|
11
|
-
const
|
|
12
|
-
const local_1 = require("./local");
|
|
11
|
+
const openid_client_1 = require("openid-client");
|
|
13
12
|
const auth_1 = require("../../auth");
|
|
14
13
|
const env_1 = __importDefault(require("../../env"));
|
|
15
|
-
const services_1 = require("../../services");
|
|
16
14
|
const exceptions_1 = require("../../exceptions");
|
|
15
|
+
const logger_1 = __importDefault(require("../../logger"));
|
|
17
16
|
const respond_1 = require("../../middleware/respond");
|
|
17
|
+
const services_1 = require("../../services");
|
|
18
18
|
const async_handler_1 = __importDefault(require("../../utils/async-handler"));
|
|
19
|
-
const url_1 = require("../../utils/url");
|
|
20
|
-
const logger_1 = __importDefault(require("../../logger"));
|
|
21
|
-
const get_ip_from_req_1 = require("../../utils/get-ip-from-req");
|
|
22
19
|
const get_config_from_env_1 = require("../../utils/get-config-from-env");
|
|
20
|
+
const get_ip_from_req_1 = require("../../utils/get-ip-from-req");
|
|
21
|
+
const parse_json_1 = require("../../utils/parse-json");
|
|
22
|
+
const url_1 = require("../../utils/url");
|
|
23
|
+
const local_1 = require("./local");
|
|
23
24
|
class OpenIDAuthDriver extends local_1.LocalAuthDriver {
|
|
24
25
|
constructor(options, config) {
|
|
25
26
|
super(options, config);
|
|
@@ -85,7 +86,6 @@ class OpenIDAuthDriver extends local_1.LocalAuthDriver {
|
|
|
85
86
|
return user === null || user === void 0 ? void 0 : user.id;
|
|
86
87
|
}
|
|
87
88
|
async getUserID(payload) {
|
|
88
|
-
var _a, _b;
|
|
89
89
|
if (!payload.code || !payload.codeVerifier) {
|
|
90
90
|
logger_1.default.trace('[OpenID] No code or codeVerifier in payload');
|
|
91
91
|
throw new exceptions_1.InvalidCredentialsException();
|
|
@@ -109,9 +109,9 @@ class OpenIDAuthDriver extends local_1.LocalAuthDriver {
|
|
|
109
109
|
// Flatten response to support dot indexes
|
|
110
110
|
userInfo = (0, flat_1.default)(userInfo);
|
|
111
111
|
const { provider, identifierKey, allowPublicRegistration, requireVerifiedEmail } = this.config;
|
|
112
|
-
const email = userInfo.email;
|
|
112
|
+
const email = userInfo.email ? String(userInfo.email) : undefined;
|
|
113
113
|
// Fallback to email if explicit identifier not found
|
|
114
|
-
const identifier =
|
|
114
|
+
const identifier = userInfo[identifierKey !== null && identifierKey !== void 0 ? identifierKey : 'sub'] ? String(userInfo[identifierKey !== null && identifierKey !== void 0 ? identifierKey : 'sub']) : email;
|
|
115
115
|
if (!identifier) {
|
|
116
116
|
logger_1.default.warn(`[OpenID] Failed to find user identifier for provider "${provider}"`);
|
|
117
117
|
throw new exceptions_1.InvalidCredentialsException();
|
|
@@ -150,7 +150,7 @@ class OpenIDAuthDriver extends local_1.LocalAuthDriver {
|
|
|
150
150
|
let authData = user.auth_data;
|
|
151
151
|
if (typeof authData === 'string') {
|
|
152
152
|
try {
|
|
153
|
-
authData =
|
|
153
|
+
authData = (0, parse_json_1.parseJSON)(authData);
|
|
154
154
|
}
|
|
155
155
|
catch {
|
|
156
156
|
logger_1.default.warn(`[OpenID] Session data isn't valid JSON: ${authData}`);
|
|
@@ -260,6 +260,9 @@ function createOpenIDAuthRouter(providerName) {
|
|
|
260
260
|
else if (error instanceof exceptions_1.InvalidTokenException) {
|
|
261
261
|
reason = 'INVALID_TOKEN';
|
|
262
262
|
}
|
|
263
|
+
else if (error instanceof exceptions_1.InvalidProviderException) {
|
|
264
|
+
reason = 'INVALID_PROVIDER';
|
|
265
|
+
}
|
|
263
266
|
else {
|
|
264
267
|
logger_1.default.warn(error, `[OpenID] Unexpected error during OpenID login`);
|
|
265
268
|
}
|
|
@@ -28,12 +28,13 @@ const fs_1 = require("fs");
|
|
|
28
28
|
const inquirer_1 = __importDefault(require("inquirer"));
|
|
29
29
|
const js_yaml_1 = require("js-yaml");
|
|
30
30
|
const path_1 = __importDefault(require("path"));
|
|
31
|
+
const cache_1 = require("../../../cache");
|
|
31
32
|
const database_1 = __importStar(require("../../../database"));
|
|
32
33
|
const logger_1 = __importDefault(require("../../../logger"));
|
|
34
|
+
const apply_snapshot_1 = require("../../../utils/apply-snapshot");
|
|
33
35
|
const get_snapshot_1 = require("../../../utils/get-snapshot");
|
|
34
36
|
const get_snapshot_diff_1 = require("../../../utils/get-snapshot-diff");
|
|
35
|
-
const
|
|
36
|
-
const cache_1 = require("../../../cache");
|
|
37
|
+
const parse_json_1 = require("../../../utils/parse-json");
|
|
37
38
|
async function apply(snapshotPath, options) {
|
|
38
39
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
39
40
|
const filename = path_1.default.resolve(process.cwd(), snapshotPath);
|
|
@@ -52,7 +53,7 @@ async function apply(snapshotPath, options) {
|
|
|
52
53
|
snapshot = (await (0, js_yaml_1.load)(fileContents));
|
|
53
54
|
}
|
|
54
55
|
else {
|
|
55
|
-
snapshot =
|
|
56
|
+
snapshot = (0, parse_json_1.parseJSON)(fileContents);
|
|
56
57
|
}
|
|
57
58
|
const currentSnapshot = await (0, get_snapshot_1.getSnapshot)({ database });
|
|
58
59
|
const snapshotDiff = (0, get_snapshot_diff_1.getSnapshotDiff)(currentSnapshot, snapshot);
|
|
@@ -1,60 +1,317 @@
|
|
|
1
1
|
####################################################################################################
|
|
2
|
-
|
|
2
|
+
#
|
|
3
|
+
# These values set environment variables which modify core settings of Directus.
|
|
4
|
+
#
|
|
5
|
+
# Values in square brackets are the default values.
|
|
6
|
+
#
|
|
7
|
+
# The following options are not all possible options. For more, see
|
|
8
|
+
# https://docs.directus.io/configuration/config-options/
|
|
9
|
+
#
|
|
10
|
+
####################################################################################################
|
|
11
|
+
####################################################################################################
|
|
12
|
+
|
|
13
|
+
### General
|
|
3
14
|
|
|
15
|
+
# IP or host the API listens on ["0.0.0.0"]
|
|
4
16
|
HOST="0.0.0.0"
|
|
17
|
+
|
|
18
|
+
# The port Directus will run on [8055]
|
|
5
19
|
PORT=8055
|
|
20
|
+
|
|
21
|
+
# The URL where your API can be reached on the web. It is also used for things like OAuth redirects,
|
|
22
|
+
# forgot-password emails, and logos that needs to be publicly available on the internet. ["/"]
|
|
6
23
|
PUBLIC_URL="/"
|
|
24
|
+
# PUBLIC_URL="http://localhost:8055"
|
|
25
|
+
|
|
26
|
+
# What level of detail to log. [info]
|
|
27
|
+
# "fatal", "error", "warn", "info", "debug", "trace", "silent"
|
|
28
|
+
# LOG_LEVEL="info"
|
|
29
|
+
|
|
30
|
+
# Render the logs human readable (pretty) or as JSON (raw), [pretty]
|
|
31
|
+
# "pretty", "raw"
|
|
32
|
+
# LOG_STYLE="pretty"
|
|
33
|
+
|
|
34
|
+
# Controls the maximum request body size. Accepts number of bytes, or human readable string ["100kb"]
|
|
35
|
+
# MAX_PAYLOAD_SIZE="100kb"
|
|
36
|
+
|
|
37
|
+
# Where to redirect to when navigating to /. Accepts a relative path, absolute URL, or false to disable ["./admin"]
|
|
38
|
+
# ROOT_REDIRECT="./admin"
|
|
39
|
+
|
|
40
|
+
# Whether or not to serve the Admin App under /admin. [true]
|
|
41
|
+
# SERVE_APP=true
|
|
42
|
+
|
|
43
|
+
# Whether or not to enable GraphQL Introspection [true]
|
|
44
|
+
# GRAPHQL_INTROSPECTION=true
|
|
7
45
|
|
|
8
46
|
####################################################################################################
|
|
9
|
-
|
|
47
|
+
### Database
|
|
48
|
+
|
|
49
|
+
# All DB_* environment variables are passed to the connection configuration of a Knex instance.
|
|
50
|
+
# Based on your project's needs, you can extend the DB_* environment variables with any config
|
|
51
|
+
# you need to pass to the database instance.
|
|
10
52
|
|
|
11
53
|
{{ database }}
|
|
12
54
|
|
|
55
|
+
|
|
56
|
+
# These match the databases defined in the docker-compose file in the root of this repo
|
|
57
|
+
|
|
58
|
+
## Postgres
|
|
59
|
+
# DB_CLIENT="pg"
|
|
60
|
+
# DB_HOST="localhost"
|
|
61
|
+
# DB_PORT=5432
|
|
62
|
+
# DB_DATABASE="directus"
|
|
63
|
+
# DB_USER="postgres"
|
|
64
|
+
# DB_PASSWORD="secret"
|
|
65
|
+
|
|
66
|
+
## CockroachDB
|
|
67
|
+
# DB_CLIENT="cockroachdb"
|
|
68
|
+
# DB_HOST="localhost"
|
|
69
|
+
# DB_PORT=26257
|
|
70
|
+
# DB_DATABASE="directus"
|
|
71
|
+
# DB_USER="root"
|
|
72
|
+
# DB_PASSWORD=""
|
|
73
|
+
|
|
74
|
+
## MySQL 8
|
|
75
|
+
# DB_CLIENT="mysql"
|
|
76
|
+
# DB_HOST="localhost"
|
|
77
|
+
# DB_PORT=3306
|
|
78
|
+
# DB_DATABASE="directus"
|
|
79
|
+
# DB_USER="root"
|
|
80
|
+
# DB_PASSWORD="secret"
|
|
81
|
+
|
|
82
|
+
## MariaDB
|
|
83
|
+
# DB_CLIENT="mysql"
|
|
84
|
+
# DB_HOST="localhost"
|
|
85
|
+
# DB_PORT=3306
|
|
86
|
+
# DB_DATABASE="directus"
|
|
87
|
+
# DB_USER="root"
|
|
88
|
+
# DB_PASSWORD="secret"
|
|
89
|
+
|
|
90
|
+
## MS SQL
|
|
91
|
+
# DB_CLIENT="mssql"
|
|
92
|
+
# DB_HOST="localhost"
|
|
93
|
+
# DB_PORT=1343
|
|
94
|
+
# DB_DATABASE="directus"
|
|
95
|
+
# DB_USER="sa"
|
|
96
|
+
# DB_PASSWORD="Test@123"
|
|
97
|
+
|
|
98
|
+
## OracleDB
|
|
99
|
+
# DB_CLIENT="oracledb"
|
|
100
|
+
# DB_CONNECT_STRING="localhost:1521/XE"
|
|
101
|
+
# DB_USER="secretsysuser"
|
|
102
|
+
# DB_PASSWORD="secretpassword"
|
|
103
|
+
|
|
104
|
+
## SQLite Example
|
|
105
|
+
# DB_CLIENT="sqlite3"
|
|
106
|
+
# DB_FILENAME="./data.db"
|
|
107
|
+
|
|
108
|
+
## MySQL 5.7
|
|
109
|
+
# DB_CLIENT="mysql"
|
|
110
|
+
# DB_HOST="localhost"
|
|
111
|
+
# DB_PORT=3306
|
|
112
|
+
# DB_DATABASE="directus"
|
|
113
|
+
# DB_USER="root"
|
|
114
|
+
# DB_PASSWORD="secret"
|
|
115
|
+
|
|
13
116
|
####################################################################################################
|
|
14
|
-
|
|
117
|
+
### Rate Limiting
|
|
15
118
|
|
|
119
|
+
# Whether or not to enable rate limiting on the API [false]
|
|
16
120
|
RATE_LIMITER_ENABLED=false
|
|
121
|
+
|
|
122
|
+
# Where to store the rate limiter counts [memory]
|
|
123
|
+
# memory, redis, memcache
|
|
17
124
|
RATE_LIMITER_STORE=memory
|
|
125
|
+
# RATE_LIMITER_REDIS="redis://@127.0.0.1:5105"
|
|
126
|
+
# RATE_LIMITER_MEMCACHE="localhost:5109"
|
|
127
|
+
|
|
128
|
+
# The amount of allowed hits per duration [50]
|
|
18
129
|
RATE_LIMITER_POINTS=25
|
|
130
|
+
|
|
131
|
+
# The time window in seconds in which the hits are counted [1]
|
|
19
132
|
RATE_LIMITER_DURATION=1
|
|
20
133
|
|
|
21
134
|
####################################################################################################
|
|
22
|
-
|
|
135
|
+
### Caching
|
|
23
136
|
|
|
137
|
+
# Whether or not caching is enabled. [false]
|
|
24
138
|
CACHE_ENABLED=false
|
|
25
139
|
|
|
140
|
+
# How long the cache is persisted ["5m"]
|
|
141
|
+
# CACHE_TTL="30m"
|
|
142
|
+
|
|
143
|
+
# How to scope the cache data ["directus-cache"]
|
|
144
|
+
# CACHE_NAMESPACE="directus-cache"
|
|
145
|
+
|
|
146
|
+
# Automatically purge the cache on create, update, and delete actions. [false]
|
|
147
|
+
# CACHE_AUTO_PURGE=true
|
|
148
|
+
|
|
149
|
+
# memory | redis | memcache
|
|
150
|
+
CACHE_STORE=memory
|
|
151
|
+
|
|
152
|
+
# How long assets will be cached for in the browser. Sets the max-age value of the Cache-Control header ["30m"]
|
|
153
|
+
ASSETS_CACHE_TTL="30m"
|
|
154
|
+
|
|
155
|
+
# CACHE_REDIS="redis://@127.0.0.1:5105"
|
|
156
|
+
# CACHE_MEMCACHE="localhost:5109"
|
|
157
|
+
|
|
26
158
|
####################################################################################################
|
|
27
|
-
|
|
159
|
+
### File Storage
|
|
28
160
|
|
|
161
|
+
# A CSV of storage locations (eg: local,digitalocean,amazon) to use. You can use any names you'd like for these keys ["local"]
|
|
29
162
|
STORAGE_LOCATIONS="local"
|
|
30
163
|
STORAGE_LOCAL_DRIVER="local"
|
|
31
164
|
STORAGE_LOCAL_ROOT="./uploads"
|
|
32
165
|
|
|
166
|
+
## S3 Example (location name: DigitalOcean)
|
|
167
|
+
# STORAGE_DIGITALOCEAN_DRIVER="s3"
|
|
168
|
+
# STORAGE_DIGITALOCEAN_KEY="abcdef"
|
|
169
|
+
# STORAGE_DIGITALOCEAN_SECRET="ghijkl"
|
|
170
|
+
# STORAGE_DIGITALOCEAN_ENDPOINT="ams3.digitaloceanspaces.com"
|
|
171
|
+
# STORAGE_DIGITALOCEAN_BUCKET="my-files"
|
|
172
|
+
# STORAGE_DIGITALOCEAN_REGION="ams3"
|
|
173
|
+
|
|
174
|
+
## Google Cloud Storage Example (location name: Google)
|
|
175
|
+
# STORAGE_GOOGLE_DRIVER="gcs"
|
|
176
|
+
# STORAGE_GOOGLE_KEY_FILENAME="abcdef"
|
|
177
|
+
# STORAGE_GOOGLE_BUCKET="my-files"
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
## A comma-separated list of metadata keys to collect during file upload. Use * for all
|
|
181
|
+
# Extracting all metadata might cause memory issues when the file has an unusually large set of metadata
|
|
182
|
+
# [ifd0.Make,ifd0.Model,exif.FNumber,exif.ExposureTime,exif.FocalLength,exif.ISO]
|
|
183
|
+
# FILE_METADATA_ALLOW_LIST=
|
|
184
|
+
|
|
33
185
|
####################################################################################################
|
|
34
|
-
|
|
186
|
+
### Security
|
|
35
187
|
|
|
36
188
|
{{ security }}
|
|
37
189
|
|
|
190
|
+
# Unique identifier for the project
|
|
191
|
+
# KEY="xxxxxxx-xxxxxx-xxxxxxxx-xxxxxxxxxx"
|
|
192
|
+
|
|
193
|
+
# Secret string for the project
|
|
194
|
+
# SECRET="abcdef"
|
|
195
|
+
|
|
196
|
+
# The duration that the access token is valid ["15m"]
|
|
38
197
|
ACCESS_TOKEN_TTL="15m"
|
|
198
|
+
|
|
199
|
+
# The duration that the refresh token is valid, and also how long users stay logged-in to the App ["7d"]
|
|
39
200
|
REFRESH_TOKEN_TTL="7d"
|
|
201
|
+
|
|
202
|
+
# Whether or not to use a secure cookie for the refresh token in cookie mode [false]
|
|
40
203
|
REFRESH_TOKEN_COOKIE_SECURE=false
|
|
204
|
+
|
|
205
|
+
# Value for sameSite in the refresh token cookie when in cookie mode ["lax"]
|
|
41
206
|
REFRESH_TOKEN_COOKIE_SAME_SITE="lax"
|
|
207
|
+
|
|
208
|
+
# Name of refresh token cookie ["directus_refresh_token"]
|
|
42
209
|
REFRESH_TOKEN_COOKIE_NAME="directus_refresh_token"
|
|
43
210
|
|
|
211
|
+
# Which domain to use for the refresh cookie. Useful for development mode.
|
|
212
|
+
# REFRESH_TOKEN_COOKIE_DOMAIN
|
|
213
|
+
|
|
214
|
+
# Whether or not to enable the CORS headers [false]
|
|
215
|
+
CORS_ENABLED=true
|
|
216
|
+
|
|
217
|
+
# Value for the Access-Control-Allow-Origin header. Use true to match the Origin header, or provide a domain or a CSV of domains for specific access [false]
|
|
218
|
+
CORS_ORIGIN=true
|
|
219
|
+
|
|
220
|
+
# Value for the Access-Control-Allow-Methods header [GET,POST,PATCH,DELETE]
|
|
221
|
+
CORS_METHODS=GET,POST,PATCH,DELETE
|
|
222
|
+
|
|
223
|
+
# Value for the Access-Control-Allow-Headers header [Content-Type,Authorization]
|
|
224
|
+
CORS_ALLOWED_HEADERS=Content-Type,Authorization
|
|
225
|
+
|
|
226
|
+
# Value for the Access-Control-Expose-Headers header [Content-R
|
|
227
|
+
CORS_EXPOSED_HEADERS=Content-Range
|
|
228
|
+
|
|
229
|
+
# Whether or not to send the Access-Control-Allow-Credentials header [true]
|
|
230
|
+
CORS_CREDENTIALS=true
|
|
231
|
+
|
|
232
|
+
# Value for the Access-Control-Max-Age header [18000]
|
|
233
|
+
CORS_MAX_AGE=18000
|
|
234
|
+
|
|
235
|
+
####################################################################################################
|
|
236
|
+
### Argon2
|
|
237
|
+
|
|
238
|
+
# How much memory to use when generating hashes, in KiB [4096]
|
|
239
|
+
# HASH_MEMORY_COST=81920
|
|
240
|
+
|
|
241
|
+
# The length of the hash function output in bytes [32]
|
|
242
|
+
# HASH_HASH_LENGTH=32
|
|
243
|
+
|
|
244
|
+
# The amount of passes (iterations) used by the hash function. It increases hash strength at the cost of time required to compute [3]
|
|
245
|
+
# HASH_TIME_COST=10
|
|
246
|
+
|
|
247
|
+
# The amount of threads to compute the hash on. Each thread has a memory pool with HASH_MEMORY_COST size [1]
|
|
248
|
+
# HASH_PARALLELISM=2
|
|
249
|
+
|
|
250
|
+
# The variant of the hash function (0: argon2d, 1: argon2i, or 2: argon2id) [1]
|
|
251
|
+
# HASH_TYPE=2
|
|
252
|
+
|
|
253
|
+
An extra and optional non-secret value. The value will be included B64 encoded in the parameters portion of the digest []
|
|
254
|
+
# HASH_ASSOCIATED_DATA=foo
|
|
255
|
+
|
|
44
256
|
####################################################################################################
|
|
45
|
-
|
|
257
|
+
### Auth Providers
|
|
46
258
|
|
|
259
|
+
# A comma-separated list of auth providers []
|
|
47
260
|
AUTH_PROVIDERS=""
|
|
261
|
+
# AUTH_PROVIDERS="github"
|
|
262
|
+
|
|
263
|
+
# AUTH_GITHUB_DRIVER="oauth2"
|
|
264
|
+
# AUTH_GITHUB_CLIENT_ID="73e...4b"
|
|
265
|
+
# AUTH_GITHUB_CLIENT_SECRET="b9...98"
|
|
266
|
+
# AUTH_GITHUB_AUTHORIZE_URL="https://github.com/login/oauth/authorize"
|
|
267
|
+
# AUTH_GITHUB_ACCESS_URL="https://github.com/login/oauth/access_token"
|
|
268
|
+
# AUTH_GITHUB_PROFILE_URL="https://api.github.com/user"
|
|
269
|
+
# AUTH_GITHUB_ALLOW_PUBLIC_REGISTRATION=true
|
|
270
|
+
# AUTH_GITHUB_DEFAULT_ROLE_ID="82424427-c9d4-4289-8bc5-ed1bf8422c90"
|
|
271
|
+
# AUTH_GITHUB_ICON="github"
|
|
272
|
+
# AUTH_GITHUB_EMAIL_KEY="email"
|
|
273
|
+
# AUTH_GITHUB_IDENTIFIER_KEY="login"
|
|
48
274
|
|
|
49
275
|
####################################################################################################
|
|
50
|
-
|
|
276
|
+
### Extensions
|
|
51
277
|
|
|
278
|
+
# Path to your local extensions folder ["./extensions"]
|
|
52
279
|
EXTENSIONS_PATH="./extensions"
|
|
53
280
|
|
|
281
|
+
# Automatically reload extensions when they have changed [false]
|
|
282
|
+
EXTENSIONS_AUTO_RELOAD=false
|
|
283
|
+
|
|
54
284
|
####################################################################################################
|
|
55
|
-
|
|
285
|
+
### Email
|
|
56
286
|
|
|
287
|
+
# Email address from which emails are sent ["no-reply@directus.io"]
|
|
57
288
|
EMAIL_FROM="no-reply@directus.io"
|
|
289
|
+
|
|
290
|
+
# What to use to send emails. One of
|
|
291
|
+
# sendmail, smtp, mailgun, ses.
|
|
58
292
|
EMAIL_TRANSPORT="sendmail"
|
|
59
293
|
EMAIL_SENDMAIL_NEW_LINE="unix"
|
|
60
294
|
EMAIL_SENDMAIL_PATH="/usr/sbin/sendmail"
|
|
295
|
+
|
|
296
|
+
## Email (Sendmail Transport)
|
|
297
|
+
|
|
298
|
+
# What new line style to use in sendmail ["unix"]
|
|
299
|
+
EMAIL_SENDMAIL_NEW_LINE="unix"
|
|
300
|
+
|
|
301
|
+
# Path to your sendmail executable ["/usr/sbin/sendmail"]
|
|
302
|
+
EMAIL_SENDMAIL_PATH="/usr/sbin/sendmail"
|
|
303
|
+
|
|
304
|
+
## Email (SMTP Transport)
|
|
305
|
+
# EMAIL_SMTP_HOST="localhost"
|
|
306
|
+
|
|
307
|
+
# Use SMTP pooling
|
|
308
|
+
# EMAIL_SMTP_POOL=true
|
|
309
|
+
# EMAIL_SMTP_PORT=465
|
|
310
|
+
# EMAIL_SMTP_SECURE=false # Use TLS
|
|
311
|
+
# EMAIL_SMTP_IGNORE_TLS=false
|
|
312
|
+
# EMAIL_SMTP_USER="username"
|
|
313
|
+
# EMAIL_SMTP_PASSWORD="password"
|
|
314
|
+
|
|
315
|
+
## Email (Mailgun Transport)
|
|
316
|
+
# EMAIL_MAILGUN_API_KEY="key-1234123412341234"
|
|
317
|
+
# EMAIL_MAILGUN_DOMAIN="a domain name from https://app.mailgun.com/app/sending/domains"
|
|
@@ -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 types_1 = require("@directus/shared/types");
|
|
6
7
|
const express_1 = __importDefault(require("express"));
|
|
7
8
|
const joi_1 = __importDefault(require("joi"));
|
|
8
9
|
const exceptions_1 = require("../exceptions");
|
|
@@ -10,7 +11,6 @@ const respond_1 = require("../middleware/respond");
|
|
|
10
11
|
const use_collection_1 = __importDefault(require("../middleware/use-collection"));
|
|
11
12
|
const validate_batch_1 = require("../middleware/validate-batch");
|
|
12
13
|
const services_1 = require("../services");
|
|
13
|
-
const types_1 = require("../types");
|
|
14
14
|
const async_handler_1 = __importDefault(require("../utils/async-handler"));
|
|
15
15
|
const get_ip_from_req_1 = require("../utils/get-ip-from-req");
|
|
16
16
|
const router = express_1.default.Router();
|
|
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const express_1 = require("express");
|
|
7
|
+
const helmet_1 = __importDefault(require("helmet"));
|
|
7
8
|
const lodash_1 = require("lodash");
|
|
8
9
|
const ms_1 = __importDefault(require("ms"));
|
|
9
10
|
const constants_1 = require("../constants");
|
|
@@ -14,9 +15,8 @@ const use_collection_1 = __importDefault(require("../middleware/use-collection")
|
|
|
14
15
|
const services_1 = require("../services");
|
|
15
16
|
const assets_1 = require("../types/assets");
|
|
16
17
|
const async_handler_1 = __importDefault(require("../utils/async-handler"));
|
|
17
|
-
const helmet_1 = __importDefault(require("helmet"));
|
|
18
|
-
const lodash_2 = require("lodash");
|
|
19
18
|
const get_config_from_env_1 = require("../utils/get-config-from-env");
|
|
19
|
+
const parse_json_1 = require("../utils/parse-json");
|
|
20
20
|
const router = (0, express_1.Router)();
|
|
21
21
|
router.use((0, use_collection_1.default)('directus_files'));
|
|
22
22
|
router.get('/:pk',
|
|
@@ -41,7 +41,7 @@ router.get('/:pk',
|
|
|
41
41
|
let transforms;
|
|
42
42
|
// Try parse the JSON array
|
|
43
43
|
try {
|
|
44
|
-
transforms =
|
|
44
|
+
transforms = (0, parse_json_1.parseJSON)(transformation['transforms']);
|
|
45
45
|
}
|
|
46
46
|
catch {
|
|
47
47
|
throw new exceptions_1.InvalidQueryException(`"transforms" Parameter needs to be a JSON array of allowed transformations.`);
|
|
@@ -91,7 +91,7 @@ router.get('/:pk',
|
|
|
91
91
|
return next();
|
|
92
92
|
throw new exceptions_1.InvalidQueryException(`Dynamic asset generation has been disabled for this project.`);
|
|
93
93
|
}
|
|
94
|
-
}), helmet_1.default.contentSecurityPolicy((0,
|
|
94
|
+
}), helmet_1.default.contentSecurityPolicy((0, lodash_1.merge)({
|
|
95
95
|
useDefaults: false,
|
|
96
96
|
directives: {
|
|
97
97
|
defaultSrc: ['none'],
|
|
@@ -110,11 +110,10 @@ router.get('/:pk',
|
|
|
110
110
|
: res.locals.transformation;
|
|
111
111
|
let range = undefined;
|
|
112
112
|
if (req.headers.range) {
|
|
113
|
-
|
|
114
|
-
const rangeParts = req.headers.range.substring(6).split('-');
|
|
113
|
+
const rangeParts = /bytes=([0-9]*)-([0-9]*)/.exec(req.headers.range);
|
|
115
114
|
range = {
|
|
116
|
-
start: rangeParts[
|
|
117
|
-
end: rangeParts[
|
|
115
|
+
start: (rangeParts === null || rangeParts === void 0 ? void 0 : rangeParts[1]) ? Number(rangeParts[1]) : undefined,
|
|
116
|
+
end: (rangeParts === null || rangeParts === void 0 ? void 0 : rangeParts[2]) ? Number(rangeParts[2]) : undefined,
|
|
118
117
|
};
|
|
119
118
|
if (Number.isNaN(range.start) || Number.isNaN(range.end)) {
|
|
120
119
|
throw new exceptions_1.RangeNotSatisfiableException(range);
|
|
@@ -134,7 +133,7 @@ router.get('/:pk',
|
|
|
134
133
|
if (range) {
|
|
135
134
|
res.setHeader('Content-Range', `bytes ${range.start}-${range.end || stat.size - 1}/${stat.size}`);
|
|
136
135
|
res.status(206);
|
|
137
|
-
res.setHeader('Content-Length', (range.end ? range.end + 1 : stat.size) - range.start);
|
|
136
|
+
res.setHeader('Content-Length', (range.end ? range.end + 1 : stat.size) - (range.start || 0));
|
|
138
137
|
}
|
|
139
138
|
else {
|
|
140
139
|
res.setHeader('Content-Length', stat.size);
|