directus

Форк
0
131 строка · 3.8 Кб
1
import { InvalidPayloadError, UnsupportedMediaTypeError } from '@directus/errors';
2
import { parseJSON } from '@directus/utils';
3
import Busboy from 'busboy';
4
import type { RequestHandler } from 'express';
5
import express from 'express';
6
import { load as loadYaml } from 'js-yaml';
7
import { useLogger } from '../logger.js';
8
import { respond } from '../middleware/respond.js';
9
import { SchemaService } from '../services/schema.js';
10
import type { Snapshot, SnapshotDiffWithHash } from '../types/index.js';
11
import asyncHandler from '../utils/async-handler.js';
12
import { getVersionedHash } from '../utils/get-versioned-hash.js';
13

14
const router = express.Router();
15

16
router.get(
17
	'/snapshot',
18
	asyncHandler(async (req, res, next) => {
19
		const service = new SchemaService({ accountability: req.accountability });
20
		const currentSnapshot = await service.snapshot();
21
		res.locals['payload'] = { data: currentSnapshot };
22
		return next();
23
	}),
24
	respond,
25
);
26

27
const schemaMultipartHandler: RequestHandler = (req, res, next) => {
28
	if (req.is('application/json')) {
29
		if (Object.keys(req.body).length === 0) {
30
			throw new InvalidPayloadError({ reason: `No data was included in the body` });
31
		}
32

33
		res.locals['upload'] = req.body;
34
		return next();
35
	}
36

37
	if (!req.is('multipart/form-data')) {
38
		throw new UnsupportedMediaTypeError({ mediaType: req.headers['content-type']!, where: 'Content-Type header' });
39
	}
40

41
	const headers = req.headers['content-type']
42
		? req.headers
43
		: {
44
				...req.headers,
45
				'content-type': 'application/octet-stream',
46
		  };
47

48
	const busboy = Busboy({ headers });
49

50
	let isFileIncluded = false;
51
	let upload: any | null = null;
52

53
	busboy.on('file', async (_, fileStream, { mimeType }) => {
54
		const logger = useLogger();
55

56
		if (isFileIncluded) return next(new InvalidPayloadError({ reason: `More than one file was included in the body` }));
57

58
		isFileIncluded = true;
59

60
		const { readableStreamToString } = await import('@directus/utils/node');
61

62
		try {
63
			const uploadedString = await readableStreamToString(fileStream);
64

65
			if (mimeType === 'application/json') {
66
				try {
67
					upload = parseJSON(uploadedString);
68
				} catch (err: any) {
69
					logger.warn(err);
70
					throw new InvalidPayloadError({ reason: 'The provided JSON is invalid' });
71
				}
72
			} else {
73
				try {
74
					upload = await loadYaml(uploadedString);
75
				} catch (err: any) {
76
					logger.warn(err);
77
					throw new InvalidPayloadError({ reason: 'The provided YAML is invalid' });
78
				}
79
			}
80

81
			if (!upload) {
82
				throw new InvalidPayloadError({ reason: `No file was included in the body` });
83
			}
84

85
			res.locals['upload'] = upload;
86

87
			return next();
88
		} catch (error: any) {
89
			busboy.emit('error', error);
90
		}
91
	});
92

93
	busboy.on('error', (error: Error) => next(error));
94

95
	busboy.on('close', () => {
96
		if (!isFileIncluded) return next(new InvalidPayloadError({ reason: `No file was included in the body` }));
97
	});
98

99
	req.pipe(busboy);
100
};
101

102
router.post(
103
	'/diff',
104
	asyncHandler(schemaMultipartHandler),
105
	asyncHandler(async (req, res, next) => {
106
		const service = new SchemaService({ accountability: req.accountability });
107
		const snapshot: Snapshot = res.locals['upload'];
108
		const currentSnapshot = await service.snapshot();
109
		const snapshotDiff = await service.diff(snapshot, { currentSnapshot, force: 'force' in req.query });
110
		if (!snapshotDiff) return next();
111

112
		const currentSnapshotHash = getVersionedHash(currentSnapshot);
113
		res.locals['payload'] = { data: { hash: currentSnapshotHash, diff: snapshotDiff } };
114
		return next();
115
	}),
116
	respond,
117
);
118

119
router.post(
120
	'/apply',
121
	asyncHandler(schemaMultipartHandler),
122
	asyncHandler(async (req, res, next) => {
123
		const service = new SchemaService({ accountability: req.accountability });
124
		const diff: SnapshotDiffWithHash = res.locals['upload'];
125
		await service.apply(diff);
126
		return next();
127
	}),
128
	respond,
129
);
130

131
export default router;
132

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.