@kollors/deep-json-server 0.7.0 → 0.8.1
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/README.md +19 -16
- package/README.ru.md +20 -17
- package/dist/bin/deep-json-server.d.ts +2 -0
- package/dist/bin/deep-json-server.js +11 -0
- package/dist/bin/deep-json-server.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/src/cli.d.ts +4 -0
- package/dist/src/cli.js +80 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/config.d.ts +54 -0
- package/dist/src/config.js +145 -0
- package/dist/src/config.js.map +1 -0
- package/{src → dist/src}/constants.js +1 -0
- package/dist/src/constants.js.map +1 -0
- package/dist/src/database.d.ts +20 -0
- package/dist/src/database.js +136 -0
- package/dist/src/database.js.map +1 -0
- package/dist/src/files/contract.d.ts +67 -0
- package/dist/src/files/contract.js +106 -0
- package/dist/src/files/contract.js.map +1 -0
- package/dist/src/files/disk-store.d.ts +5 -0
- package/dist/src/files/disk-store.js +313 -0
- package/dist/src/files/disk-store.js.map +1 -0
- package/dist/src/files/index.d.ts +5 -0
- package/dist/src/files/index.js +6 -0
- package/dist/src/files/index.js.map +1 -0
- package/dist/src/files/memory-store.d.ts +3 -0
- package/dist/src/files/memory-store.js +80 -0
- package/dist/src/files/memory-store.js.map +1 -0
- package/dist/src/files/routes.d.ts +6 -0
- package/dist/src/files/routes.js +77 -0
- package/dist/src/files/routes.js.map +1 -0
- package/dist/src/openapi/config.d.ts +11 -0
- package/dist/src/openapi/config.js +204 -0
- package/dist/src/openapi/config.js.map +1 -0
- package/dist/src/openapi/document.d.ts +9 -0
- package/dist/src/openapi/document.js +338 -0
- package/dist/src/openapi/document.js.map +1 -0
- package/dist/src/openapi/index.d.ts +10 -0
- package/dist/src/openapi/index.js +27 -0
- package/dist/src/openapi/index.js.map +1 -0
- package/dist/src/openapi/inference.d.ts +14 -0
- package/dist/src/openapi/inference.js +145 -0
- package/dist/src/openapi/inference.js.map +1 -0
- package/dist/src/query/filter.d.ts +6 -0
- package/dist/src/query/filter.js +248 -0
- package/dist/src/query/filter.js.map +1 -0
- package/{src → dist/src}/query/index.js +1 -0
- package/dist/src/query/index.js.map +1 -0
- package/dist/src/query/pagination.d.ts +11 -0
- package/dist/src/query/pagination.js +28 -0
- package/dist/src/query/pagination.js.map +1 -0
- package/dist/src/query/sort.d.ts +1 -0
- package/dist/src/query/sort.js +54 -0
- package/dist/src/query/sort.js.map +1 -0
- package/dist/src/relation-metadata.d.ts +10 -0
- package/dist/src/relation-metadata.js +32 -0
- package/dist/src/relation-metadata.js.map +1 -0
- package/dist/src/relations.d.ts +12 -0
- package/dist/src/relations.js +155 -0
- package/dist/src/relations.js.map +1 -0
- package/dist/src/server.d.ts +13 -0
- package/dist/src/server.js +188 -0
- package/dist/src/server.js.map +1 -0
- package/dist/src/types.d.ts +52 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/utils.d.ts +20 -0
- package/dist/src/utils.js +63 -0
- package/dist/src/utils.js.map +1 -0
- package/package.json +17 -16
- package/bin/deep-json-server.js +0 -11
- package/index.js +0 -12
- package/src/cli.js +0 -91
- package/src/config.js +0 -203
- package/src/database.js +0 -148
- package/src/files.js +0 -633
- package/src/openapi/config.js +0 -124
- package/src/openapi/document.js +0 -394
- package/src/openapi/index.js +0 -36
- package/src/openapi/inference.js +0 -187
- package/src/query/filter.js +0 -283
- package/src/query/pagination.js +0 -44
- package/src/query/sort.js +0 -63
- package/src/relation-metadata.js +0 -41
- package/src/relations.js +0 -173
- package/src/server.js +0 -250
- package/src/utils.js +0 -84
- package/types/index.d.ts +0 -21
- package/types/src/config.d.ts +0 -73
- package/types/src/database.d.ts +0 -33
- package/types/src/files.d.ts +0 -64
- package/types/src/openapi/config.d.ts +0 -3
- package/types/src/openapi/document.d.ts +0 -11
- package/types/src/openapi/index.d.ts +0 -9
- package/types/src/openapi/inference.d.ts +0 -9
- package/types/src/query/filter.d.ts +0 -3
- package/types/src/query/pagination.d.ts +0 -8
- package/types/src/query/sort.d.ts +0 -1
- package/types/src/relation-metadata.d.ts +0 -9
- package/types/src/relations.d.ts +0 -3
- package/types/src/server.d.ts +0 -14
- package/types/src/utils.d.ts +0 -18
- /package/{types → dist}/src/constants.d.ts +0 -0
- /package/{types → dist}/src/query/index.d.ts +0 -0
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
import { randomBytes } from 'node:crypto';
|
|
2
|
+
import { createWriteStream } from 'node:fs';
|
|
3
|
+
import { access, lstat, mkdir, open, readFile, realpath, rename, rm, stat, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { dirname, isAbsolute, relative, resolve, sep } from 'node:path';
|
|
5
|
+
import { Transform } from 'node:stream';
|
|
6
|
+
import { pipeline } from 'node:stream/promises';
|
|
7
|
+
import { createHttpError, createSerialQueue, isSystemError } from '../utils.js';
|
|
8
|
+
import { getFileKey, normalizeStoredFileMetadata } from './contract.js';
|
|
9
|
+
const createSizeLimiter = (maxFileSize, onSize) => {
|
|
10
|
+
let size = 0;
|
|
11
|
+
return new Transform({
|
|
12
|
+
transform(chunk, _encoding, callback) {
|
|
13
|
+
size += chunk.length;
|
|
14
|
+
if (size > maxFileSize) {
|
|
15
|
+
callback(createHttpError(413, `Размер файла не должен превышать ${maxFileSize} байт`));
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
onSize(size);
|
|
19
|
+
callback(null, chunk);
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
const pathExists = async (path) => {
|
|
24
|
+
try {
|
|
25
|
+
await access(path);
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
if (isSystemError(error) && error.code === 'ENOENT') {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
const isPathInside = (rootPath, targetPath) => {
|
|
36
|
+
const relativePath = relative(rootPath, targetPath);
|
|
37
|
+
return relativePath === '' || (relativePath !== '..' && !relativePath.startsWith(`..${sep}`) && !isAbsolute(relativePath));
|
|
38
|
+
};
|
|
39
|
+
const readMetadata = async (metadataPath) => {
|
|
40
|
+
let source;
|
|
41
|
+
try {
|
|
42
|
+
source = JSON.parse(await readFile(metadataPath, 'utf8'));
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
if (isSystemError(error) && error.code === 'ENOENT') {
|
|
46
|
+
return new Map();
|
|
47
|
+
}
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
if (!Array.isArray(source)) {
|
|
51
|
+
throw new Error(`Файл метаданных ${metadataPath} должен содержать JSON-массив`);
|
|
52
|
+
}
|
|
53
|
+
const files = new Map();
|
|
54
|
+
source.forEach((value, index) => {
|
|
55
|
+
const file = normalizeStoredFileMetadata(value, `Запись ${index} в файле метаданных ${metadataPath}`);
|
|
56
|
+
const path = getFileKey(file);
|
|
57
|
+
if (files.has(path)) {
|
|
58
|
+
throw new Error(`Файл метаданных ${metadataPath} содержит повторяющийся путь «${path}»`);
|
|
59
|
+
}
|
|
60
|
+
files.set(path, file);
|
|
61
|
+
});
|
|
62
|
+
return files;
|
|
63
|
+
};
|
|
64
|
+
const writeMetadata = async (metadataPath, files) => {
|
|
65
|
+
const temporaryPath = `${metadataPath}.${randomBytes(6).toString('hex')}.tmp`;
|
|
66
|
+
try {
|
|
67
|
+
await writeFile(temporaryPath, JSON.stringify([...files.values()], null, 2), { encoding: 'utf8', flag: 'wx' });
|
|
68
|
+
await rename(temporaryPath, metadataPath);
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
await rm(temporaryPath, { force: true }).catch(() => undefined);
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
const getFileSize = async (path) => {
|
|
76
|
+
try {
|
|
77
|
+
return (await stat(path)).size;
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
if (isSystemError(error) && error.code === 'ENOENT') {
|
|
81
|
+
throw createHttpError(404, 'Файл не найден');
|
|
82
|
+
}
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
export const createDiskFileStore = async ({ directory: sourceDirectoryPath, metadata: sourceMetadataPath }) => {
|
|
87
|
+
const directoryPath = resolve(sourceDirectoryPath);
|
|
88
|
+
const metadataPath = resolve(sourceMetadataPath);
|
|
89
|
+
const stagingPath = resolve(directoryPath, '.deep-json-server');
|
|
90
|
+
const schedule = createSerialQueue();
|
|
91
|
+
const pendingCleanup = new Set();
|
|
92
|
+
await Promise.all([mkdir(directoryPath, { recursive: true }), mkdir(dirname(metadataPath), { recursive: true }), mkdir(stagingPath, { recursive: true })]);
|
|
93
|
+
const realDirectoryPath = await realpath(directoryPath);
|
|
94
|
+
let files = await readMetadata(metadataPath);
|
|
95
|
+
const commitMetadata = async (nextFiles, rollback, rollbackMessage) => {
|
|
96
|
+
try {
|
|
97
|
+
await writeMetadata(metadataPath, nextFiles);
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
try {
|
|
101
|
+
await rollback();
|
|
102
|
+
}
|
|
103
|
+
catch (rollbackError) {
|
|
104
|
+
throw new AggregateError([error, rollbackError], rollbackMessage);
|
|
105
|
+
}
|
|
106
|
+
throw error;
|
|
107
|
+
}
|
|
108
|
+
files = nextFiles;
|
|
109
|
+
};
|
|
110
|
+
const assertContained = (path) => {
|
|
111
|
+
if (!isPathInside(realDirectoryPath, path)) {
|
|
112
|
+
throw createHttpError(400, 'Путь файла выходит за пределы директории хранения');
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
const resolveFilePath = (path) => {
|
|
116
|
+
const filePath = resolve(directoryPath, path);
|
|
117
|
+
if (filePath === directoryPath || !isPathInside(directoryPath, filePath) || filePath === metadataPath || isPathInside(stagingPath, filePath)) {
|
|
118
|
+
throw createHttpError(400, 'Путь файла выходит за пределы директории хранения');
|
|
119
|
+
}
|
|
120
|
+
return filePath;
|
|
121
|
+
};
|
|
122
|
+
const assertNoSymlinks = async (targetPath) => {
|
|
123
|
+
const relativePath = relative(directoryPath, targetPath);
|
|
124
|
+
const parts = relativePath === '' ? [] : relativePath.split(sep);
|
|
125
|
+
let currentPath = directoryPath;
|
|
126
|
+
for (const part of parts) {
|
|
127
|
+
currentPath = resolve(currentPath, part);
|
|
128
|
+
try {
|
|
129
|
+
if ((await lstat(currentPath)).isSymbolicLink()) {
|
|
130
|
+
throw createHttpError(400, 'Путь файла не должен содержать символические ссылки');
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
if (!isSystemError(error) || error.code !== 'ENOENT') {
|
|
135
|
+
throw error;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
const prepareTargetPath = async (path) => {
|
|
141
|
+
const filePath = resolveFilePath(path);
|
|
142
|
+
const targetDirectory = dirname(filePath);
|
|
143
|
+
const parts = relative(directoryPath, targetDirectory).split(sep).filter(Boolean);
|
|
144
|
+
let currentPath = directoryPath;
|
|
145
|
+
for (const part of parts) {
|
|
146
|
+
currentPath = resolve(currentPath, part);
|
|
147
|
+
try {
|
|
148
|
+
await mkdir(currentPath);
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
if (!isSystemError(error) || error.code !== 'EEXIST') {
|
|
152
|
+
throw error;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
const currentStats = await lstat(currentPath);
|
|
156
|
+
if (currentStats.isSymbolicLink() || !currentStats.isDirectory()) {
|
|
157
|
+
throw createHttpError(400, 'Путь файла должен содержать только обычные директории');
|
|
158
|
+
}
|
|
159
|
+
assertContained(await realpath(currentPath));
|
|
160
|
+
}
|
|
161
|
+
if (await pathExists(filePath)) {
|
|
162
|
+
await assertNoSymlinks(filePath);
|
|
163
|
+
assertContained(await realpath(filePath));
|
|
164
|
+
}
|
|
165
|
+
return filePath;
|
|
166
|
+
};
|
|
167
|
+
const resolveExistingPath = async (path) => {
|
|
168
|
+
const filePath = resolveFilePath(path);
|
|
169
|
+
try {
|
|
170
|
+
await assertNoSymlinks(filePath);
|
|
171
|
+
assertContained(await realpath(filePath));
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
if (isSystemError(error) && error.code === 'ENOENT') {
|
|
175
|
+
throw createHttpError(404, 'Файл не найден');
|
|
176
|
+
}
|
|
177
|
+
throw error;
|
|
178
|
+
}
|
|
179
|
+
return filePath;
|
|
180
|
+
};
|
|
181
|
+
const cleanupLater = async (path) => {
|
|
182
|
+
try {
|
|
183
|
+
await rm(path, { force: true });
|
|
184
|
+
pendingCleanup.delete(path);
|
|
185
|
+
}
|
|
186
|
+
catch {
|
|
187
|
+
pendingCleanup.add(path);
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
const flushCleanup = async () => {
|
|
191
|
+
await Promise.all([...pendingCleanup].map(cleanupLater));
|
|
192
|
+
};
|
|
193
|
+
const findFile = (path) => {
|
|
194
|
+
const file = files.get(path);
|
|
195
|
+
if (file == null) {
|
|
196
|
+
throw createHttpError(404, 'Файл не найден');
|
|
197
|
+
}
|
|
198
|
+
return file;
|
|
199
|
+
};
|
|
200
|
+
const metadata = async (path) => {
|
|
201
|
+
const file = findFile(path);
|
|
202
|
+
const filePath = await resolveExistingPath(path);
|
|
203
|
+
return { ...file, size: await getFileSize(filePath) };
|
|
204
|
+
};
|
|
205
|
+
const get = async (path) => {
|
|
206
|
+
const file = findFile(path);
|
|
207
|
+
const filePath = await resolveExistingPath(path);
|
|
208
|
+
const handle = await open(filePath, 'r');
|
|
209
|
+
try {
|
|
210
|
+
const size = (await handle.stat()).size;
|
|
211
|
+
return { file: { ...file, size }, stream: handle.createReadStream() };
|
|
212
|
+
}
|
|
213
|
+
catch (error) {
|
|
214
|
+
await handle.close();
|
|
215
|
+
throw error;
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
const upload = async ({ directory, maxFileSize, mimeType, name, override, stream }) => {
|
|
219
|
+
const storedFile = { directory, mimeType, name };
|
|
220
|
+
const key = getFileKey(storedFile);
|
|
221
|
+
if (files.has(key) && !override) {
|
|
222
|
+
throw createHttpError(409, 'Файл уже существует');
|
|
223
|
+
}
|
|
224
|
+
const stagedPath = resolve(stagingPath, `${randomBytes(12).toString('hex')}.upload`);
|
|
225
|
+
let size = 0;
|
|
226
|
+
try {
|
|
227
|
+
await pipeline(stream, createSizeLimiter(maxFileSize, (value) => {
|
|
228
|
+
size = value;
|
|
229
|
+
}), createWriteStream(stagedPath, { flags: 'wx' }));
|
|
230
|
+
return await schedule(async () => {
|
|
231
|
+
await flushCleanup();
|
|
232
|
+
const path = await prepareTargetPath(key);
|
|
233
|
+
const existsOnDisk = await pathExists(path);
|
|
234
|
+
const exists = files.has(key) || existsOnDisk;
|
|
235
|
+
if (exists && !override) {
|
|
236
|
+
throw createHttpError(409, 'Файл уже существует');
|
|
237
|
+
}
|
|
238
|
+
const backupPath = `${path}.${randomBytes(6).toString('hex')}.backup`;
|
|
239
|
+
let backedUp = false;
|
|
240
|
+
let installed = false;
|
|
241
|
+
let committed = false;
|
|
242
|
+
try {
|
|
243
|
+
if (existsOnDisk) {
|
|
244
|
+
await rename(path, backupPath);
|
|
245
|
+
backedUp = true;
|
|
246
|
+
}
|
|
247
|
+
await rename(stagedPath, path);
|
|
248
|
+
installed = true;
|
|
249
|
+
const nextFiles = new Map(files);
|
|
250
|
+
nextFiles.set(key, storedFile);
|
|
251
|
+
await writeMetadata(metadataPath, nextFiles);
|
|
252
|
+
files = nextFiles;
|
|
253
|
+
committed = true;
|
|
254
|
+
return { created: !exists, file: { ...storedFile, size } };
|
|
255
|
+
}
|
|
256
|
+
catch (error) {
|
|
257
|
+
const rollbackErrors = [];
|
|
258
|
+
if (installed) {
|
|
259
|
+
await rm(path, { force: true }).catch((rollbackError) => rollbackErrors.push(rollbackError));
|
|
260
|
+
}
|
|
261
|
+
if (backedUp) {
|
|
262
|
+
await rename(backupPath, path).catch((rollbackError) => rollbackErrors.push(rollbackError));
|
|
263
|
+
}
|
|
264
|
+
if (rollbackErrors.length > 0) {
|
|
265
|
+
throw new AggregateError([error, ...rollbackErrors], 'Не удалось сохранить файл и полностью откатить операцию');
|
|
266
|
+
}
|
|
267
|
+
throw error;
|
|
268
|
+
}
|
|
269
|
+
finally {
|
|
270
|
+
if (committed && backedUp) {
|
|
271
|
+
await cleanupLater(backupPath);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
finally {
|
|
277
|
+
await cleanupLater(stagedPath);
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
const update = (sourcePath, updates) => schedule(async () => {
|
|
281
|
+
await flushCleanup();
|
|
282
|
+
const file = findFile(sourcePath);
|
|
283
|
+
const updatedFile = { ...file, ...updates };
|
|
284
|
+
const targetPath = getFileKey(updatedFile);
|
|
285
|
+
const sourceFilePath = await resolveExistingPath(sourcePath);
|
|
286
|
+
if (sourcePath === targetPath) {
|
|
287
|
+
return { ...file, size: await getFileSize(sourceFilePath) };
|
|
288
|
+
}
|
|
289
|
+
const targetFilePath = await prepareTargetPath(targetPath);
|
|
290
|
+
if (files.has(targetPath) || (await pathExists(targetFilePath))) {
|
|
291
|
+
throw createHttpError(409, 'Файл с таким путём уже существует');
|
|
292
|
+
}
|
|
293
|
+
await rename(sourceFilePath, targetFilePath);
|
|
294
|
+
const nextFiles = new Map(files);
|
|
295
|
+
nextFiles.delete(sourcePath);
|
|
296
|
+
nextFiles.set(targetPath, updatedFile);
|
|
297
|
+
await commitMetadata(nextFiles, () => rename(targetFilePath, sourceFilePath), 'Не удалось обновить файл и откатить перемещение');
|
|
298
|
+
return { ...updatedFile, size: await getFileSize(targetFilePath) };
|
|
299
|
+
});
|
|
300
|
+
const remove = (path) => schedule(async () => {
|
|
301
|
+
await flushCleanup();
|
|
302
|
+
findFile(path);
|
|
303
|
+
const filePath = await resolveExistingPath(path);
|
|
304
|
+
const temporaryPath = `${filePath}.${randomBytes(6).toString('hex')}.delete`;
|
|
305
|
+
await rename(filePath, temporaryPath);
|
|
306
|
+
const nextFiles = new Map(files);
|
|
307
|
+
nextFiles.delete(path);
|
|
308
|
+
await commitMetadata(nextFiles, () => rename(temporaryPath, filePath), 'Не удалось удалить файл и откатить операцию');
|
|
309
|
+
await cleanupLater(temporaryPath);
|
|
310
|
+
});
|
|
311
|
+
return { get, metadata, remove, update, upload };
|
|
312
|
+
};
|
|
313
|
+
//# sourceMappingURL=disk-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"disk-store.js","sourceRoot":"","sources":["../../../src/files/disk-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { FilesConfig } from '../config.js';
|
|
2
|
+
import type { FileStore } from './contract.js';
|
|
3
|
+
/** Creates a disk- or memory-backed file store. */
|
|
4
|
+
export declare const createFileStore: (config: FilesConfig) => Promise<FileStore>;
|
|
5
|
+
export { registerFileRoutes } from './routes.js';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { createDiskFileStore } from './disk-store.js';
|
|
2
|
+
import { createMemoryFileStore } from './memory-store.js';
|
|
3
|
+
/** Creates a disk- or memory-backed file store. */
|
|
4
|
+
export const createFileStore = async (config) => (config.data != null ? createMemoryFileStore(config.data) : createDiskFileStore(config));
|
|
5
|
+
export { registerFileRoutes } from './routes.js';
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/files/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAE1D,mDAAmD;AACnD,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,EAAE,MAAmB,EAAsB,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,qBAAqB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC,CAAC;AAE3K,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { Readable } from 'node:stream';
|
|
2
|
+
import { createHttpError, createSerialQueue } from '../utils.js';
|
|
3
|
+
import { getFileKey, normalizeStoredFileMetadata } from './contract.js';
|
|
4
|
+
const readUpload = async (stream, maxFileSize) => {
|
|
5
|
+
const chunks = [];
|
|
6
|
+
let size = 0;
|
|
7
|
+
for await (const chunk of stream) {
|
|
8
|
+
const buffer = Buffer.from(chunk);
|
|
9
|
+
size += buffer.length;
|
|
10
|
+
if (size > maxFileSize) {
|
|
11
|
+
throw createHttpError(413, `Размер файла не должен превышать ${maxFileSize} байт`);
|
|
12
|
+
}
|
|
13
|
+
chunks.push(buffer);
|
|
14
|
+
}
|
|
15
|
+
return Buffer.concat(chunks);
|
|
16
|
+
};
|
|
17
|
+
export const createMemoryFileStore = (sourceFiles) => {
|
|
18
|
+
const storedFiles = new Map();
|
|
19
|
+
sourceFiles.forEach((sourceFile, index) => {
|
|
20
|
+
if (!(sourceFile.content instanceof Uint8Array)) {
|
|
21
|
+
throw new Error(`Некорректная запись ${index} в config.files.data`);
|
|
22
|
+
}
|
|
23
|
+
const file = {
|
|
24
|
+
...normalizeStoredFileMetadata(sourceFile, `Запись ${index} в config.files.data`),
|
|
25
|
+
size: sourceFile.content.length,
|
|
26
|
+
};
|
|
27
|
+
const path = getFileKey(file);
|
|
28
|
+
if (storedFiles.has(path)) {
|
|
29
|
+
throw new Error(`config.files.data содержит повторяющийся путь «${path}»`);
|
|
30
|
+
}
|
|
31
|
+
storedFiles.set(path, { content: Buffer.from(sourceFile.content), file });
|
|
32
|
+
});
|
|
33
|
+
const schedule = createSerialQueue();
|
|
34
|
+
const findStoredFile = (path) => {
|
|
35
|
+
const storedFile = storedFiles.get(path);
|
|
36
|
+
if (storedFile == null) {
|
|
37
|
+
throw createHttpError(404, 'Файл не найден');
|
|
38
|
+
}
|
|
39
|
+
return storedFile;
|
|
40
|
+
};
|
|
41
|
+
const metadata = async (path) => findStoredFile(path).file;
|
|
42
|
+
const get = async (path) => {
|
|
43
|
+
const storedFile = findStoredFile(path);
|
|
44
|
+
return { file: storedFile.file, stream: Readable.from([storedFile.content]) };
|
|
45
|
+
};
|
|
46
|
+
const upload = async ({ directory, maxFileSize, mimeType, name, override, stream }) => {
|
|
47
|
+
const path = getFileKey({ directory, name });
|
|
48
|
+
if (storedFiles.has(path) && !override) {
|
|
49
|
+
throw createHttpError(409, 'Файл уже существует');
|
|
50
|
+
}
|
|
51
|
+
const content = await readUpload(stream, maxFileSize);
|
|
52
|
+
return schedule(() => {
|
|
53
|
+
const exists = storedFiles.has(path);
|
|
54
|
+
if (exists && !override) {
|
|
55
|
+
throw createHttpError(409, 'Файл уже существует');
|
|
56
|
+
}
|
|
57
|
+
const file = { directory, mimeType, name, size: content.length };
|
|
58
|
+
storedFiles.set(path, { content, file });
|
|
59
|
+
return { created: !exists, file };
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
const update = (sourcePath, updates) => schedule(() => {
|
|
63
|
+
const storedFile = findStoredFile(sourcePath);
|
|
64
|
+
const file = { ...storedFile.file, ...updates };
|
|
65
|
+
const targetPath = getFileKey(file);
|
|
66
|
+
if (sourcePath !== targetPath && storedFiles.has(targetPath)) {
|
|
67
|
+
throw createHttpError(409, 'Файл с таким путём уже существует');
|
|
68
|
+
}
|
|
69
|
+
storedFiles.delete(sourcePath);
|
|
70
|
+
storedFiles.set(targetPath, { ...storedFile, file });
|
|
71
|
+
return file;
|
|
72
|
+
});
|
|
73
|
+
const remove = (path) => schedule(() => {
|
|
74
|
+
if (!storedFiles.delete(path)) {
|
|
75
|
+
throw createHttpError(404, 'Файл не найден');
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
return { get, metadata, remove, update, upload };
|
|
79
|
+
};
|
|
80
|
+
//# sourceMappingURL=memory-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-store.js","sourceRoot":"","sources":["../../../src/files/memory-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { FastifyInstance } from 'fastify';
|
|
2
|
+
import { type FileStore } from './contract.js';
|
|
3
|
+
export declare const registerFileRoutes: (fastify: FastifyInstance, { getStore, maxFileSize }: {
|
|
4
|
+
getStore: () => Promise<FileStore>;
|
|
5
|
+
maxFileSize: number;
|
|
6
|
+
}) => void;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { createHttpError } from '../utils.js';
|
|
2
|
+
import { createFileMetadata, FILE_HEADERS, FILE_ROUTES, FILE_UPDATE_SCHEMA, getDownloadName, getFileKey, getPathLocation, normalizeMimeType, PATCH_BODY_LIMIT, validateDirectory, validateName, } from './contract.js';
|
|
3
|
+
const decodeHeader = (value, name) => {
|
|
4
|
+
if (typeof value !== 'string') {
|
|
5
|
+
throw createHttpError(400, `Заголовок ${name} обязателен`);
|
|
6
|
+
}
|
|
7
|
+
try {
|
|
8
|
+
return decodeURIComponent(value);
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
throw createHttpError(400, `Заголовок ${name} содержит некорректное значение`);
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
const getContentName = (value) => validateName(decodeHeader(value, FILE_HEADERS.name.name), `Заголовок ${FILE_HEADERS.name.name}`);
|
|
15
|
+
const getContentDirectory = (value) => (value == null ? '' : validateDirectory(decodeHeader(value, FILE_HEADERS.directory.name), `Заголовок ${FILE_HEADERS.directory.name}`));
|
|
16
|
+
const getContentOverride = (value) => {
|
|
17
|
+
if (value == null || value === 'false') {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
if (value === 'true') {
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
throw createHttpError(400, `Заголовок ${FILE_HEADERS.override.name} должен содержать true или false`);
|
|
24
|
+
};
|
|
25
|
+
const getRequestPath = (request) => getFileKey(getPathLocation(request.params['*']));
|
|
26
|
+
const normalizeUpdate = (body) => ({
|
|
27
|
+
...(body.directory != null && { directory: validateDirectory(body.directory, 'Ключ body.directory') }),
|
|
28
|
+
...(body.name != null && { name: validateName(body.name, 'Ключ body.name') }),
|
|
29
|
+
});
|
|
30
|
+
const sendFile = async (store, path, reply, disposition) => {
|
|
31
|
+
const { file, stream } = await store.get(path);
|
|
32
|
+
reply.header('Content-Disposition', `${disposition}; filename*=UTF-8''${getDownloadName(file.name)}`);
|
|
33
|
+
reply.type(file.mimeType);
|
|
34
|
+
return reply.send(stream);
|
|
35
|
+
};
|
|
36
|
+
export const registerFileRoutes = (fastify, { getStore, maxFileSize }) => {
|
|
37
|
+
fastify.register(async (fileServer) => {
|
|
38
|
+
const store = await getStore();
|
|
39
|
+
fileServer.register((uploadServer, _options, done) => {
|
|
40
|
+
uploadServer.removeAllContentTypeParsers();
|
|
41
|
+
uploadServer.addContentTypeParser('*', (_request, payload, parserDone) => parserDone(null, payload));
|
|
42
|
+
uploadServer.post(FILE_ROUTES.storage, async (request, reply) => {
|
|
43
|
+
const contentLength = Number(request.headers['content-length']);
|
|
44
|
+
if (Number.isFinite(contentLength) && contentLength > maxFileSize) {
|
|
45
|
+
throw createHttpError(413, `Размер файла не должен превышать ${maxFileSize} байт`);
|
|
46
|
+
}
|
|
47
|
+
const result = await store.upload({
|
|
48
|
+
directory: getContentDirectory(request.headers[FILE_HEADERS.directory.key]),
|
|
49
|
+
maxFileSize,
|
|
50
|
+
mimeType: normalizeMimeType(request.headers['content-type']),
|
|
51
|
+
name: getContentName(request.headers[FILE_HEADERS.name.key]),
|
|
52
|
+
override: getContentOverride(request.headers[FILE_HEADERS.override.key]),
|
|
53
|
+
stream: request.body,
|
|
54
|
+
});
|
|
55
|
+
return reply.code(result.created ? 201 : 200).send(createFileMetadata(result.file));
|
|
56
|
+
});
|
|
57
|
+
done();
|
|
58
|
+
});
|
|
59
|
+
fileServer.get(`${FILE_ROUTES.storage}/*`, async (request, reply) => sendFile(store, getRequestPath(request), reply, 'inline'));
|
|
60
|
+
fileServer.get(`${FILE_ROUTES.download}/*`, async (request, reply) => sendFile(store, getRequestPath(request), reply, 'attachment'));
|
|
61
|
+
fileServer.get(`${FILE_ROUTES.metadata}/*`, async (request) => createFileMetadata(await store.metadata(getRequestPath(request))));
|
|
62
|
+
fileServer.patch(`${FILE_ROUTES.storage}/*`, {
|
|
63
|
+
bodyLimit: PATCH_BODY_LIMIT,
|
|
64
|
+
preValidation: async (request) => {
|
|
65
|
+
if (normalizeMimeType(request.headers['content-type']) !== 'application/json') {
|
|
66
|
+
throw createHttpError(415, 'Для изменения файла используйте Content-Type: application/json');
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
schema: { body: FILE_UPDATE_SCHEMA },
|
|
70
|
+
}, async (request) => createFileMetadata(await store.update(getRequestPath(request), normalizeUpdate(request.body))));
|
|
71
|
+
fileServer.delete(`${FILE_ROUTES.storage}/*`, async (request, reply) => {
|
|
72
|
+
await store.remove(getRequestPath(request));
|
|
73
|
+
return reply.code(204).send();
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
};
|
|
77
|
+
//# sourceMappingURL=routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../../../src/files/routes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { DatabaseSchema } from '../config.js';
|
|
2
|
+
import type { JsonObject, OpenapiSchema } from '../types.js';
|
|
3
|
+
export interface ResourceSchemaConfig {
|
|
4
|
+
formats: Record<string, string>;
|
|
5
|
+
name?: string;
|
|
6
|
+
properties: Record<string, OpenapiSchema>;
|
|
7
|
+
required: string[];
|
|
8
|
+
}
|
|
9
|
+
export declare const normalizeSchemaConfig: (schemaConfig: JsonObject, resources: string[]) => Record<string, ResourceSchemaConfig>;
|
|
10
|
+
export declare const applyConfiguredFields: (schema: OpenapiSchema, resource: string, resourceConfig: ResourceSchemaConfig) => OpenapiSchema;
|
|
11
|
+
export declare const resolveSchemaConfig: (schema: DatabaseSchema | string | undefined) => Promise<DatabaseSchema>;
|