@kollors/deep-json-server 1.0.0-alpha.1 → 1.0.0-alpha.11
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 +522 -179
- package/README.ru.md +525 -180
- package/dist/bin/deep-json-server.js +1 -1
- package/dist/bin/deep-json-server.js.map +1 -1
- package/dist/index.d.ts +13 -6
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/src/auth/contract.d.ts +31 -0
- package/dist/src/auth/contract.js +12 -0
- package/dist/src/auth/contract.js.map +1 -0
- package/dist/src/auth/input.d.ts +18 -0
- package/dist/src/auth/input.js +34 -0
- package/dist/src/auth/input.js.map +1 -0
- package/dist/src/auth/password.d.ts +17 -0
- package/dist/src/auth/password.js +41 -0
- package/dist/src/auth/password.js.map +1 -0
- package/dist/src/auth/public.d.ts +3 -0
- package/dist/src/auth/public.js +5 -0
- package/dist/src/auth/public.js.map +1 -0
- package/dist/src/auth/routes.d.ts +6 -0
- package/dist/src/auth/routes.js +18 -0
- package/dist/src/auth/routes.js.map +1 -0
- package/dist/src/auth/service.d.ts +78 -0
- package/dist/src/auth/service.js +218 -0
- package/dist/src/auth/service.js.map +1 -0
- package/dist/src/auth/store.d.ts +26 -0
- package/dist/src/auth/store.js +82 -0
- package/dist/src/auth/store.js.map +1 -0
- package/dist/src/cli/index.d.ts +7 -0
- package/dist/src/cli/index.js +113 -0
- package/dist/src/cli/index.js.map +1 -0
- package/dist/src/core/config-values.d.ts +28 -0
- package/dist/src/core/config-values.js +53 -0
- package/dist/src/core/config-values.js.map +1 -0
- package/dist/src/{constants.d.ts → core/constants.d.ts} +1 -0
- package/dist/src/{constants.js → core/constants.js} +1 -0
- package/dist/src/core/constants.js.map +1 -0
- package/dist/src/core/database.d.ts +48 -0
- package/dist/src/{database.js → core/database.js} +36 -14
- package/dist/src/core/database.js.map +1 -0
- package/dist/src/core/engine.d.ts +53 -0
- package/dist/src/core/engine.js +249 -0
- package/dist/src/core/engine.js.map +1 -0
- package/dist/src/core/errors.d.ts +9 -0
- package/dist/src/core/errors.js +12 -0
- package/dist/src/core/errors.js.map +1 -0
- package/dist/src/core/http-errors.d.ts +9 -0
- package/dist/src/core/http-errors.js +15 -0
- package/dist/src/core/http-errors.js.map +1 -0
- package/dist/src/core/lifecycle/mutation.d.ts +49 -0
- package/dist/src/core/lifecycle/mutation.js +170 -0
- package/dist/src/core/lifecycle/mutation.js.map +1 -0
- package/dist/src/core/lifecycle/options.d.ts +21 -0
- package/dist/src/core/lifecycle/options.js +19 -0
- package/dist/src/core/lifecycle/options.js.map +1 -0
- package/dist/src/core/model.d.ts +145 -0
- package/dist/src/{model.js → core/model.js} +224 -33
- package/dist/src/core/model.js.map +1 -0
- package/dist/src/core/mutations/write.d.ts +26 -0
- package/dist/src/core/mutations/write.js +284 -0
- package/dist/src/core/mutations/write.js.map +1 -0
- package/dist/src/core/operations.d.ts +33 -0
- package/dist/src/core/operations.js +10 -0
- package/dist/src/core/operations.js.map +1 -0
- package/dist/src/core/pagination.d.ts +10 -0
- package/dist/src/core/pagination.js +13 -0
- package/dist/src/core/pagination.js.map +1 -0
- package/dist/src/core/paths.d.ts +16 -0
- package/dist/src/core/paths.js +64 -0
- package/dist/src/core/paths.js.map +1 -0
- package/dist/src/core/query/contract.d.ts +6 -0
- package/dist/src/core/query/contract.js +22 -0
- package/dist/src/core/query/contract.js.map +1 -0
- package/dist/src/core/query/execute.d.ts +25 -0
- package/dist/src/core/query/execute.js +82 -0
- package/dist/src/core/query/execute.js.map +1 -0
- package/dist/src/core/query/filter.d.ts +6 -0
- package/dist/src/core/query/filter.js +100 -0
- package/dist/src/core/query/filter.js.map +1 -0
- package/dist/src/core/query/options.d.ts +30 -0
- package/dist/src/core/query/options.js +44 -0
- package/dist/src/core/query/options.js.map +1 -0
- package/dist/src/core/records.d.ts +46 -0
- package/dist/src/core/records.js +69 -0
- package/dist/src/core/records.js.map +1 -0
- package/dist/src/core/relation-metadata.d.ts +15 -0
- package/dist/src/{relation-metadata.js → core/relation-metadata.js} +7 -2
- package/dist/src/core/relation-metadata.js.map +1 -0
- package/dist/src/core/storage.d.ts +2 -0
- package/dist/src/core/storage.js +2 -0
- package/dist/src/core/storage.js.map +1 -0
- package/dist/src/core/types.d.ts +10 -0
- package/dist/src/{types.js.map → core/types.js.map} +1 -1
- package/dist/src/core/utils.d.ts +56 -0
- package/dist/src/core/utils.js +102 -0
- package/dist/src/core/utils.js.map +1 -0
- package/dist/src/files/contract.d.ts +32 -30
- package/dist/src/files/contract.js +29 -48
- package/dist/src/files/contract.js.map +1 -1
- package/dist/src/files/disk-metadata.d.ts +9 -0
- package/dist/src/files/disk-metadata.js +44 -0
- package/dist/src/files/disk-metadata.js.map +1 -0
- package/dist/src/files/disk-paths.d.ts +15 -0
- package/dist/src/files/disk-paths.js +100 -0
- package/dist/src/files/disk-paths.js.map +1 -0
- package/dist/src/files/disk-store.d.ts +5 -1
- package/dist/src/files/disk-store.js +46 -173
- package/dist/src/files/disk-store.js.map +1 -1
- package/dist/src/files/http.d.ts +36 -0
- package/dist/src/files/http.js +53 -0
- package/dist/src/files/http.js.map +1 -0
- package/dist/src/files/index.d.ts +5 -4
- package/dist/src/files/index.js +7 -2
- package/dist/src/files/index.js.map +1 -1
- package/dist/src/files/memory-store.d.ts +4 -1
- package/dist/src/files/memory-store.js +29 -22
- package/dist/src/files/memory-store.js.map +1 -1
- package/dist/src/files/routes.d.ts +3 -0
- package/dist/src/files/routes.js +27 -2
- package/dist/src/files/routes.js.map +1 -1
- package/dist/src/files/streams.d.ts +5 -0
- package/dist/src/files/streams.js +20 -0
- package/dist/src/files/streams.js.map +1 -0
- package/dist/src/graphql/entry.d.ts +12 -0
- package/dist/src/graphql/entry.js +13 -0
- package/dist/src/graphql/entry.js.map +1 -0
- package/dist/src/graphql/generate.d.ts +12 -0
- package/dist/src/graphql/generate.js +19 -0
- package/dist/src/graphql/generate.js.map +1 -0
- package/dist/src/graphql/preflight.d.ts +6 -0
- package/dist/src/graphql/preflight.js +44 -0
- package/dist/src/graphql/preflight.js.map +1 -0
- package/dist/src/graphql/resolvers.d.ts +17 -0
- package/dist/src/graphql/resolvers.js +44 -0
- package/dist/src/graphql/resolvers.js.map +1 -0
- package/dist/src/graphql/routes.d.ts +8 -0
- package/dist/src/graphql/routes.js +30 -0
- package/dist/src/graphql/routes.js.map +1 -0
- package/dist/src/graphql/schema.d.ts +6 -0
- package/dist/src/{graphql.js → graphql/schema.js} +59 -49
- package/dist/src/graphql/schema.js.map +1 -0
- package/dist/src/graphql/write.d.ts +4 -0
- package/dist/src/graphql/write.js +10 -0
- package/dist/src/graphql/write.js.map +1 -0
- package/dist/src/openapi/auth.d.ts +13 -0
- package/dist/src/openapi/auth.js +139 -0
- package/dist/src/openapi/auth.js.map +1 -0
- package/dist/src/openapi/document.d.ts +7 -3
- package/dist/src/openapi/document.js +154 -103
- package/dist/src/openapi/document.js.map +1 -1
- package/dist/src/openapi/entry.d.ts +14 -0
- package/dist/src/openapi/entry.js +13 -0
- package/dist/src/openapi/entry.js.map +1 -0
- package/dist/src/openapi/files.d.ts +6 -0
- package/dist/src/{files/openapi.js → openapi/files.js} +6 -5
- package/dist/src/openapi/files.js.map +1 -0
- package/dist/src/openapi/generate.d.ts +19 -0
- package/dist/src/openapi/generate.js +41 -0
- package/dist/src/openapi/generate.js.map +1 -0
- package/dist/src/openapi/helpers.d.ts +26 -0
- package/dist/src/openapi/helpers.js +13 -0
- package/dist/src/openapi/helpers.js.map +1 -0
- package/dist/src/openapi/options.d.ts +18 -0
- package/dist/src/openapi/options.js +13 -0
- package/dist/src/openapi/options.js.map +1 -0
- package/dist/src/openapi/registry.d.ts +11 -0
- package/dist/src/openapi/registry.js +20 -0
- package/dist/src/openapi/registry.js.map +1 -0
- package/dist/src/{types.d.ts → openapi/types.d.ts} +2 -10
- package/dist/src/openapi/types.js +2 -0
- package/dist/src/openapi/types.js.map +1 -0
- package/dist/src/openapi/write.d.ts +5 -0
- package/dist/src/openapi/write.js +12 -0
- package/dist/src/openapi/write.js.map +1 -0
- package/dist/src/rest/options.d.ts +22 -0
- package/dist/src/rest/options.js +65 -0
- package/dist/src/rest/options.js.map +1 -0
- package/dist/src/rest/projection.d.ts +13 -0
- package/dist/src/rest/projection.js +61 -0
- package/dist/src/rest/projection.js.map +1 -0
- package/dist/src/rest/routes.d.ts +7 -0
- package/dist/src/rest/routes.js +58 -0
- package/dist/src/rest/routes.js.map +1 -0
- package/dist/src/server/bootstrap.d.ts +7 -0
- package/dist/src/server/bootstrap.js +48 -0
- package/dist/src/server/bootstrap.js.map +1 -0
- package/dist/src/server/config.d.ts +74 -0
- package/dist/src/server/config.js +142 -0
- package/dist/src/server/config.js.map +1 -0
- package/dist/src/server/create.d.ts +16 -0
- package/dist/src/server/create.js +59 -0
- package/dist/src/server/create.js.map +1 -0
- package/dist/src/server/features.d.ts +4 -0
- package/dist/src/server/features.js +11 -0
- package/dist/src/server/features.js.map +1 -0
- package/dist/src/server/http.d.ts +12 -0
- package/dist/src/server/http.js +40 -0
- package/dist/src/server/http.js.map +1 -0
- package/dist/src/server/model.d.ts +6 -0
- package/dist/src/server/model.js +14 -0
- package/dist/src/server/model.js.map +1 -0
- package/dist/src/server/openapi-options.d.ts +6 -0
- package/dist/src/server/openapi-options.js +15 -0
- package/dist/src/server/openapi-options.js.map +1 -0
- package/dist/src/server/public.d.ts +2 -0
- package/dist/src/server/public.js +2 -0
- package/dist/src/server/public.js.map +1 -0
- package/package.json +19 -3
- package/dist/src/cli.d.ts +0 -4
- package/dist/src/cli.js +0 -67
- package/dist/src/cli.js.map +0 -1
- package/dist/src/config.d.ts +0 -68
- package/dist/src/config.js +0 -161
- package/dist/src/config.js.map +0 -1
- package/dist/src/constants.js.map +0 -1
- package/dist/src/database.d.ts +0 -21
- package/dist/src/database.js.map +0 -1
- package/dist/src/engine.d.ts +0 -47
- package/dist/src/engine.js +0 -438
- package/dist/src/engine.js.map +0 -1
- package/dist/src/files/openapi.d.ts +0 -3
- package/dist/src/files/openapi.js.map +0 -1
- package/dist/src/graphql.d.ts +0 -4
- package/dist/src/graphql.js.map +0 -1
- package/dist/src/model.d.ts +0 -67
- package/dist/src/model.js.map +0 -1
- package/dist/src/openapi/index.d.ts +0 -7
- package/dist/src/openapi/index.js +0 -26
- package/dist/src/openapi/index.js.map +0 -1
- package/dist/src/query/filter.d.ts +0 -1
- package/dist/src/query/filter.js +0 -84
- package/dist/src/query/filter.js.map +0 -1
- package/dist/src/query/options.d.ts +0 -31
- package/dist/src/query/options.js +0 -135
- package/dist/src/query/options.js.map +0 -1
- package/dist/src/relation-metadata.d.ts +0 -10
- package/dist/src/relation-metadata.js.map +0 -1
- package/dist/src/server.d.ts +0 -14
- package/dist/src/server.js +0 -166
- package/dist/src/server.js.map +0 -1
- package/dist/src/utils.d.ts +0 -19
- package/dist/src/utils.js +0 -62
- package/dist/src/utils.js.map +0 -1
- /package/dist/src/{types.js → core/types.js} +0 -0
|
@@ -1,5 +1,18 @@
|
|
|
1
|
+
import type { Storage } from '../core/storage.js';
|
|
2
|
+
export interface MemoryFile {
|
|
3
|
+
content: Uint8Array;
|
|
4
|
+
directory?: string;
|
|
5
|
+
mimeType: string;
|
|
6
|
+
name: string;
|
|
7
|
+
}
|
|
8
|
+
export type FilesConfig<S extends Storage = Storage> = S extends 'file' ? {
|
|
9
|
+
source: string;
|
|
10
|
+
metadata?: string;
|
|
11
|
+
} : {
|
|
12
|
+
source: MemoryFile[];
|
|
13
|
+
metadata?: never;
|
|
14
|
+
};
|
|
1
15
|
import type { Readable } from 'node:stream';
|
|
2
|
-
import type { OpenapiSchema } from '../types.js';
|
|
3
16
|
export interface StoredFileMetadata {
|
|
4
17
|
directory: string;
|
|
5
18
|
mimeType: string;
|
|
@@ -8,11 +21,6 @@ export interface StoredFileMetadata {
|
|
|
8
21
|
export interface FileRecord extends StoredFileMetadata {
|
|
9
22
|
size: number;
|
|
10
23
|
}
|
|
11
|
-
export interface FileMetadata extends FileRecord {
|
|
12
|
-
downloadUrl: string;
|
|
13
|
-
metadataUrl: string;
|
|
14
|
-
url: string;
|
|
15
|
-
}
|
|
16
24
|
export interface FileUpload extends StoredFileMetadata {
|
|
17
25
|
maxFileSize: number;
|
|
18
26
|
override: boolean;
|
|
@@ -35,33 +43,27 @@ export interface FileStore {
|
|
|
35
43
|
file: FileRecord;
|
|
36
44
|
}>;
|
|
37
45
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
readonly name: 'Content-Directory';
|
|
42
|
-
};
|
|
43
|
-
readonly name: {
|
|
44
|
-
readonly key: 'content-name';
|
|
45
|
-
readonly name: 'Content-Name';
|
|
46
|
-
};
|
|
47
|
-
readonly override: {
|
|
48
|
-
readonly key: 'content-override';
|
|
49
|
-
readonly name: 'Content-Override';
|
|
50
|
-
};
|
|
51
|
-
};
|
|
52
|
-
export declare const FILE_ROUTES: {
|
|
53
|
-
readonly download: '/_files/download';
|
|
54
|
-
readonly metadata: '/_files/metadata';
|
|
55
|
-
readonly storage: '/_files/storage';
|
|
56
|
-
};
|
|
57
|
-
export declare const PATCH_BODY_LIMIT: number;
|
|
58
|
-
export declare const FILE_METADATA_SCHEMA: OpenapiSchema;
|
|
59
|
-
export declare const FILE_UPDATE_SCHEMA: OpenapiSchema;
|
|
46
|
+
/** Соединяет непустой каталог и имя через косую черту, не проверяя безопасность пути.
|
|
47
|
+
* @example getFileKey({ directory: 'photos', name: 'a.jpg' }) → 'photos/a.jpg'.
|
|
48
|
+
*/
|
|
60
49
|
export declare const getFileKey: ({ directory, name }: Pick<StoredFileMetadata, 'directory' | 'name'>) => string;
|
|
50
|
+
/** Проверяет безопасное имя файла, включая зарезервированные имена Windows.
|
|
51
|
+
* @example validateName('a.txt', 'name') → 'a.txt'; validateName('../a', 'name') → ошибка.
|
|
52
|
+
*/
|
|
61
53
|
export declare const validateName: (value: unknown, source: string) => string;
|
|
54
|
+
/** Проверяет относительный путь с разделителем /; пустой путь обозначает корень.
|
|
55
|
+
* @example validateDirectory('photos/2026', 'path') → 'photos/2026'; '../photos' → ошибка.
|
|
56
|
+
*/
|
|
62
57
|
export declare const validateDirectory: (value: unknown, source: string) => string;
|
|
58
|
+
/** Удаляет параметры MIME-типа, приводит его к нижнему регистру и проверяет формат.
|
|
59
|
+
* @example normalizeMimeType('Text/Plain; charset=utf-8') → 'text/plain'.
|
|
60
|
+
*/
|
|
63
61
|
export declare const normalizeMimeType: (value: unknown, source?: string) => string;
|
|
62
|
+
/** Проверяет метаданные и возвращает объект из каталога, имени и MIME-типа.
|
|
63
|
+
* @example { name: 'a.txt', mimeType: 'TEXT/PLAIN' } → { directory: '', name: 'a.txt', mimeType: 'text/plain' }.
|
|
64
|
+
*/
|
|
64
65
|
export declare const normalizeStoredFileMetadata: (value: unknown, source: string) => StoredFileMetadata;
|
|
66
|
+
/** Разделяет проверенный относительный путь на каталог и имя файла.
|
|
67
|
+
* @example getPathLocation('photos/a.jpg') → { directory: 'photos', name: 'a.jpg' }.
|
|
68
|
+
*/
|
|
65
69
|
export declare const getPathLocation: (path: string) => Pick<StoredFileMetadata, 'directory' | 'name'>;
|
|
66
|
-
export declare const createFileMetadata: (file: FileRecord) => FileMetadata;
|
|
67
|
-
export declare const getDownloadName: (name: string) => string;
|
|
@@ -1,42 +1,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
directory: { key: 'content-directory', name: 'Content-Directory' },
|
|
5
|
-
name: { key: 'content-name', name: 'Content-Name' },
|
|
6
|
-
override: { key: 'content-override', name: 'Content-Override' },
|
|
7
|
-
};
|
|
8
|
-
export const FILE_ROUTES = {
|
|
9
|
-
download: '/_files/download',
|
|
10
|
-
metadata: '/_files/metadata',
|
|
11
|
-
storage: '/_files/storage',
|
|
12
|
-
};
|
|
13
|
-
export const PATCH_BODY_LIMIT = 64 * 1024;
|
|
1
|
+
import { errorMessage } from '../core/utils.js';
|
|
2
|
+
import { domainError } from '../core/errors.js';
|
|
3
|
+
import { isObject } from '../core/utils.js';
|
|
14
4
|
const MIME_TYPE_PATTERN = /^[A-Za-z0-9!#$%&'*+.^_`|~-]+\/[A-Za-z0-9!#$%&'*+.^_`|~-]+$/;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
downloadUrl: { format: 'uri-reference', type: 'string' },
|
|
19
|
-
metadataUrl: { format: 'uri-reference', type: 'string' },
|
|
20
|
-
mimeType: { type: 'string' },
|
|
21
|
-
name: { type: 'string' },
|
|
22
|
-
size: { minimum: 0, type: 'integer' },
|
|
23
|
-
url: { format: 'uri-reference', type: 'string' },
|
|
24
|
-
},
|
|
25
|
-
required: ['directory', 'downloadUrl', 'metadataUrl', 'mimeType', 'name', 'size', 'url'],
|
|
26
|
-
type: 'object',
|
|
27
|
-
};
|
|
28
|
-
export const FILE_UPDATE_SCHEMA = {
|
|
29
|
-
additionalProperties: false,
|
|
30
|
-
anyOf: [{ required: ['directory'] }, { required: ['name'] }],
|
|
31
|
-
properties: { directory: { type: 'string' }, name: { type: 'string' } },
|
|
32
|
-
type: 'object',
|
|
33
|
-
};
|
|
5
|
+
/** Соединяет непустой каталог и имя через косую черту, не проверяя безопасность пути.
|
|
6
|
+
* @example getFileKey({ directory: 'photos', name: 'a.jpg' }) → 'photos/a.jpg'.
|
|
7
|
+
*/
|
|
34
8
|
export const getFileKey = ({ directory, name }) => [directory, name].filter(Boolean).join('/');
|
|
35
9
|
const WINDOWS_RESERVED_NAME = /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\..*)?$/i;
|
|
10
|
+
/** Ищет управляющие символы ASCII с кодами 0–31 или 127.
|
|
11
|
+
* @example hasControlCharacter('abc') → false; строка с переводом строки → true.
|
|
12
|
+
*/
|
|
36
13
|
const hasControlCharacter = (value) => [...value].some((character) => {
|
|
37
14
|
const code = character.charCodeAt(0);
|
|
38
15
|
return code <= 31 || code === 127;
|
|
39
16
|
});
|
|
17
|
+
/** Проверяет безопасное имя файла, включая зарезервированные имена Windows.
|
|
18
|
+
* @example validateName('a.txt', 'name') → 'a.txt'; validateName('../a', 'name') → ошибка.
|
|
19
|
+
*/
|
|
40
20
|
export const validateName = (value, source) => {
|
|
41
21
|
if (typeof value !== 'string' ||
|
|
42
22
|
value === '' ||
|
|
@@ -47,30 +27,39 @@ export const validateName = (value, source) => {
|
|
|
47
27
|
/[<>:"/\\|?*]/.test(value) ||
|
|
48
28
|
hasControlCharacter(value) ||
|
|
49
29
|
WINDOWS_RESERVED_NAME.test(value)) {
|
|
50
|
-
throw
|
|
30
|
+
throw domainError('INVALID_INPUT', `${source} должен содержать безопасное имя файла`);
|
|
51
31
|
}
|
|
52
32
|
return value;
|
|
53
33
|
};
|
|
34
|
+
/** Проверяет относительный путь с разделителем /; пустой путь обозначает корень.
|
|
35
|
+
* @example validateDirectory('photos/2026', 'path') → 'photos/2026'; '../photos' → ошибка.
|
|
36
|
+
*/
|
|
54
37
|
export const validateDirectory = (value, source) => {
|
|
55
38
|
if (typeof value !== 'string') {
|
|
56
|
-
throw
|
|
39
|
+
throw domainError('INVALID_INPUT', `${source} должен содержать безопасный относительный путь`);
|
|
57
40
|
}
|
|
58
41
|
if (value === '') {
|
|
59
42
|
return value;
|
|
60
43
|
}
|
|
61
44
|
const parts = value.split('/');
|
|
62
45
|
if (value.startsWith('/') || value.endsWith('/') || value.includes('\\') || value.includes('\0') || parts.some((part) => part === '' || part === '.' || part === '..')) {
|
|
63
|
-
throw
|
|
46
|
+
throw domainError('INVALID_INPUT', `${source} должен содержать безопасный относительный путь`);
|
|
64
47
|
}
|
|
65
48
|
return value;
|
|
66
49
|
};
|
|
50
|
+
/** Удаляет параметры MIME-типа, приводит его к нижнему регистру и проверяет формат.
|
|
51
|
+
* @example normalizeMimeType('Text/Plain; charset=utf-8') → 'text/plain'.
|
|
52
|
+
*/
|
|
67
53
|
export const normalizeMimeType = (value, source = 'Заголовок Content-Type') => {
|
|
68
54
|
const mimeType = typeof value === 'string' ? value.split(';', 1)[0].trim().toLowerCase() : '';
|
|
69
55
|
if (mimeType === '' || !MIME_TYPE_PATTERN.test(mimeType)) {
|
|
70
|
-
throw
|
|
56
|
+
throw domainError('INVALID_INPUT', `${source} должен содержать корректный MIME-тип`);
|
|
71
57
|
}
|
|
72
58
|
return mimeType;
|
|
73
59
|
};
|
|
60
|
+
/** Проверяет метаданные и возвращает объект из каталога, имени и MIME-типа.
|
|
61
|
+
* @example { name: 'a.txt', mimeType: 'TEXT/PLAIN' } → { directory: '', name: 'a.txt', mimeType: 'text/plain' }.
|
|
62
|
+
*/
|
|
74
63
|
export const normalizeStoredFileMetadata = (value, source) => {
|
|
75
64
|
if (!isObject(value)) {
|
|
76
65
|
throw new Error(`${source} должна содержать JSON-объект`);
|
|
@@ -83,24 +72,16 @@ export const normalizeStoredFileMetadata = (value, source) => {
|
|
|
83
72
|
};
|
|
84
73
|
}
|
|
85
74
|
catch (error) {
|
|
86
|
-
throw new Error(
|
|
75
|
+
throw new Error(errorMessage(error), { cause: error });
|
|
87
76
|
}
|
|
88
77
|
};
|
|
78
|
+
/** Разделяет проверенный относительный путь на каталог и имя файла.
|
|
79
|
+
* @example getPathLocation('photos/a.jpg') → { directory: 'photos', name: 'a.jpg' }.
|
|
80
|
+
*/
|
|
89
81
|
export const getPathLocation = (path) => {
|
|
90
82
|
const normalizedPath = validateDirectory(path, 'Путь файла');
|
|
91
83
|
const parts = normalizedPath.split('/');
|
|
92
84
|
const name = validateName(parts.pop(), 'Путь файла');
|
|
93
85
|
return { directory: parts.join('/'), name };
|
|
94
86
|
};
|
|
95
|
-
const encodeFilePath = (file) => getFileKey(file).split('/').map(encodeURIComponent).join('/');
|
|
96
|
-
export const createFileMetadata = (file) => {
|
|
97
|
-
const path = encodeFilePath(file);
|
|
98
|
-
return {
|
|
99
|
-
...file,
|
|
100
|
-
downloadUrl: `${FILE_ROUTES.download}/${path}`,
|
|
101
|
-
metadataUrl: `${FILE_ROUTES.metadata}/${path}`,
|
|
102
|
-
url: `${FILE_ROUTES.storage}/${path}`,
|
|
103
|
-
};
|
|
104
|
-
};
|
|
105
|
-
export const getDownloadName = (name) => encodeURIComponent(basename(name)).replaceAll("'", '%27');
|
|
106
87
|
//# sourceMappingURL=contract.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../../../src/files/contract.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../../../src/files/contract.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type StoredFileMetadata } from './contract.js';
|
|
2
|
+
/** Читает массив метаданных и индексирует его по относительному пути файла.
|
|
3
|
+
* @example Отсутствующий файл → пустой Map; две одинаковые записи → ошибка.
|
|
4
|
+
*/
|
|
5
|
+
export declare function readDiskMetadata(metadataPath: string): Promise<Map<string, StoredFileMetadata>>;
|
|
6
|
+
/** Атомарно заменяет файл метаданных через соседний временный файл.
|
|
7
|
+
* @example Map с одной записью → JSON-массив в metadataPath.
|
|
8
|
+
*/
|
|
9
|
+
export declare function writeDiskMetadata(metadataPath: string, files: Map<string, StoredFileMetadata>): Promise<void>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { randomBytes } from 'node:crypto';
|
|
2
|
+
import { readFile, rename, rm, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { isSystemError } from '../core/utils.js';
|
|
4
|
+
import { getFileKey, normalizeStoredFileMetadata } from './contract.js';
|
|
5
|
+
/** Читает массив метаданных и индексирует его по относительному пути файла.
|
|
6
|
+
* @example Отсутствующий файл → пустой Map; две одинаковые записи → ошибка.
|
|
7
|
+
*/
|
|
8
|
+
export async function readDiskMetadata(metadataPath) {
|
|
9
|
+
let source;
|
|
10
|
+
try {
|
|
11
|
+
source = JSON.parse(await readFile(metadataPath, 'utf8'));
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
if (isSystemError(error) && error.code === 'ENOENT')
|
|
15
|
+
return new Map();
|
|
16
|
+
throw error;
|
|
17
|
+
}
|
|
18
|
+
if (!Array.isArray(source))
|
|
19
|
+
throw new Error(`Файл метаданных ${metadataPath} должен содержать JSON-массив`);
|
|
20
|
+
const files = new Map();
|
|
21
|
+
source.forEach((value, index) => {
|
|
22
|
+
const file = normalizeStoredFileMetadata(value, `Запись ${index} в файле метаданных ${metadataPath}`);
|
|
23
|
+
const path = getFileKey(file);
|
|
24
|
+
if (files.has(path))
|
|
25
|
+
throw new Error(`Файл метаданных ${metadataPath} содержит повторяющийся путь «${path}»`);
|
|
26
|
+
files.set(path, file);
|
|
27
|
+
});
|
|
28
|
+
return files;
|
|
29
|
+
}
|
|
30
|
+
/** Атомарно заменяет файл метаданных через соседний временный файл.
|
|
31
|
+
* @example Map с одной записью → JSON-массив в metadataPath.
|
|
32
|
+
*/
|
|
33
|
+
export async function writeDiskMetadata(metadataPath, files) {
|
|
34
|
+
const temporaryPath = `${metadataPath}.${randomBytes(6).toString('hex')}.tmp`;
|
|
35
|
+
try {
|
|
36
|
+
await writeFile(temporaryPath, JSON.stringify([...files.values()], null, 2), { encoding: 'utf8', flag: 'wx' });
|
|
37
|
+
await rename(temporaryPath, metadataPath);
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
await rm(temporaryPath, { force: true }).catch(() => undefined);
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=disk-metadata.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"disk-metadata.js","sourceRoot":"","sources":["../../../src/files/disk-metadata.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,2BAA2B,EAA2B,MAAM,eAAe,CAAC;AAEjG;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,YAAoB;IACzD,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC;IAC5D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,aAAa,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,GAAG,EAAE,CAAC;QACtE,MAAM,KAAK,CAAC;IACd,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,YAAY,+BAA+B,CAAC,CAAC;IAC5G,MAAM,KAAK,GAAG,IAAI,GAAG,EAA8B,CAAC;IACpD,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;QAC9B,MAAM,IAAI,GAAG,2BAA2B,CAAC,KAAK,EAAE,UAAU,KAAK,uBAAuB,YAAY,EAAE,CAAC,CAAC;QACtG,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;QAC9B,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,YAAY,iCAAiC,IAAI,GAAG,CAAC,CAAC;QAC9G,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACxB,CAAC,CAAC,CAAC;IACH,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,YAAoB,EAAE,KAAsC;IAClG,MAAM,aAAa,GAAG,GAAG,YAAY,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC;IAC9E,IAAI,CAAC;QACH,MAAM,SAAS,CAAC,aAAa,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/G,MAAM,MAAM,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC;IAC5C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,EAAE,CAAC,aAAa,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAChE,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
declare const pathExists: (path: string) => Promise<boolean>;
|
|
2
|
+
/** Готовит и проверяет пути дискового хранилища, не выполняя операций с содержимым файлов.
|
|
3
|
+
* @example resolveExistingPath('images/a.png') → абсолютный путь внутри directory.
|
|
4
|
+
*/
|
|
5
|
+
export declare function createDiskPaths({ directory, metadata, staging, protectedPaths }: {
|
|
6
|
+
directory: string;
|
|
7
|
+
metadata: string;
|
|
8
|
+
staging: string;
|
|
9
|
+
protectedPaths: Set<string>;
|
|
10
|
+
}): Promise<{
|
|
11
|
+
pathExists: typeof pathExists;
|
|
12
|
+
prepareTargetPath: (path: string) => Promise<string>;
|
|
13
|
+
resolveExistingPath: (path: string) => Promise<string>;
|
|
14
|
+
}>;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { access, lstat, mkdir, realpath } from 'node:fs/promises';
|
|
2
|
+
import { dirname, isAbsolute, relative, resolve, sep } from 'node:path';
|
|
3
|
+
import { domainError } from '../core/errors.js';
|
|
4
|
+
import { isSystemError } from '../core/utils.js';
|
|
5
|
+
const pathExists = async (path) => {
|
|
6
|
+
try {
|
|
7
|
+
await access(path);
|
|
8
|
+
return true;
|
|
9
|
+
}
|
|
10
|
+
catch (error) {
|
|
11
|
+
if (isSystemError(error) && error.code === 'ENOENT')
|
|
12
|
+
return false;
|
|
13
|
+
throw error;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
const isPathInside = (rootPath, targetPath) => {
|
|
17
|
+
const relativePath = relative(rootPath, targetPath);
|
|
18
|
+
return relativePath === '' || (relativePath !== '..' && !relativePath.startsWith(`..${sep}`) && !isAbsolute(relativePath));
|
|
19
|
+
};
|
|
20
|
+
const assertRegularFile = (stats) => {
|
|
21
|
+
if (!stats.isFile())
|
|
22
|
+
throw domainError('INVALID_INPUT', 'Путь должен указывать на обычный файл');
|
|
23
|
+
};
|
|
24
|
+
/** Готовит и проверяет пути дискового хранилища, не выполняя операций с содержимым файлов.
|
|
25
|
+
* @example resolveExistingPath('images/a.png') → абсолютный путь внутри directory.
|
|
26
|
+
*/
|
|
27
|
+
export async function createDiskPaths({ directory, metadata, staging, protectedPaths }) {
|
|
28
|
+
const realDirectory = await realpath(directory);
|
|
29
|
+
const assertContained = (path) => {
|
|
30
|
+
if (!isPathInside(realDirectory, path))
|
|
31
|
+
throw domainError('INVALID_INPUT', 'Путь файла выходит за пределы директории хранения');
|
|
32
|
+
};
|
|
33
|
+
const resolveFilePath = (path) => {
|
|
34
|
+
const filePath = resolve(directory, path);
|
|
35
|
+
const canonical = resolve(realDirectory, relative(directory, filePath));
|
|
36
|
+
if (protectedPaths.has(canonical))
|
|
37
|
+
throw domainError('INVALID_INPUT', 'Path is reserved for a server input file');
|
|
38
|
+
if (filePath === directory || !isPathInside(directory, filePath) || filePath === metadata || isPathInside(staging, filePath))
|
|
39
|
+
throw domainError('INVALID_INPUT', 'Путь файла выходит за пределы директории хранения');
|
|
40
|
+
return filePath;
|
|
41
|
+
};
|
|
42
|
+
const assertNoSymlinks = async (targetPath) => {
|
|
43
|
+
const relativePath = relative(directory, targetPath);
|
|
44
|
+
const parts = relativePath === '' ? [] : relativePath.split(sep);
|
|
45
|
+
let currentPath = directory;
|
|
46
|
+
for (const part of parts) {
|
|
47
|
+
currentPath = resolve(currentPath, part);
|
|
48
|
+
try {
|
|
49
|
+
if ((await lstat(currentPath)).isSymbolicLink())
|
|
50
|
+
throw domainError('INVALID_INPUT', 'Путь файла не должен содержать символические ссылки');
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
if (!isSystemError(error) || error.code !== 'ENOENT')
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
const prepareTargetPath = async (path) => {
|
|
59
|
+
const filePath = resolveFilePath(path);
|
|
60
|
+
const targetDirectory = dirname(filePath);
|
|
61
|
+
const parts = relative(directory, targetDirectory).split(sep).filter(Boolean);
|
|
62
|
+
let currentPath = directory;
|
|
63
|
+
for (const part of parts) {
|
|
64
|
+
currentPath = resolve(currentPath, part);
|
|
65
|
+
try {
|
|
66
|
+
await mkdir(currentPath);
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
if (!isSystemError(error) || error.code !== 'EEXIST')
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
const currentStats = await lstat(currentPath);
|
|
73
|
+
if (currentStats.isSymbolicLink() || !currentStats.isDirectory())
|
|
74
|
+
throw domainError('INVALID_INPUT', 'Путь файла должен содержать только обычные директории');
|
|
75
|
+
assertContained(await realpath(currentPath));
|
|
76
|
+
}
|
|
77
|
+
if (await pathExists(filePath)) {
|
|
78
|
+
await assertNoSymlinks(filePath);
|
|
79
|
+
assertContained(await realpath(filePath));
|
|
80
|
+
assertRegularFile(await lstat(filePath));
|
|
81
|
+
}
|
|
82
|
+
return filePath;
|
|
83
|
+
};
|
|
84
|
+
const resolveExistingPath = async (path) => {
|
|
85
|
+
const filePath = resolveFilePath(path);
|
|
86
|
+
try {
|
|
87
|
+
await assertNoSymlinks(filePath);
|
|
88
|
+
assertContained(await realpath(filePath));
|
|
89
|
+
assertRegularFile(await lstat(filePath));
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
if (isSystemError(error) && error.code === 'ENOENT')
|
|
93
|
+
throw domainError('NOT_FOUND', 'Файл не найден');
|
|
94
|
+
throw error;
|
|
95
|
+
}
|
|
96
|
+
return filePath;
|
|
97
|
+
};
|
|
98
|
+
return { pathExists, prepareTargetPath, resolveExistingPath };
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=disk-paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"disk-paths.js","sourceRoot":"","sources":["../../../src/files/disk-paths.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { type FileStore } from './contract.js';
|
|
2
|
-
|
|
2
|
+
/** Открывает дисковое хранилище с проверками путей, защитой входных файлов и последовательными изменениями.
|
|
3
|
+
* @example Каталог и файл метаданных → Promise<FileStore>; операции сохраняют байты и метаданные на диске.
|
|
4
|
+
*/
|
|
5
|
+
export declare const createDiskFileStore: ({ directory: sourceDirectoryPath, metadata: sourceMetadataPath, protectedPaths, }: {
|
|
3
6
|
directory: string;
|
|
4
7
|
metadata: string;
|
|
8
|
+
protectedPaths?: string[];
|
|
5
9
|
}) => Promise<FileStore>;
|