directus

Форк
0
188 строк · 4.6 Кб
1
import { InvalidPayloadError, InvalidQueryError, UnsupportedMediaTypeError } from '@directus/errors';
2
import argon2 from 'argon2';
3
import Busboy from 'busboy';
4
import { Router } from 'express';
5
import Joi from 'joi';
6
import collectionExists from '../middleware/collection-exists.js';
7
import { respond } from '../middleware/respond.js';
8
import { ExportService, ImportService } from '../services/import-export.js';
9
import { RevisionsService } from '../services/revisions.js';
10
import { UtilsService } from '../services/utils.js';
11
import asyncHandler from '../utils/async-handler.js';
12
import { generateHash } from '../utils/generate-hash.js';
13
import { sanitizeQuery } from '../utils/sanitize-query.js';
14

15
const router = Router();
16

17
const randomStringSchema = Joi.object<{ length: number }>({
18
	length: Joi.number().integer().min(1).max(500).default(32),
19
});
20

21
router.get(
22
	'/random/string',
23
	asyncHandler(async (req, res) => {
24
		const { nanoid } = await import('nanoid');
25

26
		const { error, value } = randomStringSchema.validate(req.query, { allowUnknown: true });
27

28
		if (error) throw new InvalidQueryError({ reason: error.message });
29

30
		return res.json({ data: nanoid(value.length) });
31
	}),
32
);
33

34
router.post(
35
	'/hash/generate',
36
	asyncHandler(async (req, res) => {
37
		if (!req.body?.string) {
38
			throw new InvalidPayloadError({ reason: `"string" is required` });
39
		}
40

41
		const hash = await generateHash(req.body.string);
42

43
		return res.json({ data: hash });
44
	}),
45
);
46

47
router.post(
48
	'/hash/verify',
49
	asyncHandler(async (req, res) => {
50
		if (!req.body?.string) {
51
			throw new InvalidPayloadError({ reason: `"string" is required` });
52
		}
53

54
		if (!req.body?.hash) {
55
			throw new InvalidPayloadError({ reason: `"hash" is required` });
56
		}
57

58
		const result = await argon2.verify(req.body.hash, req.body.string);
59

60
		return res.json({ data: result });
61
	}),
62
);
63

64
const SortSchema = Joi.object({
65
	item: Joi.alternatives(Joi.string(), Joi.number()).required(),
66
	to: Joi.alternatives(Joi.string(), Joi.number()).required(),
67
});
68

69
router.post(
70
	'/sort/:collection',
71
	collectionExists,
72
	asyncHandler(async (req, res) => {
73
		const { error } = SortSchema.validate(req.body);
74
		if (error) throw new InvalidPayloadError({ reason: error.message });
75

76
		const service = new UtilsService({
77
			accountability: req.accountability,
78
			schema: req.schema,
79
		});
80

81
		await service.sort(req.collection, req.body);
82

83
		return res.status(200).end();
84
	}),
85
);
86

87
router.post(
88
	'/revert/:revision',
89
	asyncHandler(async (req, _res, next) => {
90
		const service = new RevisionsService({
91
			accountability: req.accountability,
92
			schema: req.schema,
93
		});
94

95
		await service.revert(req.params['revision']!);
96
		next();
97
	}),
98
	respond,
99
);
100

101
router.post(
102
	'/import/:collection',
103
	collectionExists,
104
	asyncHandler(async (req, res, next) => {
105
		if (req.is('multipart/form-data') === false) {
106
			throw new UnsupportedMediaTypeError({ mediaType: req.headers['content-type']!, where: 'Content-Type header' });
107
		}
108

109
		const service = new ImportService({
110
			accountability: req.accountability,
111
			schema: req.schema,
112
		});
113

114
		let headers;
115

116
		if (req.headers['content-type']) {
117
			headers = req.headers;
118
		} else {
119
			headers = {
120
				...req.headers,
121
				'content-type': 'application/octet-stream',
122
			};
123
		}
124

125
		const busboy = Busboy({ headers });
126

127
		busboy.on('file', async (_fieldname, fileStream, { mimeType }) => {
128
			try {
129
				await service.import(req.params['collection']!, mimeType, fileStream);
130
			} catch (err: any) {
131
				return next(err);
132
			}
133

134
			return res.status(200).end();
135
		});
136

137
		busboy.on('error', (err: Error) => next(err));
138

139
		req.pipe(busboy);
140
	}),
141
);
142

143
router.post(
144
	'/export/:collection',
145
	collectionExists,
146
	asyncHandler(async (req, _res, next) => {
147
		if (!req.body.query) {
148
			throw new InvalidPayloadError({ reason: `"query" is required` });
149
		}
150

151
		if (!req.body.format) {
152
			throw new InvalidPayloadError({ reason: `"format" is required` });
153
		}
154

155
		const service = new ExportService({
156
			accountability: req.accountability,
157
			schema: req.schema,
158
		});
159

160
		const sanitizedQuery = sanitizeQuery(req.body.query, req.accountability ?? null);
161

162
		// We're not awaiting this, as it's supposed to run async in the background
163
		service.exportToFile(req.params['collection']!, sanitizedQuery, req.body.format, {
164
			file: req.body.file,
165
		});
166

167
		return next();
168
	}),
169
	respond,
170
);
171

172
router.post(
173
	'/cache/clear',
174
	asyncHandler(async (req, res) => {
175
		const service = new UtilsService({
176
			accountability: req.accountability,
177
			schema: req.schema,
178
		});
179

180
		const clearSystemCache = 'system' in req.query && (req.query['system'] === '' || Boolean(req.query['system']));
181

182
		await service.clearCache({ system: clearSystemCache });
183

184
		res.status(200).end();
185
	}),
186
);
187

188
export default router;
189

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

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

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

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