@jrmc/adonis-attachment 5.0.0-beta.1 → 5.0.0-beta.3
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/build/index.d.ts +0 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +0 -1
- package/build/providers/attachment_provider.d.ts.map +1 -1
- package/build/providers/attachment_provider.js +2 -1
- package/build/src/attachment_manager.d.ts +4 -2
- package/build/src/attachment_manager.d.ts.map +1 -1
- package/build/src/attachment_manager.js +3 -1
- package/build/src/attachments/attachment.d.ts +1 -1
- package/build/src/attachments/attachment.js +1 -1
- package/build/src/controllers/attachments_controller.d.ts.map +1 -1
- package/build/src/controllers/attachments_controller.js +74 -69
- package/build/src/decorators/attachment.d.ts +3 -3
- package/build/src/decorators/attachment.d.ts.map +1 -1
- package/build/src/services/record_with_attachment.d.ts.map +1 -1
- package/build/src/services/record_with_attachment.js +22 -9
- package/build/src/services/variant/variant_generator.d.ts +18 -0
- package/build/src/services/variant/variant_generator.d.ts.map +1 -0
- package/build/src/services/variant/variant_generator.js +91 -0
- package/build/src/services/variant/variant_persister.d.ts +17 -0
- package/build/src/services/variant/variant_persister.d.ts.map +1 -0
- package/build/src/services/variant/variant_persister.js +54 -0
- package/build/src/services/variant/variant_purger.d.ts +9 -0
- package/build/src/services/variant/variant_purger.d.ts.map +1 -0
- package/build/src/services/variant/variant_purger.js +42 -0
- package/build/src/services/variant_service.d.ts +7 -0
- package/build/src/services/variant_service.d.ts.map +1 -0
- package/build/src/services/variant_service.js +52 -0
- package/build/src/types/attachment.d.ts +3 -3
- package/build/src/types/attachment.d.ts.map +1 -1
- package/build/stubs/config.stub +102 -4
- package/build/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -8
- package/build/src/converter_manager.d.ts +0 -13
- package/build/src/converter_manager.d.ts.map +0 -1
- package/build/src/converter_manager.js +0 -121
package/build/index.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import attachmentManager from './services/main.js';
|
|
2
2
|
import RegenerateService from './services/regenerate_service.js';
|
|
3
3
|
export { configure } from './configure.js';
|
|
4
|
-
export { ConverterManager } from './src/converter_manager.js';
|
|
5
4
|
export { Attachment } from './src/attachments/attachment.js';
|
|
6
5
|
export { attachment } from './src/decorators/attachment.js';
|
|
7
6
|
export { attachments } from './src/decorators/attachment.js';
|
package/build/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,iBAAiB,MAAM,oBAAoB,CAAA;AAClD,OAAO,iBAAiB,MAAM,kCAAkC,CAAA;AAEhE,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,iBAAiB,MAAM,oBAAoB,CAAA;AAClD,OAAO,iBAAiB,MAAM,kCAAkC,CAAA;AAEhE,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAA;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAA;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAA;AACrD,OAAO,KAAK,MAAM,MAAM,iBAAiB,CAAA;AACzC,OAAO,EAAE,iBAAiB,EAAE,CAAA;AAC5B,OAAO,EAAE,iBAAiB,EAAE,CAAA;AAC5B,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,uBAAuB,CAAA"}
|
package/build/index.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import attachmentManager from './services/main.js';
|
|
2
2
|
import RegenerateService from './services/regenerate_service.js';
|
|
3
3
|
export { configure } from './configure.js';
|
|
4
|
-
export { ConverterManager } from './src/converter_manager.js';
|
|
5
4
|
export { Attachment } from './src/attachments/attachment.js';
|
|
6
5
|
export { attachment } from './src/decorators/attachment.js';
|
|
7
6
|
export { attachments } from './src/decorators/attachment.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attachment_provider.d.ts","sourceRoot":"","sources":["../../providers/attachment_provider.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AAC9D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAA;AAIhD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAE/D,OAAO,QAAQ,sBAAsB,CAAC;IACpC,UAAiB,iBAAiB;QAChC,iBAAiB,EAAE,iBAAiB,CAAA;KACrC;CACF;AAED,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,MAAM;QACd,WAAW,EAAE,CAAC,OAAO,CAAC,EAAE,MAAM,KAAK,KAAK,CAAA;KACzC;CACF;AAED,MAAM,CAAC,OAAO,OAAO,kBAAkB;;IAGzB,SAAS,CAAC,GAAG,EAAE,kBAAkB;gBAAvB,GAAG,EAAE,kBAAkB;IAE7C,QAAQ;
|
|
1
|
+
{"version":3,"file":"attachment_provider.d.ts","sourceRoot":"","sources":["../../providers/attachment_provider.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AAC9D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAA;AAIhD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAE/D,OAAO,QAAQ,sBAAsB,CAAC;IACpC,UAAiB,iBAAiB;QAChC,iBAAiB,EAAE,iBAAiB,CAAA;KACrC;CACF;AAED,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,MAAM;QACd,WAAW,EAAE,CAAC,OAAO,CAAC,EAAE,MAAM,KAAK,KAAK,CAAA;KACzC;CACF;AAED,MAAM,CAAC,OAAO,OAAO,kBAAkB;;IAGzB,SAAS,CAAC,GAAG,EAAE,kBAAkB;gBAAvB,GAAG,EAAE,kBAAkB;IAE7C,QAAQ;IAqBF,IAAI;CAQX"}
|
|
@@ -18,10 +18,11 @@ export default class AttachmentProvider {
|
|
|
18
18
|
const attachmentConfig = this.app.config.get('attachment');
|
|
19
19
|
const config = await configProvider.resolve(this.app, attachmentConfig);
|
|
20
20
|
const drive = await this.app.container.make('drive.manager');
|
|
21
|
+
const lock = await this.app.container.make('lock.manager');
|
|
21
22
|
if (!config) {
|
|
22
23
|
throw new RuntimeException('Invalid config exported from "config/attachment.ts" file. Make sure to use the defineConfig method');
|
|
23
24
|
}
|
|
24
|
-
this.#manager = new AttachmentManager(config, drive);
|
|
25
|
+
this.#manager = new AttachmentManager(config, drive, lock);
|
|
25
26
|
return this.#manager;
|
|
26
27
|
});
|
|
27
28
|
}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
|
|
6
6
|
*/
|
|
7
7
|
import type { DriveService, SignedURLOptions } from '@adonisjs/drive/types';
|
|
8
|
+
import type { LockService } from '@adonisjs/lock/types';
|
|
8
9
|
import type { MultipartFile } from '@adonisjs/core/bodyparser';
|
|
9
10
|
import type { AttachmentAttributes, AttachmentBase, Attachment as AttachmentType } from './types/attachment.js';
|
|
10
11
|
import type { ResolvedAttachmentConfig } from './define_config.js';
|
|
@@ -13,14 +14,15 @@ import Converter from './converters/converter.js';
|
|
|
13
14
|
export declare class AttachmentManager<KnownConverters extends Record<string, Converter>> {
|
|
14
15
|
#private;
|
|
15
16
|
queue: DeferQueue;
|
|
16
|
-
|
|
17
|
+
lock: LockService;
|
|
18
|
+
constructor(config: ResolvedAttachmentConfig<KnownConverters>, drive: DriveService, lock: LockService);
|
|
17
19
|
createFromDbResponse(response?: string | JSON): AttachmentType | null;
|
|
18
20
|
createFromFile(input: MultipartFile): Promise<AttachmentType>;
|
|
19
21
|
createFromFiles(inputs: MultipartFile[]): Promise<(AttachmentBase & {
|
|
20
22
|
originalName: string;
|
|
21
23
|
variants?: import("./types/attachment.js").Variant[];
|
|
22
24
|
createVariant(key: string, input: import("./types/input.js").Input): Promise<import("./types/attachment.js").Variant>;
|
|
23
|
-
getVariant(variantName: string): import("./types/attachment.js").Variant |
|
|
25
|
+
getVariant(variantName: string): import("./types/attachment.js").Variant | null;
|
|
24
26
|
getUrl(variantName?: string): Promise<string>;
|
|
25
27
|
getSignedUrl(variantNameOrOptions?: string | SignedURLOptions, signedUrlOptions?: SignedURLOptions): Promise<string>;
|
|
26
28
|
toObject(): AttachmentAttributes;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attachment_manager.d.ts","sourceRoot":"","sources":["../../src/attachment_manager.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAC3E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAA;AAC9D,OAAO,KAAK,EACV,oBAAoB,EACpB,cAAc,EACd,UAAU,IAAI,cAAc,EAC7B,MAAM,uBAAuB,CAAA;AAC9B,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAA;AAGlE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAG5C,OAAO,SAAS,MAAM,2BAA2B,CAAA;AAQjD,qBAAa,iBAAiB,CAAC,eAAe,SAAS,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC;;IAC9E,KAAK,aAAA;
|
|
1
|
+
{"version":3,"file":"attachment_manager.d.ts","sourceRoot":"","sources":["../../src/attachment_manager.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAC3E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AACvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAA;AAC9D,OAAO,KAAK,EACV,oBAAoB,EACpB,cAAc,EACd,UAAU,IAAI,cAAc,EAC7B,MAAM,uBAAuB,CAAA;AAC9B,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAA;AAGlE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAG5C,OAAO,SAAS,MAAM,2BAA2B,CAAA;AAQjD,qBAAa,iBAAiB,CAAC,eAAe,SAAS,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC;;IAC9E,KAAK,aAAA;IAGL,IAAI,EAAE,WAAW,CAAA;gBAEL,MAAM,EAAE,wBAAwB,CAAC,eAAe,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,WAAW;IAUrG,oBAAoB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI;IAiBvC,cAAc,CAAC,KAAK,EAAE,aAAa;IAgBnC,eAAe,CAAC,MAAM,EAAE,aAAa,EAAE;;;;;;;;;IAIvC,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM;IAiB3C,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM;IAgB7C,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM;IAW7C,aAAa,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,CAAC,EAAE,MAAM;IAMvC,gBAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,cAAc,EAAE,IAAI,CAAC,EAAE,MAAM;IAM7D,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,SAAS,CAAC;IAMpD,UAAU,CACd,UAAU,EAAE,cAAc,GAAG,cAAc,EAC3C,gBAAgB,CAAC,EAAE,gBAAgB;IAY/B,aAAa,CAAC,UAAU,EAAE,cAAc;IAgBxC,KAAK,CAAC,UAAU,EAAE,cAAc;IAgBhC,MAAM,CAAC,UAAU,EAAE,cAAc;IAuBvC,SAAS;CAqBV"}
|
|
@@ -17,8 +17,10 @@ export class AttachmentManager {
|
|
|
17
17
|
queue;
|
|
18
18
|
#config;
|
|
19
19
|
#drive;
|
|
20
|
-
|
|
20
|
+
lock;
|
|
21
|
+
constructor(config, drive, lock) {
|
|
21
22
|
this.#drive = drive;
|
|
23
|
+
this.lock = lock;
|
|
22
24
|
this.#config = config;
|
|
23
25
|
const concurrency = this.#config.queue?.concurrency || 1;
|
|
24
26
|
this.queue = new DeferQueue({ concurrency });
|
|
@@ -21,7 +21,7 @@ export declare class Attachment extends AttachmentBase implements AttachmentInte
|
|
|
21
21
|
* Methods
|
|
22
22
|
*/
|
|
23
23
|
createVariant(key: string, input: Input): Promise<Variant>;
|
|
24
|
-
getVariant(variantName: string): Variant |
|
|
24
|
+
getVariant(variantName: string): Variant | null;
|
|
25
25
|
getUrl(variantName?: string): Promise<string>;
|
|
26
26
|
getSignedUrl(variantNameOrOptions?: string | SignedURLOptions, signedUrlOptions?: SignedURLOptions): Promise<string>;
|
|
27
27
|
setOptions(options: LucidOptions): this;
|
|
@@ -56,7 +56,7 @@ export class Attachment extends AttachmentBase {
|
|
|
56
56
|
return variant;
|
|
57
57
|
}
|
|
58
58
|
getVariant(variantName) {
|
|
59
|
-
return this.variants?.find((v) => v.key === variantName);
|
|
59
|
+
return this.variants?.find((v) => v.key === variantName) ?? null;
|
|
60
60
|
}
|
|
61
61
|
async getUrl(variantName) {
|
|
62
62
|
if (variantName) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attachments_controller.d.ts","sourceRoot":"","sources":["../../../src/controllers/attachments_controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"attachments_controller.d.ts","sourceRoot":"","sources":["../../../src/controllers/attachments_controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAoBtD,MAAM,CAAC,OAAO,OAAO,qBAAqB;IAElC,MAAM,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,WAAW;CAgGhD"}
|
|
@@ -1,87 +1,92 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
+
import { Readable } from 'node:stream';
|
|
2
3
|
import encryption from '@adonisjs/core/services/encryption';
|
|
3
4
|
import db from '@adonisjs/lucid/services/db';
|
|
4
5
|
import { attachmentManager } from '@jrmc/adonis-attachment';
|
|
5
|
-
import
|
|
6
|
-
import
|
|
6
|
+
import VariantGenerator from '../services/variant/variant_generator.js';
|
|
7
|
+
import VariantPersister from '../services/variant/variant_persister.js';
|
|
7
8
|
export default class AttachmentsController {
|
|
8
9
|
async handle({ request, response }) {
|
|
9
10
|
const { key } = request.params();
|
|
10
11
|
const format = request.qs()?.variant;
|
|
11
|
-
|
|
12
|
-
let isAttachments = false;
|
|
12
|
+
let multiple = false;
|
|
13
13
|
const data = encryption.decrypt(key);
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
* 4. Get the variant
|
|
37
|
-
*/
|
|
38
|
-
const variant = attachment?.getVariant(format);
|
|
39
|
-
/*
|
|
40
|
-
* 5. Get the stream
|
|
41
|
-
* if variant and path, get the stream and return it
|
|
42
|
-
* if not, generate the variant
|
|
43
|
-
* if not, return the default file
|
|
44
|
-
*/
|
|
45
|
-
if (variant && variant?.path) {
|
|
46
|
-
const image = await variant.getStream();
|
|
47
|
-
const readable = Readable.from(image);
|
|
48
|
-
response.header('Content-Type', variant?.mimeType);
|
|
49
|
-
response.stream(readable);
|
|
50
|
-
}
|
|
51
|
-
else {
|
|
52
|
-
let attachmentOrAttachmentsString;
|
|
53
|
-
const converter = (await attachmentManager.getConverter(format));
|
|
54
|
-
const variant = await ConverterManager.generate({
|
|
55
|
-
key: format,
|
|
56
|
-
attachment,
|
|
57
|
-
converter
|
|
58
|
-
});
|
|
59
|
-
if (isAttachments) {
|
|
60
|
-
attachmentOrAttachmentsString = JSON.stringify([attachment.toObject()]);
|
|
14
|
+
await attachmentManager.lock.createLock(`attachment.${data.model}-${data.attribute}`).run(async () => {
|
|
15
|
+
const queryWithTableSelection = await db
|
|
16
|
+
.from(data.model)
|
|
17
|
+
.select(data.attribute)
|
|
18
|
+
.where('id', data.id).first();
|
|
19
|
+
/*
|
|
20
|
+
* 1. Get the Attachment(s)
|
|
21
|
+
*/
|
|
22
|
+
const result = JSON.parse(queryWithTableSelection[data.attribute]);
|
|
23
|
+
const attachments = [];
|
|
24
|
+
let currentAttachment = null;
|
|
25
|
+
if (Array.isArray(result)) {
|
|
26
|
+
multiple = true;
|
|
27
|
+
for (const item of result) {
|
|
28
|
+
item.folder = path.dirname(item.path);
|
|
29
|
+
const attachment = attachmentManager.createFromDbResponse(item);
|
|
30
|
+
if (attachment) {
|
|
31
|
+
attachment.setOptions(data.options);
|
|
32
|
+
attachments.push(attachment);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
currentAttachment = attachments[data.index || 0];
|
|
61
36
|
}
|
|
62
37
|
else {
|
|
63
|
-
|
|
38
|
+
result.folder = path.dirname(result.path);
|
|
39
|
+
currentAttachment = attachmentManager.createFromDbResponse(result);
|
|
40
|
+
if (currentAttachment) {
|
|
41
|
+
currentAttachment.setOptions(data.options);
|
|
42
|
+
}
|
|
64
43
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
44
|
+
if (!currentAttachment) {
|
|
45
|
+
return response.notFound();
|
|
46
|
+
}
|
|
47
|
+
/*
|
|
48
|
+
* 2. Get the variant
|
|
49
|
+
*/
|
|
50
|
+
let variant = currentAttachment.getVariant(format);
|
|
51
|
+
/*
|
|
52
|
+
* 3. Get the stream
|
|
53
|
+
* if variant and path, get the stream and return it
|
|
54
|
+
* if not, generate the variant
|
|
55
|
+
* if not, return the default file
|
|
56
|
+
*/
|
|
57
|
+
if (!variant && format) {
|
|
58
|
+
const converter = (await attachmentManager.getConverter(format));
|
|
59
|
+
variant = await (new VariantGenerator()).generateVariant({
|
|
60
|
+
key: format,
|
|
61
|
+
attachment: currentAttachment,
|
|
62
|
+
converter
|
|
70
63
|
});
|
|
71
|
-
|
|
64
|
+
if (variant) {
|
|
65
|
+
const variantPersister = new VariantPersister({
|
|
66
|
+
id: data.id,
|
|
67
|
+
modelTable: data.model,
|
|
68
|
+
attributeName: data.attribute,
|
|
69
|
+
multiple
|
|
70
|
+
});
|
|
71
|
+
await variantPersister.persist({ attachments: attachments ?? [currentAttachment], variants: [variant] });
|
|
72
|
+
}
|
|
72
73
|
}
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
/*
|
|
75
|
+
* 5. Get the stream
|
|
76
|
+
*/
|
|
77
|
+
let file;
|
|
78
|
+
let mimeType;
|
|
79
|
+
if (variant) {
|
|
80
|
+
file = await variant.getStream();
|
|
81
|
+
mimeType = variant.mimeType;
|
|
75
82
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
const image = await variant.getStream();
|
|
81
|
-
const readable = Readable.from(image);
|
|
82
|
-
response.header('Content-Type', variant.mimeType);
|
|
83
|
-
response.stream(readable);
|
|
83
|
+
else {
|
|
84
|
+
file = await currentAttachment.getStream();
|
|
85
|
+
mimeType = currentAttachment.mimeType;
|
|
84
86
|
}
|
|
85
|
-
|
|
87
|
+
const readable = Readable.from(file);
|
|
88
|
+
response.header('Content-Type', mimeType);
|
|
89
|
+
response.stream(readable);
|
|
90
|
+
});
|
|
86
91
|
}
|
|
87
92
|
}
|
|
@@ -4,12 +4,12 @@
|
|
|
4
4
|
* @license MIT
|
|
5
5
|
* @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
|
|
6
6
|
*/
|
|
7
|
-
import type { LucidModel } from '@adonisjs/lucid/types/model';
|
|
7
|
+
import type { LucidModel, LucidRow } from '@adonisjs/lucid/types/model';
|
|
8
8
|
import type { LucidOptions } from '../types/attachment.js';
|
|
9
9
|
import type { AttributeOfRowWithAttachment } from '../types/mixin.js';
|
|
10
10
|
export declare const bootModel: (model: LucidModel & {
|
|
11
11
|
$attachments: AttributeOfRowWithAttachment;
|
|
12
12
|
}) => void;
|
|
13
|
-
export declare const attachment: (options?: LucidOptions) => (target: any, attributeName: string) => void;
|
|
14
|
-
export declare const attachments: (options?: LucidOptions) => (target: any, attributeName: string) => void;
|
|
13
|
+
export declare const attachment: <T = LucidRow>(options?: LucidOptions<T>) => (target: any, attributeName: string) => void;
|
|
14
|
+
export declare const attachments: <T = LucidRow>(options?: LucidOptions<T>) => (target: any, attributeName: string) => void;
|
|
15
15
|
//# sourceMappingURL=attachment.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attachment.d.ts","sourceRoot":"","sources":["../../../src/decorators/attachment.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAA;
|
|
1
|
+
{"version":3,"file":"attachment.d.ts","sourceRoot":"","sources":["../../../src/decorators/attachment.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAA;AACvE,OAAO,KAAK,EAAc,YAAY,EAAE,MAAM,wBAAwB,CAAA;AAKtE,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,mBAAmB,CAAA;AAYrE,eAAO,MAAM,SAAS,UACb,UAAU,GAAG;IAClB,YAAY,EAAE,4BAA4B,CAAA;CAC3C,SA0BF,CAAA;AAyDD,eAAO,MAAM,UAAU,GAtBwC,CAAC,uBAAuB,YAAY,CAAC,CAAC,CAAC,cACzE,GAAG,iBAAiB,MAAM,SAqBJ,CAAA;AACnD,eAAO,MAAM,WAAW,GAvBuC,CAAC,uBAAuB,YAAY,CAAC,CAAC,CAAC,cACzE,GAAG,iBAAiB,MAAM,SAiCpD,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"record_with_attachment.d.ts","sourceRoot":"","sources":["../../../src/services/record_with_attachment.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAC1D,OAAO,KAAK,EAAE,UAAU,IAAI,cAAc,EAAgB,MAAM,wBAAwB,CAAA;AACxF,OAAO,KAAK,EAAE,oBAAoB,IAAI,kCAAkC,EAAE,MAAM,qBAAqB,CAAA;AACrG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAW/D,MAAM,CAAC,OAAO,OAAO,oBAAqB,YAAW,kCAAkC;;gBAGzE,GAAG,EAAE,iBAAiB;IAWlC;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ7B;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAQzB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAiDxB,WAAW,CAAC,OAAO;;KAA4B,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB/D,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAkB9B,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"record_with_attachment.d.ts","sourceRoot":"","sources":["../../../src/services/record_with_attachment.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAC1D,OAAO,KAAK,EAAE,UAAU,IAAI,cAAc,EAAgB,MAAM,wBAAwB,CAAA;AACxF,OAAO,KAAK,EAAE,oBAAoB,IAAI,kCAAkC,EAAE,MAAM,qBAAqB,CAAA;AACrG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAW/D,MAAM,CAAC,OAAO,OAAO,oBAAqB,YAAW,kCAAkC;;gBAGzE,GAAG,EAAE,iBAAiB;IAWlC;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ7B;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAQzB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAiDxB,WAAW,CAAC,OAAO;;KAA4B,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB/D,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAkB9B,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IA6BzB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IAqCjC,kBAAkB,CAAC,OAAO,GAAE,iBAAsB;IAuClD,MAAM;IA2CN,SAAS;IAkBf,IAAI,GAAG,sBAEN;IAED,cAAc,CAAC,OAAO,EAAE;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,gBAAgB,CAAC,EAAE,OAAO,CAAC;QAAC,aAAa,CAAC,EAAE,OAAO,CAAA;KAAE;CA4EvG"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import logger from '@adonisjs/core/services/logger';
|
|
2
2
|
import encryption from '@adonisjs/core/services/encryption';
|
|
3
3
|
import attachmentManager from '../../services/main.js';
|
|
4
|
+
import VariantService from './variant_service.js';
|
|
4
5
|
import { defaultStateAttributeMixin } from '../utils/default_values.js';
|
|
5
6
|
import { Attachment } from '../attachments/attachment.js';
|
|
6
7
|
import { optionsSym } from '../utils/symbols.js';
|
|
7
|
-
import { ConverterManager } from '../converter_manager.js';
|
|
8
8
|
import { E_CANNOT_CREATE_VARIANT } from '../errors.js';
|
|
9
9
|
export default class RecordWithAttachment {
|
|
10
10
|
#row;
|
|
@@ -113,6 +113,7 @@ export default class RecordWithAttachment {
|
|
|
113
113
|
model: model.table,
|
|
114
114
|
id: this.#row.$attributes['id'],
|
|
115
115
|
attribute: name,
|
|
116
|
+
index: i,
|
|
116
117
|
options: {
|
|
117
118
|
disk,
|
|
118
119
|
folder,
|
|
@@ -133,16 +134,21 @@ export default class RecordWithAttachment {
|
|
|
133
134
|
* Launch async generation variants
|
|
134
135
|
*/
|
|
135
136
|
for await (const name of attachmentAttributeNames) {
|
|
137
|
+
if (!this.#row.$attributes[name]) {
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
136
140
|
const record = this;
|
|
137
141
|
attachmentManager.queue.push({
|
|
138
142
|
name: `${this.#row.constructor.name}-${name}`,
|
|
139
143
|
async run() {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
+
await attachmentManager.lock.createLock(`attachment.${record.#row.constructor.name}-${name}`).run(async () => {
|
|
145
|
+
const variantService = new VariantService({
|
|
146
|
+
record,
|
|
147
|
+
attributeName: name,
|
|
148
|
+
options: record.#getOptionsByAttributeName(name),
|
|
149
|
+
});
|
|
150
|
+
await variantService.run();
|
|
144
151
|
});
|
|
145
|
-
await converterManager.run();
|
|
146
152
|
},
|
|
147
153
|
})
|
|
148
154
|
.onError = function (error) {
|
|
@@ -164,11 +170,14 @@ export default class RecordWithAttachment {
|
|
|
164
170
|
attachmentAttributeNames = this.#getAttributeNamesOfAttachment();
|
|
165
171
|
}
|
|
166
172
|
for await (const name of attachmentAttributeNames) {
|
|
173
|
+
if (!this.#row.$attributes[name]) {
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
167
176
|
const record = this;
|
|
168
177
|
attachmentManager.queue.push({
|
|
169
178
|
name: `${this.#row.constructor.name}-${name}`,
|
|
170
179
|
async run() {
|
|
171
|
-
const
|
|
180
|
+
const variantService = new VariantService({
|
|
172
181
|
record,
|
|
173
182
|
attributeName: name,
|
|
174
183
|
options: record.#getOptionsByAttributeName(name),
|
|
@@ -176,7 +185,7 @@ export default class RecordWithAttachment {
|
|
|
176
185
|
variants: options.variants
|
|
177
186
|
}
|
|
178
187
|
});
|
|
179
|
-
await
|
|
188
|
+
await variantService.run();
|
|
180
189
|
},
|
|
181
190
|
})
|
|
182
191
|
.onError = function (error) {
|
|
@@ -254,7 +263,11 @@ export default class RecordWithAttachment {
|
|
|
254
263
|
attachments = this.#getAttachmentsByAttributeName(options.attributeName);
|
|
255
264
|
}
|
|
256
265
|
const opts = this.#getOptionsByAttributeName(options.attributeName);
|
|
257
|
-
attachments.
|
|
266
|
+
attachments.forEach((attachment) => {
|
|
267
|
+
if (attachment) {
|
|
268
|
+
attachment.setOptions(opts).makeFolder(this.#row);
|
|
269
|
+
}
|
|
270
|
+
});
|
|
258
271
|
return attachments;
|
|
259
272
|
}
|
|
260
273
|
#getAttachmentsByAttributeName(name) {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Attachment, Variant, LucidOptions } from '../../types/attachment.js';
|
|
2
|
+
import type { Converter } from '../../types/converter.js';
|
|
3
|
+
export default class VariantGenerator {
|
|
4
|
+
#private;
|
|
5
|
+
generate({ attachments, options, filters }: {
|
|
6
|
+
attachments: Attachment[];
|
|
7
|
+
options: LucidOptions;
|
|
8
|
+
filters?: {
|
|
9
|
+
variants?: string[];
|
|
10
|
+
};
|
|
11
|
+
}): Promise<Variant[]>;
|
|
12
|
+
generateVariant({ key, attachment, converter }: {
|
|
13
|
+
key: string;
|
|
14
|
+
attachment: Attachment;
|
|
15
|
+
converter: Converter;
|
|
16
|
+
}): Promise<Variant | null>;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=variant_generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"variant_generator.d.ts","sourceRoot":"","sources":["../../../../src/services/variant/variant_generator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AAClF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAA;AAOzD,MAAM,CAAC,OAAO,OAAO,gBAAgB;;IAC7B,QAAQ,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE;QAChD,WAAW,EAAE,UAAU,EAAE,CAAC;QAC1B,OAAO,EAAE,YAAY,CAAC;QACtB,OAAO,CAAC,EAAE;YAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;SAAE,CAAA;KAClC,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAmBhB,eAAe,CAAC,EAAE,GAAG,EAAE,UAAU,EAAE,SAAS,EAAE,EAAE;QACpD,GAAG,EAAE,MAAM,CAAC;QACZ,UAAU,EAAE,UAAU,CAAC;QACvB,SAAS,EAAE,SAAS,CAAA;KACrB,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;CAkF5B"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import logger from '@adonisjs/core/services/logger';
|
|
2
|
+
import attachmentManager from '../../../services/main.js';
|
|
3
|
+
import { streamToTempFile } from '../../utils/helpers.js';
|
|
4
|
+
export default class VariantGenerator {
|
|
5
|
+
async generate({ attachments, options, filters }) {
|
|
6
|
+
const variants = [];
|
|
7
|
+
const variantKeys = this.#getVariantKeysToProcess(options, filters);
|
|
8
|
+
for (const key of variantKeys) {
|
|
9
|
+
const converter = await this.#getConverter(key);
|
|
10
|
+
if (!converter)
|
|
11
|
+
continue;
|
|
12
|
+
for (const attachment of attachments) {
|
|
13
|
+
const variant = await this.generateVariant({ key, attachment, converter });
|
|
14
|
+
if (variant) {
|
|
15
|
+
variants.push(variant);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return variants;
|
|
20
|
+
}
|
|
21
|
+
async generateVariant({ key, attachment, converter }) {
|
|
22
|
+
try {
|
|
23
|
+
const input = await this.#prepareInput(attachment);
|
|
24
|
+
const output = await this.#convertFile(input, converter);
|
|
25
|
+
if (!output) {
|
|
26
|
+
// throw new errors.E_CANNOT_PATH_BY_CONVERTER()
|
|
27
|
+
logger.warn(`Converter returned no output for key: ${key}`);
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
const variant = await attachment.createVariant(key, output);
|
|
31
|
+
await this.#processBlurhash(variant, converter);
|
|
32
|
+
await attachmentManager.write(variant);
|
|
33
|
+
return variant;
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
logger.error(`Failed to generate variant ${key} for attachment: ${error.message}`);
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
#getVariantKeysToProcess(options, filters) {
|
|
41
|
+
if (!options.variants)
|
|
42
|
+
return [];
|
|
43
|
+
return options.variants.filter(key => filters?.variants === undefined ||
|
|
44
|
+
filters.variants.includes(key));
|
|
45
|
+
}
|
|
46
|
+
async #getConverter(key) {
|
|
47
|
+
try {
|
|
48
|
+
return await attachmentManager.getConverter(key);
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
logger.error(`Failed to get converter for key ${key}: ${error.message}`);
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async #prepareInput(attachment) {
|
|
56
|
+
if (attachment.input) {
|
|
57
|
+
return attachment.input;
|
|
58
|
+
}
|
|
59
|
+
const stream = await attachment.getStream();
|
|
60
|
+
return await streamToTempFile(stream);
|
|
61
|
+
}
|
|
62
|
+
async #convertFile(input, converter) {
|
|
63
|
+
if (!converter.handle) {
|
|
64
|
+
throw new Error('Converter handle method is required');
|
|
65
|
+
}
|
|
66
|
+
if (!converter.options) {
|
|
67
|
+
throw new Error('Converter options are required');
|
|
68
|
+
}
|
|
69
|
+
return await converter.handle({
|
|
70
|
+
input,
|
|
71
|
+
options: converter.options,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
async #processBlurhash(variant, converter) {
|
|
75
|
+
const blurhashConfig = converter.options?.blurhash;
|
|
76
|
+
if (!blurhashConfig)
|
|
77
|
+
return;
|
|
78
|
+
const shouldGenerate = typeof blurhashConfig === 'boolean'
|
|
79
|
+
? blurhashConfig
|
|
80
|
+
: blurhashConfig.enabled === true;
|
|
81
|
+
if (!shouldGenerate)
|
|
82
|
+
return;
|
|
83
|
+
try {
|
|
84
|
+
const options = typeof blurhashConfig !== 'boolean' ? blurhashConfig : undefined;
|
|
85
|
+
await variant.generateBlurhash(options);
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
logger.error(`Blurhash generation failed: ${error.message}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Attachment, Variant } from '../../types/attachment.js';
|
|
2
|
+
type PersistAttributes = {
|
|
3
|
+
id: string;
|
|
4
|
+
modelTable: string;
|
|
5
|
+
attributeName: string;
|
|
6
|
+
multiple: boolean;
|
|
7
|
+
};
|
|
8
|
+
export default class VariantPersister {
|
|
9
|
+
#private;
|
|
10
|
+
constructor({ id, modelTable, attributeName, multiple }: PersistAttributes);
|
|
11
|
+
persist({ attachments, variants }: {
|
|
12
|
+
attachments: Attachment[];
|
|
13
|
+
variants: Variant[];
|
|
14
|
+
}): Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=variant_persister.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"variant_persister.d.ts","sourceRoot":"","sources":["../../../../src/services/variant/variant_persister.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAA;AAOpE,KAAK,iBAAiB,GAAG;IACvB,EAAE,EAAE,MAAM,CAAA;IACV,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,EAAE,MAAM,CAAA;IACrB,QAAQ,EAAE,OAAO,CAAA;CAClB,CAAA;AAED,MAAM,CAAC,OAAO,OAAO,gBAAgB;;gBAMvB,EAAE,EAAE,EAAE,UAAU,EAAE,aAAa,EAAE,QAAQ,EAAE,EAAE,iBAAiB;IAOpE,OAAO,CAAC,EAAE,WAAW,EAAE,QAAQ,EAAE,EAAE;QACvC,WAAW,EAAE,UAAU,EAAE,CAAC;QAC1B,QAAQ,EAAE,OAAO,EAAE,CAAA;KACpB,GAAG,OAAO,CAAC,IAAI,CAAC;CA2ClB"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import logger from '@adonisjs/core/services/logger';
|
|
2
|
+
import string from '@adonisjs/core/helpers/string';
|
|
3
|
+
import db from '@adonisjs/lucid/services/db';
|
|
4
|
+
import attachmentManager from '../../../services/main.js';
|
|
5
|
+
export default class VariantPersister {
|
|
6
|
+
#id;
|
|
7
|
+
#modelTable;
|
|
8
|
+
#attributeName;
|
|
9
|
+
#multiple;
|
|
10
|
+
constructor({ id, modelTable, attributeName, multiple }) {
|
|
11
|
+
this.#id = id;
|
|
12
|
+
this.#modelTable = modelTable;
|
|
13
|
+
this.#attributeName = attributeName;
|
|
14
|
+
this.#multiple = multiple;
|
|
15
|
+
}
|
|
16
|
+
async persist({ attachments, variants }) {
|
|
17
|
+
const rollback = () => this.#rollbackVariants(variants);
|
|
18
|
+
const trx = await db.transaction();
|
|
19
|
+
trx.after('rollback', rollback);
|
|
20
|
+
try {
|
|
21
|
+
const data = this.#prepareUpdateData(attachments);
|
|
22
|
+
await this.#executeUpdate(trx, data);
|
|
23
|
+
await trx.commit();
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
logger.error(`Persist failed: ${error.message}`);
|
|
27
|
+
await trx.rollback();
|
|
28
|
+
throw error;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
#rollbackVariants(variants) {
|
|
32
|
+
variants.forEach(variant => {
|
|
33
|
+
try {
|
|
34
|
+
attachmentManager.remove(variant);
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
logger.error(`Rollback failed for variant: ${error.message}`);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
#prepareUpdateData(attachments) {
|
|
42
|
+
const index = string.snakeCase(this.#attributeName);
|
|
43
|
+
const data = this.#multiple
|
|
44
|
+
? attachments.map(att => att.toObject())
|
|
45
|
+
: attachments[0]?.toObject();
|
|
46
|
+
return { [index]: JSON.stringify(data) };
|
|
47
|
+
}
|
|
48
|
+
async #executeUpdate(trx, data) {
|
|
49
|
+
await trx.query()
|
|
50
|
+
.from(this.#modelTable)
|
|
51
|
+
.where('id', this.#id)
|
|
52
|
+
.update(data);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Attachment } from '../../types/attachment.js';
|
|
2
|
+
export default class VariantPurger {
|
|
3
|
+
#private;
|
|
4
|
+
constructor(filters?: {
|
|
5
|
+
variants?: string[];
|
|
6
|
+
});
|
|
7
|
+
purge(attachments: Attachment[]): Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=variant_purger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"variant_purger.d.ts","sourceRoot":"","sources":["../../../../src/services/variant/variant_purger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAW,MAAM,2BAA2B,CAAA;AAIpE,MAAM,CAAC,OAAO,OAAO,aAAa;;gBAGpB,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE;IAIvC,KAAK,CAAC,WAAW,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CA6CtD"}
|