@tstdl/base 0.92.132 → 0.92.135
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/api/response.js +6 -6
- package/api/server/api-request-token.provider.d.ts +3 -0
- package/api/server/api-request-token.provider.js +9 -0
- package/api/server/module.js +1 -1
- package/database/mongo/module.js +6 -6
- package/document-management/api/document-management.api.d.ts +20 -4
- package/document-management/api/document-management.api.js +9 -3
- package/document-management/server/api/document-management.api.d.ts +1 -0
- package/document-management/server/api/document-management.api.js +9 -2
- package/document-management/server/module.d.ts +1 -0
- package/document-management/server/module.js +1 -0
- package/document-management/server/services/document-file.service.d.ts +16 -0
- package/document-management/server/services/document-file.service.js +55 -25
- package/document-management/server/services/document-management-ai.service.js +1 -1
- package/document-management/server/services/document-management-ancillary.service.d.ts +2 -2
- package/document-management/server/services/document-management.service.js +23 -11
- package/document-management/server/services/document-workflow.service.d.ts +1 -0
- package/document-management/server/services/document-workflow.service.js +15 -4
- package/document-management/server/services/document.service.d.ts +5 -1
- package/document-management/server/services/document.service.js +13 -10
- package/document-management/service-models/document-management.view-model.d.ts +15 -4
- package/document-management/service-models/document-management.view-model.js +42 -12
- package/document-management/service-models/document.service-model.d.ts +1 -0
- package/document-management/service-models/document.service-model.js +1 -0
- package/document-management/service-models/enriched/enriched-document-assignment.view.d.ts +13 -4
- package/document-management/service-models/enriched/enriched-document-assignment.view.js +29 -7
- package/document-management/service-models/enriched/enriched-document-collection.view.js +1 -1
- package/document-management/service-models/enriched/enriched-document-request.view.d.ts +1 -1
- package/document-management/service-models/enriched/enriched-document.view.d.ts +2 -2
- package/document-management/service-models/enriched/enriched-document.view.js +2 -6
- package/examples/document-management/main.d.ts +1 -1
- package/examples/document-management/main.js +20 -8
- package/http/client/adapters/undici.adapter.js +3 -3
- package/http/client/http-client.js +29 -30
- package/http/http-body.js +4 -4
- package/http/http.error.d.ts +5 -1
- package/http/http.error.js +6 -6
- package/http/utils.js +4 -4
- package/injector/decorators.d.ts +1 -1
- package/injector/injector.d.ts +1 -1
- package/injector/interfaces.d.ts +1 -1
- package/injector/provider.d.ts +4 -4
- package/object-storage/object-storage.d.ts +38 -2
- package/object-storage/s3/s3.object-storage-provider.js +1 -1
- package/object-storage/s3/s3.object-storage.d.ts +6 -3
- package/object-storage/s3/s3.object-storage.js +88 -14
- package/object-storage/s3/s3.object.js +2 -3
- package/orm/server/repository.js +37 -37
- package/package.json +1 -1
- package/schema/schema.error.js +4 -7
- package/search-index/elastic/module.js +5 -5
- package/utils/cryptography.js +18 -18
- package/utils/object/object.d.ts +3 -2
- package/utils/object/object.js +5 -2
- package/utils/stream/size-limited-stream.js +1 -1
- package/utils/type-guards.d.ts +7 -1
- package/utils/type-guards.js +13 -1
package/api/response.js
CHANGED
|
@@ -35,8 +35,8 @@ export function createErrorResponse(errorOrName, message = '', details) {
|
|
|
35
35
|
name: errorOrName.name,
|
|
36
36
|
message: errorOrName.message,
|
|
37
37
|
details,
|
|
38
|
-
data
|
|
39
|
-
}
|
|
38
|
+
data,
|
|
39
|
+
},
|
|
40
40
|
};
|
|
41
41
|
}
|
|
42
42
|
else {
|
|
@@ -44,8 +44,8 @@ export function createErrorResponse(errorOrName, message = '', details) {
|
|
|
44
44
|
error: {
|
|
45
45
|
name: errorOrName.name,
|
|
46
46
|
message: errorOrName.message,
|
|
47
|
-
details
|
|
48
|
-
}
|
|
47
|
+
details,
|
|
48
|
+
},
|
|
49
49
|
};
|
|
50
50
|
}
|
|
51
51
|
}
|
|
@@ -54,8 +54,8 @@ export function createErrorResponse(errorOrName, message = '', details) {
|
|
|
54
54
|
error: {
|
|
55
55
|
name: errorOrName,
|
|
56
56
|
message,
|
|
57
|
-
details
|
|
58
|
-
}
|
|
57
|
+
details,
|
|
58
|
+
},
|
|
59
59
|
};
|
|
60
60
|
}
|
|
61
61
|
return response;
|
|
@@ -5,3 +5,6 @@ export declare abstract class ApiRequestTokenProvider {
|
|
|
5
5
|
export declare class NoopApiRequestTokenProvider extends ApiRequestTokenProvider {
|
|
6
6
|
getToken<T>(): T;
|
|
7
7
|
}
|
|
8
|
+
export declare class MockApiRequestTokenProvider extends ApiRequestTokenProvider {
|
|
9
|
+
getToken<T>(): T;
|
|
10
|
+
}
|
|
@@ -16,3 +16,12 @@ NoopApiRequestTokenProvider = __decorate([
|
|
|
16
16
|
Singleton({ alias: ApiRequestTokenProvider })
|
|
17
17
|
], NoopApiRequestTokenProvider);
|
|
18
18
|
export { NoopApiRequestTokenProvider };
|
|
19
|
+
let MockApiRequestTokenProvider = class MockApiRequestTokenProvider extends ApiRequestTokenProvider {
|
|
20
|
+
getToken() {
|
|
21
|
+
return {};
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
MockApiRequestTokenProvider = __decorate([
|
|
25
|
+
Singleton({ alias: ApiRequestTokenProvider })
|
|
26
|
+
], MockApiRequestTokenProvider);
|
|
27
|
+
export { MockApiRequestTokenProvider };
|
package/api/server/module.js
CHANGED
|
@@ -4,7 +4,7 @@ import { ensureApiController } from './api-controller.js';
|
|
|
4
4
|
import { ApiRequestTokenProvider } from './api-request-token.provider.js';
|
|
5
5
|
import { API_CONTROLLER, API_MODULE_OPTIONS } from './tokens.js';
|
|
6
6
|
export const apiModuleOptions = {
|
|
7
|
-
controllers: []
|
|
7
|
+
controllers: [],
|
|
8
8
|
};
|
|
9
9
|
export function configureApiServer(options) {
|
|
10
10
|
if (isDefined(options.controllers)) {
|
package/database/mongo/module.js
CHANGED
|
@@ -6,7 +6,7 @@ import { Collection, Database, MongoClient } from './classes.js';
|
|
|
6
6
|
export const mongoModuleConfig = {
|
|
7
7
|
defaultConnection: { url: 'mongodb://localhost:27017/test-db' },
|
|
8
8
|
defaultDatabase: undefined,
|
|
9
|
-
logPrefix: 'Mongo'
|
|
9
|
+
logPrefix: 'Mongo',
|
|
10
10
|
};
|
|
11
11
|
export function configureMongo(config) {
|
|
12
12
|
mongoModuleConfig.defaultDatabase = config.defaultDatabase ?? mongoModuleConfig.defaultDatabase;
|
|
@@ -31,10 +31,10 @@ Injector.registerSingleton(MongoClient, {
|
|
|
31
31
|
},
|
|
32
32
|
async afterResolve(client, _argument, { cancellationSignal, data: { url, logger } }) {
|
|
33
33
|
await connect(`mongo at ${url}`, async () => client.connect(), logger, cancellationSignal);
|
|
34
|
-
}
|
|
34
|
+
},
|
|
35
35
|
}, {
|
|
36
36
|
defaultArgumentProvider: () => mongoModuleConfig.defaultConnection,
|
|
37
|
-
argumentIdentityProvider: JSON.stringify
|
|
37
|
+
argumentIdentityProvider: JSON.stringify,
|
|
38
38
|
});
|
|
39
39
|
Injector.registerSingleton(Database, {
|
|
40
40
|
useFactory: (argument, context) => {
|
|
@@ -45,7 +45,7 @@ Injector.registerSingleton(Database, {
|
|
|
45
45
|
},
|
|
46
46
|
defaultArgumentProvider: () => ({ database: mongoModuleConfig.defaultDatabase, connection: mongoModuleConfig.defaultConnection }),
|
|
47
47
|
}, {
|
|
48
|
-
argumentIdentityProvider: JSON.stringify
|
|
48
|
+
argumentIdentityProvider: JSON.stringify,
|
|
49
49
|
});
|
|
50
50
|
Injector.registerSingleton(Collection, {
|
|
51
51
|
useFactory: (config, context) => {
|
|
@@ -62,7 +62,7 @@ Injector.registerSingleton(Collection, {
|
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
await database.createCollection(config.collection);
|
|
65
|
-
}
|
|
65
|
+
},
|
|
66
66
|
}, {
|
|
67
|
-
argumentIdentityProvider: JSON.stringify
|
|
67
|
+
argumentIdentityProvider: JSON.stringify,
|
|
68
68
|
});
|
|
@@ -71,6 +71,15 @@ export declare const documentManagementApiDefinition: {
|
|
|
71
71
|
result: typeof DocumentType;
|
|
72
72
|
credentials: true;
|
|
73
73
|
};
|
|
74
|
+
initiateDocumentUpload: {
|
|
75
|
+
resource: string;
|
|
76
|
+
method: "POST";
|
|
77
|
+
result: import("../../schema/index.js").ObjectSchema<{
|
|
78
|
+
uploadId: string;
|
|
79
|
+
uploadUrl: string;
|
|
80
|
+
}>;
|
|
81
|
+
credentials: true;
|
|
82
|
+
};
|
|
74
83
|
createDocument: {
|
|
75
84
|
resource: string;
|
|
76
85
|
method: "POST";
|
|
@@ -94,6 +103,7 @@ export declare const documentManagementApiDefinition: {
|
|
|
94
103
|
scope: string | string[];
|
|
95
104
|
};
|
|
96
105
|
};
|
|
106
|
+
uploadId: string;
|
|
97
107
|
properties?: {
|
|
98
108
|
propertyId: import("../../orm/schemas/uuid.js").Uuid;
|
|
99
109
|
value: string | number | boolean | null;
|
|
@@ -105,8 +115,6 @@ export declare const documentManagementApiDefinition: {
|
|
|
105
115
|
attributes: import("../../orm/types.js").HasDefault<import("../../orm/schemas/json.js").Json<import("../../orm/entity.js").EntityMetadataAttributes>>;
|
|
106
116
|
}> | undefined;
|
|
107
117
|
}>;
|
|
108
|
-
body: Uint8ArrayConstructor;
|
|
109
|
-
maxBytes: number;
|
|
110
118
|
result: typeof Document;
|
|
111
119
|
credentials: true;
|
|
112
120
|
};
|
|
@@ -370,6 +378,15 @@ declare const _DocumentManagementApi: import("../../api/client/index.js").ApiCli
|
|
|
370
378
|
result: typeof DocumentType;
|
|
371
379
|
credentials: true;
|
|
372
380
|
};
|
|
381
|
+
initiateDocumentUpload: {
|
|
382
|
+
resource: string;
|
|
383
|
+
method: "POST";
|
|
384
|
+
result: import("../../schema/index.js").ObjectSchema<{
|
|
385
|
+
uploadId: string;
|
|
386
|
+
uploadUrl: string;
|
|
387
|
+
}>;
|
|
388
|
+
credentials: true;
|
|
389
|
+
};
|
|
373
390
|
createDocument: {
|
|
374
391
|
resource: string;
|
|
375
392
|
method: "POST";
|
|
@@ -393,6 +410,7 @@ declare const _DocumentManagementApi: import("../../api/client/index.js").ApiCli
|
|
|
393
410
|
scope: string | string[];
|
|
394
411
|
};
|
|
395
412
|
};
|
|
413
|
+
uploadId: string;
|
|
396
414
|
properties?: {
|
|
397
415
|
propertyId: import("../../orm/schemas/uuid.js").Uuid;
|
|
398
416
|
value: string | number | boolean | null;
|
|
@@ -404,8 +422,6 @@ declare const _DocumentManagementApi: import("../../api/client/index.js").ApiCli
|
|
|
404
422
|
attributes: import("../../orm/types.js").HasDefault<import("../../orm/schemas/json.js").Json<import("../../orm/entity.js").EntityMetadataAttributes>>;
|
|
405
423
|
}> | undefined;
|
|
406
424
|
}>;
|
|
407
|
-
body: Uint8ArrayConstructor;
|
|
408
|
-
maxBytes: number;
|
|
409
425
|
result: typeof Document;
|
|
410
426
|
credentials: true;
|
|
411
427
|
};
|
|
@@ -8,7 +8,6 @@ import { compileClient } from '../../api/client/index.js';
|
|
|
8
8
|
import { defineApi } from '../../api/index.js';
|
|
9
9
|
import { ReplaceClass } from '../../injector/decorators.js';
|
|
10
10
|
import { array, boolean, literal, object, optional, string } from '../../schema/index.js';
|
|
11
|
-
import { megabyte } from '../../utils/units.js';
|
|
12
11
|
import { Document, DocumentCategory, DocumentRequest, DocumentRequestsTemplate, DocumentRequestTemplate, DocumentType } from '../models/index.js';
|
|
13
12
|
import { addOrArchiveDocumentToOrFromCollectionParametersSchema, applyDocumentRequestsTemplateParametersSchema, createDocumentCategoryParametersSchema, createDocumentParametersSchema, createDocumentRequestParametersSchema, createDocumentRequestsTemplateParametersSchema, createDocumentRequestTemplateParametersSchema, createDocumentTypeParametersSchema, deleteDocumentRequestParametersSchema, deleteDocumentRequestsTemplateParametersSchema, deleteDocumentRequestTemplateParametersSchema, DocumentCategoryView, DocumentManagementData, DocumentRequestsTemplateData, loadDataParametersSchema, updateDocumentParametersSchema, updateDocumentRequestParametersSchema, updateDocumentRequestsTemplateParametersSchema, updateDocumentRequestTemplateParametersSchema } from '../service-models/index.js';
|
|
14
13
|
export const documentManagementApiDefinition = defineApi({
|
|
@@ -67,12 +66,19 @@ export const documentManagementApiDefinition = defineApi({
|
|
|
67
66
|
result: DocumentType,
|
|
68
67
|
credentials: true,
|
|
69
68
|
},
|
|
69
|
+
initiateDocumentUpload: {
|
|
70
|
+
resource: 'document-uploads',
|
|
71
|
+
method: 'POST',
|
|
72
|
+
result: object({
|
|
73
|
+
uploadId: string(),
|
|
74
|
+
uploadUrl: string(),
|
|
75
|
+
}),
|
|
76
|
+
credentials: true,
|
|
77
|
+
},
|
|
70
78
|
createDocument: {
|
|
71
79
|
resource: 'documents',
|
|
72
80
|
method: 'POST',
|
|
73
81
|
parameters: createDocumentParametersSchema,
|
|
74
|
-
body: Uint8Array,
|
|
75
|
-
maxBytes: 50 * megabyte,
|
|
76
82
|
result: Document,
|
|
77
83
|
credentials: true,
|
|
78
84
|
},
|
|
@@ -9,6 +9,7 @@ export declare class DocumentManagementApiController implements ApiController<Do
|
|
|
9
9
|
getContentUrl(context: ApiRequestContext<DocumentManagementApiDefinition, 'getContentUrl'>): Promise<ApiServerResult<DocumentManagementApiDefinition, 'getContentUrl'>>;
|
|
10
10
|
createCategory(context: ApiRequestContext<DocumentManagementApiDefinition, 'createCategory'>): Promise<ApiServerResult<DocumentManagementApiDefinition, 'createCategory'>>;
|
|
11
11
|
createType(context: ApiRequestContext<DocumentManagementApiDefinition, 'createType'>): Promise<ApiServerResult<DocumentManagementApiDefinition, 'createType'>>;
|
|
12
|
+
initiateDocumentUpload(context: ApiRequestContext<DocumentManagementApiDefinition, 'initiateDocumentUpload'>): Promise<ApiServerResult<DocumentManagementApiDefinition, 'initiateDocumentUpload'>>;
|
|
12
13
|
createDocument(context: ApiRequestContext<DocumentManagementApiDefinition, 'createDocument'>): Promise<ApiServerResult<DocumentManagementApiDefinition, 'createDocument'>>;
|
|
13
14
|
createDocumentRequestsTemplate(context: ApiRequestContext<DocumentManagementApiDefinition, 'createDocumentRequestsTemplate'>): Promise<ApiServerResult<DocumentManagementApiDefinition, 'createDocumentRequestsTemplate'>>;
|
|
14
15
|
updateDocumentRequestsTemplate(_context: ApiRequestContext<DocumentManagementApiDefinition, 'updateDocumentRequestsTemplate'>): Promise<ApiServerResult<DocumentManagementApiDefinition, 'updateDocumentRequestsTemplate'>>;
|
|
@@ -92,8 +92,15 @@ let DocumentManagementApiController = DocumentManagementApiController_1 = class
|
|
|
92
92
|
}
|
|
93
93
|
return await this.#documentCategoryTypeService.createType(context.parameters.label, context.parameters.categoryId);
|
|
94
94
|
}
|
|
95
|
+
async initiateDocumentUpload(context) {
|
|
96
|
+
const token = await context.getToken();
|
|
97
|
+
const subject = await this.#ancillaryService.getSubject(token);
|
|
98
|
+
return await this.#documentFileService.initiateUpload({ key: subject });
|
|
99
|
+
}
|
|
95
100
|
async createDocument(context) {
|
|
96
101
|
const token = await context.getToken();
|
|
102
|
+
const subject = await this.#ancillaryService.getSubject(token);
|
|
103
|
+
const { uploadId, ...createParameters } = context.parameters;
|
|
97
104
|
const [collectionIds, requiresAssign] = await match(context.parameters.assignment)
|
|
98
105
|
.with({ collections: P.select() }, (collectionIds) => [toArray(collectionIds), true])
|
|
99
106
|
.with({ request: P.select() }, async (requestId) => {
|
|
@@ -115,8 +122,8 @@ let DocumentManagementApiController = DocumentManagementApiController_1 = class
|
|
|
115
122
|
}
|
|
116
123
|
}
|
|
117
124
|
}
|
|
118
|
-
const actionUserId = await this.#ancillaryService.
|
|
119
|
-
return await this.#documentService.create(
|
|
125
|
+
const actionUserId = await this.#ancillaryService.getSubject(token);
|
|
126
|
+
return await this.#documentService.create(createParameters, { uploadId, uploadKey: subject }, { createUserId: actionUserId });
|
|
120
127
|
}
|
|
121
128
|
async createDocumentRequestsTemplate(context) {
|
|
122
129
|
const token = await context.getToken();
|
|
@@ -5,6 +5,7 @@ import { DocumentManagementAncillaryService } from './services/document-manageme
|
|
|
5
5
|
export declare class DocumentManagementConfig {
|
|
6
6
|
ancillaryService: InjectionToken<DocumentManagementAncillaryService>;
|
|
7
7
|
fileObjectStorageModule: string;
|
|
8
|
+
fileUploadObjectStorageModule: string;
|
|
8
9
|
filePreviewObjectStorageModule: string;
|
|
9
10
|
database?: DatabaseConfig;
|
|
10
11
|
}
|
|
@@ -4,7 +4,23 @@ import { DocumentFile } from '../../models/index.js';
|
|
|
4
4
|
export declare class DocumentFileService extends Transactional {
|
|
5
5
|
#private;
|
|
6
6
|
load(id: string): Promise<DocumentFile>;
|
|
7
|
+
/**
|
|
8
|
+
* Initiates a file upload
|
|
9
|
+
* @param key - key which can be used to authorize the creation of the file. Same key must be provided to {@link create} method, or it will throw an error.
|
|
10
|
+
* The key could be the user id from the request token. This ensures that the file can only be created by the user who initiated the upload.
|
|
11
|
+
* @returns upload information
|
|
12
|
+
*/
|
|
13
|
+
initiateUpload({ key }: {
|
|
14
|
+
key: string;
|
|
15
|
+
}): Promise<{
|
|
16
|
+
uploadId: string;
|
|
17
|
+
uploadUrl: string;
|
|
18
|
+
}>;
|
|
7
19
|
create(content: Uint8Array | ReadableStream<Uint8Array>, originalFileName: string | null): Promise<DocumentFile>;
|
|
20
|
+
create(content: {
|
|
21
|
+
uploadId: string;
|
|
22
|
+
uploadKey: string;
|
|
23
|
+
}, originalFileName: string | null): Promise<[DocumentFile, Uint8Array]>;
|
|
8
24
|
getContent(fileId: string): Promise<Uint8Array>;
|
|
9
25
|
getContentStream(fileId: string): ReadableStream<Uint8Array>;
|
|
10
26
|
getContentUrl(fileId: string, title: string | null, download?: boolean): Promise<string>;
|
|
@@ -61,6 +61,7 @@ var DocumentFileService_1;
|
|
|
61
61
|
import sharp, {} from 'sharp';
|
|
62
62
|
import { match } from 'ts-pattern';
|
|
63
63
|
import { AiService } from '../../../ai/ai.service.js';
|
|
64
|
+
import { ForbiddenError } from '../../../errors/forbidden.error.js';
|
|
64
65
|
import { NotImplementedError } from '../../../errors/not-implemented.error.js';
|
|
65
66
|
import { getMimeType, getMimeTypeExtensions } from '../../../file/index.js';
|
|
66
67
|
import { TemporaryFile } from '../../../file/server/index.js';
|
|
@@ -70,30 +71,55 @@ import { ObjectStorage } from '../../../object-storage/index.js';
|
|
|
70
71
|
import { Transactional } from '../../../orm/server/index.js';
|
|
71
72
|
import { injectRepository } from '../../../orm/server/repository.js';
|
|
72
73
|
import { pdfToImage } from '../../../pdf/index.js';
|
|
74
|
+
import { Alphabet } from '../../../utils/alphabet.js';
|
|
73
75
|
import { digest } from '../../../utils/cryptography.js';
|
|
74
76
|
import { currentTimestamp } from '../../../utils/date-time.js';
|
|
77
|
+
import { getRandomString } from '../../../utils/random.js';
|
|
75
78
|
import { readableStreamFromPromise, readBinaryStream } from '../../../utils/stream/index.js';
|
|
76
|
-
import { isDefined, isUint8Array } from '../../../utils/type-guards.js';
|
|
77
|
-
import { millisecondsPerMinute } from '../../../utils/units.js';
|
|
79
|
+
import { isDefined, isNotReadableStream, isNotUint8Array, isUint8Array } from '../../../utils/type-guards.js';
|
|
80
|
+
import { millisecondsPerMinute, secondsPerMinute } from '../../../utils/units.js';
|
|
78
81
|
import { Document, DocumentFile } from '../../models/index.js';
|
|
79
82
|
import { DocumentManagementConfig } from '../module.js';
|
|
80
83
|
import { DocumentManagementSingleton } from './singleton.js';
|
|
81
84
|
let DocumentFileService = DocumentFileService_1 = class DocumentFileService extends Transactional {
|
|
85
|
+
#config = inject(DocumentManagementConfig);
|
|
82
86
|
#documentFileRepository = injectRepository(DocumentFile);
|
|
83
87
|
#documentRepository = injectRepository(Document);
|
|
84
88
|
#aiService = inject(AiService);
|
|
85
|
-
#fileObjectStorage = inject(ObjectStorage,
|
|
86
|
-
#filePreviewObjectStorage = inject(ObjectStorage,
|
|
89
|
+
#fileObjectStorage = inject(ObjectStorage, this.#config.fileObjectStorageModule);
|
|
90
|
+
#filePreviewObjectStorage = inject(ObjectStorage, this.#config.filePreviewObjectStorageModule);
|
|
91
|
+
#fileUploadObjectStorage = inject(ObjectStorage, { module: this.#config.fileUploadObjectStorageModule, configuration: { lifecycle: { expiration: { after: 5 * secondsPerMinute } } } });
|
|
87
92
|
#logger = inject(Logger, DocumentFileService_1.name);
|
|
88
93
|
#aiFilePartCache = new Map();
|
|
89
94
|
async load(id) {
|
|
90
|
-
return this.#documentFileRepository.load(id);
|
|
95
|
+
return await this.#documentFileRepository.load(id);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Initiates a file upload
|
|
99
|
+
* @param key - key which can be used to authorize the creation of the file. Same key must be provided to {@link create} method, or it will throw an error.
|
|
100
|
+
* The key could be the user id from the request token. This ensures that the file can only be created by the user who initiated the upload.
|
|
101
|
+
* @returns upload information
|
|
102
|
+
*/
|
|
103
|
+
async initiateUpload({ key }) {
|
|
104
|
+
const id = getRandomString(64, Alphabet.LowerUpperCaseNumbers);
|
|
105
|
+
const url = await this.#fileUploadObjectStorage.getUploadUrl(id, currentTimestamp() + (5 * millisecondsPerMinute), { metadata: { 'upload-key': key } });
|
|
106
|
+
return { uploadId: id, uploadUrl: url };
|
|
91
107
|
}
|
|
92
108
|
async create(content, originalFileName) {
|
|
93
|
-
const
|
|
109
|
+
const isUpload = isNotUint8Array(content) && isNotReadableStream(content);
|
|
110
|
+
if (isUpload) {
|
|
111
|
+
const object = await this.#fileUploadObjectStorage.getObject(content.uploadId);
|
|
112
|
+
const objectMetadata = await object.getMetadata();
|
|
113
|
+
if (content.uploadKey != objectMetadata['upload-key']) {
|
|
114
|
+
throw new ForbiddenError(`Invalid upload key`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const contentAsUint8Array = isUpload
|
|
118
|
+
? await this.#fileUploadObjectStorage.getContent(content.uploadId)
|
|
119
|
+
: (isUint8Array(content) ? content : await readBinaryStream(content));
|
|
94
120
|
const hash = await digest('SHA-256', contentAsUint8Array).toHex();
|
|
95
121
|
const mimeType = await getMimeType(contentAsUint8Array);
|
|
96
|
-
return this.transaction(async (tx) => {
|
|
122
|
+
return await this.transaction(async (tx) => {
|
|
97
123
|
const documentFile = await this.#documentFileRepository
|
|
98
124
|
.withTransaction(tx)
|
|
99
125
|
.insert({
|
|
@@ -102,39 +128,43 @@ let DocumentFileService = DocumentFileService_1 = class DocumentFileService exte
|
|
|
102
128
|
hash,
|
|
103
129
|
size: contentAsUint8Array.length,
|
|
104
130
|
});
|
|
105
|
-
const
|
|
106
|
-
|
|
131
|
+
const objectKey = getDocumentFileKey(documentFile.id);
|
|
132
|
+
if (isUpload) {
|
|
133
|
+
await this.#fileUploadObjectStorage.moveObject(content.uploadId, [this.#fileObjectStorage, objectKey]);
|
|
134
|
+
return [documentFile, contentAsUint8Array];
|
|
135
|
+
}
|
|
136
|
+
await this.#fileObjectStorage.uploadObject(objectKey, contentAsUint8Array, { contentLength: contentAsUint8Array.length, contentType: mimeType });
|
|
107
137
|
return documentFile;
|
|
108
138
|
});
|
|
109
139
|
}
|
|
110
140
|
async getContent(fileId) {
|
|
111
|
-
const
|
|
112
|
-
return this.#fileObjectStorage.getContent(
|
|
141
|
+
const objectKey = getDocumentFileKey(fileId);
|
|
142
|
+
return await this.#fileObjectStorage.getContent(objectKey);
|
|
113
143
|
}
|
|
114
144
|
getContentStream(fileId) {
|
|
115
|
-
const
|
|
116
|
-
return this.#fileObjectStorage.getContentStream(
|
|
145
|
+
const objectKey = getDocumentFileKey(fileId);
|
|
146
|
+
return this.#fileObjectStorage.getContentStream(objectKey);
|
|
117
147
|
}
|
|
118
148
|
async getContentUrl(fileId, title, download = false) {
|
|
119
149
|
const file = await this.#documentFileRepository.load(fileId);
|
|
120
|
-
return this.getDocumentFileContentObjectUrl(title ?? fileId, file, download);
|
|
150
|
+
return await this.getDocumentFileContentObjectUrl(title ?? fileId, file, download);
|
|
121
151
|
}
|
|
122
152
|
async getPreview(fileId, page = 1) {
|
|
123
|
-
const
|
|
153
|
+
const objectKey = getDocumentFileKey(fileId);
|
|
124
154
|
await this.createPreviewIfNotExists(fileId, page);
|
|
125
|
-
return this.#filePreviewObjectStorage.getContent(
|
|
155
|
+
return await this.#filePreviewObjectStorage.getContent(objectKey);
|
|
126
156
|
}
|
|
127
157
|
getPreviewStream(fileId, page = 1) {
|
|
128
158
|
return readableStreamFromPromise(async () => {
|
|
129
|
-
const
|
|
159
|
+
const objectKey = getDocumentFileKey(fileId);
|
|
130
160
|
await this.createPreviewIfNotExists(fileId, page);
|
|
131
|
-
return this.#filePreviewObjectStorage.getContentStream(
|
|
161
|
+
return this.#filePreviewObjectStorage.getContentStream(objectKey);
|
|
132
162
|
});
|
|
133
163
|
}
|
|
134
164
|
async getPreviewUrl(fileId, page = 1) {
|
|
135
|
-
const
|
|
165
|
+
const objectKey = getDocumentFileKey(fileId);
|
|
136
166
|
await this.createPreviewIfNotExists(fileId, page);
|
|
137
|
-
return this.#filePreviewObjectStorage.getDownloadUrl(
|
|
167
|
+
return await this.#filePreviewObjectStorage.getDownloadUrl(objectKey, currentTimestamp() + (5 * millisecondsPerMinute), {
|
|
138
168
|
'Response-Content-Type': 'image/jpeg',
|
|
139
169
|
});
|
|
140
170
|
}
|
|
@@ -175,11 +205,11 @@ let DocumentFileService = DocumentFileService_1 = class DocumentFileService exte
|
|
|
175
205
|
const image = await match(file.mimeType)
|
|
176
206
|
.with('application/pdf', async () => {
|
|
177
207
|
const imageBytes = await pdfToImage(content, page, 768, 'jpeg');
|
|
178
|
-
return imageToPreview(imageBytes);
|
|
208
|
+
return await imageToPreview(imageBytes);
|
|
179
209
|
})
|
|
180
|
-
.with('image/*', async () => imageToPreview(content))
|
|
210
|
+
.with('image/*', async () => await imageToPreview(content))
|
|
181
211
|
.otherwise(() => { throw new NotImplementedError('Preview generation is not implemented for this file type.'); });
|
|
182
|
-
await this.#filePreviewObjectStorage.uploadObject(key, image, { contentLength: image.length,
|
|
212
|
+
await this.#filePreviewObjectStorage.uploadObject(key, image, { contentLength: image.length, contentType: 'image/jpeg' });
|
|
183
213
|
}
|
|
184
214
|
}
|
|
185
215
|
async getDocumentFileContentObjectUrl(title, file, download) {
|
|
@@ -187,7 +217,7 @@ let DocumentFileService = DocumentFileService_1 = class DocumentFileService exte
|
|
|
187
217
|
const fileExtension = getMimeTypeExtensions(file.mimeType)[0] ?? 'bin';
|
|
188
218
|
const disposition = download ? 'attachment' : 'inline';
|
|
189
219
|
const filename = `${title}.${fileExtension}`;
|
|
190
|
-
return this.#fileObjectStorage.getDownloadUrl(key, currentTimestamp() + (5 * millisecondsPerMinute), {
|
|
220
|
+
return await this.#fileObjectStorage.getDownloadUrl(key, currentTimestamp() + (5 * millisecondsPerMinute), {
|
|
191
221
|
'Response-Content-Type': file.mimeType,
|
|
192
222
|
'Response-Content-Disposition': `${disposition}; filename = "${encodeURIComponent(filename)}"`,
|
|
193
223
|
});
|
|
@@ -201,7 +231,7 @@ function getDocumentFileKey(id) {
|
|
|
201
231
|
return `${id.slice(0, 2)}/${id.slice(0, 4)}/${id}`;
|
|
202
232
|
}
|
|
203
233
|
async function imageToPreview(input) {
|
|
204
|
-
return sharp(input)
|
|
234
|
+
return await sharp(input)
|
|
205
235
|
.resize({
|
|
206
236
|
width: 768,
|
|
207
237
|
height: 768,
|
|
@@ -93,7 +93,7 @@ let DocumentManagementAiService = DocumentManagementAiService_1 = class Document
|
|
|
93
93
|
try {
|
|
94
94
|
const document = await this.#documentRepository.load(documentId);
|
|
95
95
|
const file = await this.#documentFileService.load(document.fileId);
|
|
96
|
-
const fileContentStream = this.#documentFileService.getContentStream(document.
|
|
96
|
+
const fileContentStream = this.#documentFileService.getContentStream(document.fileId);
|
|
97
97
|
const tmpFile = __addDisposableResource(env_1, await TemporaryFile.from(fileContentStream), true);
|
|
98
98
|
const filePart = await this.#aiService.processFile({ path: tmpFile.path, mimeType: file.mimeType });
|
|
99
99
|
const categories = await this.#documentCategoryTypeService.loadCategoryViews();
|
|
@@ -28,10 +28,10 @@ export declare abstract class DocumentManagementAncillaryService<Token = unknown
|
|
|
28
28
|
*/
|
|
29
29
|
abstract _resolveMetadata(collections: DocumentCollection[]): DocumentCollectionMetadata[] | Promise<DocumentCollectionMetadata[]>;
|
|
30
30
|
/**
|
|
31
|
-
* Gets the
|
|
31
|
+
* Gets the subject from the request token.
|
|
32
32
|
* @param token The token of the request
|
|
33
33
|
*/
|
|
34
|
-
abstract
|
|
34
|
+
abstract getSubject(token?: Token): string | Promise<string>;
|
|
35
35
|
/**
|
|
36
36
|
* Checks if a user can read/view a specific document collection and its documents.
|
|
37
37
|
* @param collectionId The ID of the document collection.
|
|
@@ -6,14 +6,13 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
6
6
|
};
|
|
7
7
|
import { Enumerable } from '../../../enumerable/index.js';
|
|
8
8
|
import { inject } from '../../../injector/index.js';
|
|
9
|
-
import { getEntityMap } from '../../../orm/index.js';
|
|
10
9
|
import { Transactional, injectRepository } from '../../../orm/server/index.js';
|
|
11
10
|
import { distinct } from '../../../utils/array/index.js';
|
|
12
11
|
import { compareByValueSelectionToOrder } from '../../../utils/comparison.js';
|
|
13
12
|
import { groupToMap, groupToSingleMap } from '../../../utils/iterable-helpers/index.js';
|
|
14
13
|
import { fromEntries, objectEntries } from '../../../utils/object/index.js';
|
|
15
|
-
import { assertDefinedPass, isNotNull, isNotNullOrUndefined, isNull, isUndefined } from '../../../utils/type-guards.js';
|
|
16
|
-
import { DocumentApproval, DocumentCategory, DocumentCollection, DocumentCollectionAssignment, DocumentFile, DocumentRequest, DocumentRequestCollectionAssignment, DocumentRequestTemplate, DocumentRequestsTemplate, DocumentType, DocumentValidationExecution, DocumentWorkflowStep } from '../../models/index.js';
|
|
14
|
+
import { assertDefinedPass, isDefined, isNotNull, isNotNullOrUndefined, isNull, isUndefined } from '../../../utils/type-guards.js';
|
|
15
|
+
import { DocumentApproval, DocumentAssignmentScope, DocumentAssignmentTask, DocumentCategory, DocumentCollection, DocumentCollectionAssignment, DocumentFile, DocumentRequest, DocumentRequestCollectionAssignment, DocumentRequestTemplate, DocumentRequestsTemplate, DocumentType, DocumentValidationExecution, DocumentWorkflowStep } from '../../models/index.js';
|
|
17
16
|
import { DocumentCategoryTypeService, enumTypeKey } from './document-category-type.service.js';
|
|
18
17
|
import { DocumentManagementAncillaryService } from './document-management-ancillary.service.js';
|
|
19
18
|
import { DocumentPropertyService } from './document-property.service.js';
|
|
@@ -30,6 +29,8 @@ let DocumentManagementService = class DocumentManagementService extends Transact
|
|
|
30
29
|
#documentService = inject(DocumentService);
|
|
31
30
|
#documentPropertyService = inject(DocumentPropertyService);
|
|
32
31
|
#documentRequestCollectionAssignmentRepository = injectRepository(DocumentRequestCollectionAssignment);
|
|
32
|
+
#documentAssignmentTaskRepository = injectRepository(DocumentAssignmentTask);
|
|
33
|
+
#documentAssignmentScopeRepository = injectRepository(DocumentAssignmentScope);
|
|
33
34
|
#documentRequestRepository = injectRepository(DocumentRequest);
|
|
34
35
|
#documentRequestsTemplateRepository = injectRepository(DocumentRequestsTemplate);
|
|
35
36
|
#documentRequestTemplateRepository = injectRepository(DocumentRequestTemplate);
|
|
@@ -38,19 +39,25 @@ let DocumentManagementService = class DocumentManagementService extends Transact
|
|
|
38
39
|
#documentValidationExecutionRepository = injectRepository(DocumentValidationExecution);
|
|
39
40
|
async loadData(collectionIds) {
|
|
40
41
|
return await this.transaction(async (tx) => {
|
|
41
|
-
const [collections, collectionsMetadataMap,
|
|
42
|
+
const [collections, collectionsMetadataMap, documentCollectionAssignments, requestAssignments, assignmentScopes, categories, types] = await Promise.all([
|
|
42
43
|
this.#documentCollectionRepository.withTransaction(tx).loadMany(collectionIds),
|
|
43
44
|
this.#ancillaryService.resolveMetadataMap(...collectionIds),
|
|
44
45
|
this.#documentCollectionAssignmentRepository.withTransaction(tx).loadManyByQuery({ collectionId: { $in: collectionIds } }),
|
|
45
46
|
this.#documentRequestCollectionAssignmentRepository.withTransaction(tx).loadManyByQuery({ collectionId: { $in: collectionIds } }),
|
|
47
|
+
this.#documentAssignmentScopeRepository.withTransaction(tx).loadManyByQuery({ collectionId: { $in: collectionIds } }),
|
|
46
48
|
this.#documentCategoryRepository.withTransaction(tx).loadManyByQuery({}, { order: 'label' }),
|
|
47
49
|
this.#documentTypeRepository.withTransaction(tx).loadManyByQuery({}, { order: 'label' }),
|
|
48
50
|
]);
|
|
49
51
|
const requestIds = requestAssignments.map((requestCollection) => requestCollection.requestId);
|
|
50
|
-
const
|
|
51
|
-
const
|
|
52
|
+
const taskIds = assignmentScopes.map((scope) => scope.taskId);
|
|
53
|
+
const [requests, assignmentTasks] = await Promise.all([
|
|
54
|
+
this.#documentRequestRepository.withTransaction(tx).loadManyByQuery({ id: { $in: requestIds } }, { order: { 'metadata.createTimestamp': 'desc' } }),
|
|
55
|
+
this.#documentAssignmentTaskRepository.withTransaction(tx).loadMany(taskIds),
|
|
56
|
+
]);
|
|
57
|
+
const assignmentDocumentIds = documentCollectionAssignments.map((assignment) => assignment.documentId);
|
|
52
58
|
const requestDocumentIds = requests.map((request) => request.documentId).filter(isNotNull);
|
|
53
|
-
const
|
|
59
|
+
const assignmentTaskDocumentIds = assignmentTasks.map((task) => task.documentId);
|
|
60
|
+
const documentIds = distinct([...assignmentDocumentIds, ...requestDocumentIds, ...assignmentTaskDocumentIds]);
|
|
54
61
|
const [documents, propertyValues] = await Promise.all([
|
|
55
62
|
this.#documentService.withTransaction(tx).repository.loadManyByQuery({ id: { $in: documentIds } }, { order: { 'metadata.createTimestamp': 'desc' } }),
|
|
56
63
|
this.#documentPropertyService.withTransaction(tx).loadDocumentProperties(documentIds),
|
|
@@ -61,7 +68,6 @@ let DocumentManagementService = class DocumentManagementService extends Transact
|
|
|
61
68
|
this.#documentFileRepository.withTransaction(tx).loadManyByQuery({ id: { $in: documentFileIds } }, { order: { 'metadata.createTimestamp': 'desc' } }),
|
|
62
69
|
this.#documentWorkflowService.loadLatestWorkflows(workflowRelevantDocumentIds),
|
|
63
70
|
]);
|
|
64
|
-
const documentIdMap = getEntityMap(documents);
|
|
65
71
|
const documentWorkflowMap = groupToSingleMap(currentWorkflows, (workflow) => workflow.documentId);
|
|
66
72
|
const valuesMap = Enumerable.from(propertyValues).groupToMap((value) => value.documentId);
|
|
67
73
|
const validationWorkflowIds = currentWorkflows.map((workflow) => (workflow.step == DocumentWorkflowStep.Validation) ? workflow.id : null).filter(isNotNull);
|
|
@@ -78,9 +84,16 @@ let DocumentManagementService = class DocumentManagementService extends Transact
|
|
|
78
84
|
const documentViews = documents.map((document) => {
|
|
79
85
|
const currentWorkflow = documentWorkflowMap.get(document.id) ?? null;
|
|
80
86
|
const validations = (isNotNull(currentWorkflow) && (currentWorkflow.step == DocumentWorkflowStep.Validation)) ? workflowValidationMap.get(currentWorkflow.id) ?? [] : null;
|
|
87
|
+
const assignmentTask = assignmentTasks.find((task) => task.documentId == document.id);
|
|
88
|
+
const assignmentTaskScope = isDefined(assignmentTask) ? assignmentScopes.filter((scope) => scope.taskId == assignmentTask.id).map((scope) => scope.collectionId) : [];
|
|
81
89
|
return {
|
|
82
90
|
...document,
|
|
83
|
-
|
|
91
|
+
assignment: {
|
|
92
|
+
collections: documentCollectionAssignments.filter((collectionDocument) => collectionDocument.documentId == document.id),
|
|
93
|
+
assignmentTask: isDefined(assignmentTask)
|
|
94
|
+
? { target: assignmentTask.target, scope: assignmentTaskScope }
|
|
95
|
+
: null,
|
|
96
|
+
},
|
|
84
97
|
properties: valuesMap.get(document.id) ?? [],
|
|
85
98
|
currentWorkflow,
|
|
86
99
|
validations,
|
|
@@ -88,8 +101,7 @@ let DocumentManagementService = class DocumentManagementService extends Transact
|
|
|
88
101
|
});
|
|
89
102
|
const requestViews = requests.map((request) => ({
|
|
90
103
|
...request,
|
|
91
|
-
collectionIds: requestAssignments.filter((requestCollection) => requestCollection.requestId == request.id).map((requestCollection) => requestCollection.collectionId)
|
|
92
|
-
document: isNull(request.documentId) ? null : documentIdMap.get(request.documentId) ?? null,
|
|
104
|
+
collectionIds: requestAssignments.filter((requestCollection) => requestCollection.requestId == request.id).map((requestCollection) => requestCollection.collectionId)
|
|
93
105
|
}));
|
|
94
106
|
return {
|
|
95
107
|
collections: collectionViews,
|
|
@@ -4,6 +4,7 @@ import { afterResolve } from '../../../injector/interfaces.js';
|
|
|
4
4
|
import { Transactional } from '../../../orm/server/transactional.js';
|
|
5
5
|
export declare class DocumentWorkflowService extends Transactional {
|
|
6
6
|
#private;
|
|
7
|
+
private readonly documentService;
|
|
7
8
|
readonly repository: import("../../../orm/server/repository.js").EntityRepository<DocumentWorkflow>;
|
|
8
9
|
[afterResolve](_: unknown, { cancellationSignal }: AfterResolveContext<any>): void;
|
|
9
10
|
loadLatestWorkflow(documentId: string): Promise<DocumentWorkflow>;
|
|
@@ -21,6 +21,8 @@ import { Transactional } from '../../../orm/server/transactional.js';
|
|
|
21
21
|
import { Queue } from '../../../queue/queue.js';
|
|
22
22
|
import { _throw } from '../../../utils/throw.js';
|
|
23
23
|
import { isNotNull, isNull } from '../../../utils/type-guards.js';
|
|
24
|
+
import { desc, inArray } from 'drizzle-orm';
|
|
25
|
+
import { documentWorkflow } from '../schemas.js';
|
|
24
26
|
import { DocumentCollectionService } from './document-collection.service.js';
|
|
25
27
|
import { DocumentManagementAiService } from './document-management-ai.service.js';
|
|
26
28
|
import { DocumentRequestService } from './document-request.service.js';
|
|
@@ -28,13 +30,13 @@ import { DocumentService } from './document.service.js';
|
|
|
28
30
|
import { DocumentManagementSingleton } from './singleton.js';
|
|
29
31
|
let DocumentWorkflowService = DocumentWorkflowService_1 = class DocumentWorkflowService extends Transactional {
|
|
30
32
|
#documentManagementAiService = inject(DocumentManagementAiService);
|
|
31
|
-
#documentService = inject(DocumentService);
|
|
32
33
|
#documentCollectionService = inject(DocumentCollectionService);
|
|
33
34
|
#documentRequestService = inject(DocumentRequestService);
|
|
34
35
|
#documentAssignmentTaskRepository = injectRepository(DocumentAssignmentTask);
|
|
35
36
|
#documentAssignmentScopeRepository = injectRepository(DocumentAssignmentScope);
|
|
36
37
|
#queue = inject((Queue), { name: 'DocumentWorkflow', processTimeout: 5 * 60 * 1000, maxTries: 3 });
|
|
37
38
|
#logger = inject(Logger, DocumentWorkflowService_1.name);
|
|
39
|
+
documentService = inject(DocumentService, undefined, { forwardRef: true });
|
|
38
40
|
repository = injectRepository(DocumentWorkflow).withSession(this.session);
|
|
39
41
|
[afterResolve](_, { cancellationSignal }) {
|
|
40
42
|
if (this.isInTransaction) {
|
|
@@ -49,7 +51,16 @@ let DocumentWorkflowService = DocumentWorkflowService_1 = class DocumentWorkflow
|
|
|
49
51
|
return await this.repository.tryLoadByQuery({ documentId }, { order: { 'metadata.createTimestamp': 'desc' } });
|
|
50
52
|
}
|
|
51
53
|
async loadLatestWorkflows(documentIds) {
|
|
52
|
-
|
|
54
|
+
const orderedDocumentWorkflows = this.repository.session.$with('orderedDocumentWorkflows').as((qb) => qb
|
|
55
|
+
.select()
|
|
56
|
+
.from(documentWorkflow)
|
|
57
|
+
.where(inArray(documentWorkflow.documentId, documentIds))
|
|
58
|
+
.orderBy(desc(documentWorkflow.createTimestamp)));
|
|
59
|
+
const latestWorkflows = await this.repository.session
|
|
60
|
+
.with(orderedDocumentWorkflows)
|
|
61
|
+
.selectDistinctOn([orderedDocumentWorkflows.documentId])
|
|
62
|
+
.from(orderedDocumentWorkflows);
|
|
63
|
+
return await this.repository.mapManyToEntity(latestWorkflows);
|
|
53
64
|
}
|
|
54
65
|
async proceedWorkflow(documentId, userId) {
|
|
55
66
|
await this.transaction(async (tx) => {
|
|
@@ -104,11 +115,11 @@ let DocumentWorkflowService = DocumentWorkflowService_1 = class DocumentWorkflow
|
|
|
104
115
|
}
|
|
105
116
|
async processClassificationWorkflow(workflow) {
|
|
106
117
|
const typeId = await this.#documentManagementAiService.classifyDocumentType(workflow.documentId);
|
|
107
|
-
await this
|
|
118
|
+
await this.documentService.repository.update(workflow.documentId, { typeId, approval: DocumentApproval.Pending });
|
|
108
119
|
}
|
|
109
120
|
async processExtractionWorkflow(workflow) {
|
|
110
121
|
const extraction = await this.#documentManagementAiService.extractDocumentInformation(workflow.documentId);
|
|
111
|
-
await this
|
|
122
|
+
await this.documentService.update(workflow.documentId, extraction);
|
|
112
123
|
}
|
|
113
124
|
async processAssignmentWorkflow(workflow) {
|
|
114
125
|
const assignmentTask = await this.#documentAssignmentTaskRepository.loadByQuery({ documentId: workflow.documentId });
|