@jrmc/adonis-attachment 5.0.0-beta.2 → 5.0.0-beta.4
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 +10 -1
- package/build/services/regenerate_service.js +3 -3
- package/build/src/adapters/lock.d.ts +9 -0
- package/build/src/adapters/lock.d.ts.map +1 -0
- package/build/src/adapters/lock.js +21 -0
- 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 +7 -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 +83 -74
- package/build/src/decorators/attachment.d.ts +3 -3
- package/build/src/decorators/attachment.d.ts.map +1 -1
- package/build/src/services/attachment/attachment_detachment_service.d.ts +19 -0
- package/build/src/services/attachment/attachment_detachment_service.d.ts.map +1 -0
- package/build/src/services/attachment/attachment_detachment_service.js +64 -0
- package/build/src/services/attachment/attachment_persister_service.d.ts +22 -0
- package/build/src/services/attachment/attachment_persister_service.d.ts.map +1 -0
- package/build/src/services/attachment/attachment_persister_service.js +93 -0
- package/build/src/services/attachment/attachment_recorder_service.d.ts +68 -0
- package/build/src/services/attachment/attachment_recorder_service.d.ts.map +1 -0
- package/build/src/services/attachment/attachment_recorder_service.js +119 -0
- package/build/src/services/attachment/attachment_transaction_service.d.ts +26 -0
- package/build/src/services/attachment/attachment_transaction_service.d.ts.map +1 -0
- package/build/src/services/attachment/attachment_transaction_service.js +43 -0
- package/build/src/services/attachment/attachment_utils.d.ts +35 -0
- package/build/src/services/attachment/attachment_utils.d.ts.map +1 -0
- package/build/src/services/attachment/attachment_utils.js +71 -0
- package/build/src/services/attachment/attachment_variant_service.d.ts +17 -0
- package/build/src/services/attachment/attachment_variant_service.d.ts.map +1 -0
- package/build/src/services/attachment/attachment_variant_service.js +72 -0
- package/build/src/services/attachment/index.d.ts +15 -0
- package/build/src/services/attachment/index.d.ts.map +1 -0
- package/build/src/services/attachment/index.js +15 -0
- package/build/src/services/attachment_service.d.ts +8 -0
- package/build/src/services/attachment_service.d.ts.map +1 -0
- package/build/src/services/attachment_service.js +7 -0
- package/build/src/services/variant/variant_generator_service.d.ts +24 -0
- package/build/src/services/variant/variant_generator_service.d.ts.map +1 -0
- package/build/src/services/variant/variant_generator_service.js +97 -0
- package/build/src/services/variant/variant_persister_service.d.ts +23 -0
- package/build/src/services/variant/variant_persister_service.d.ts.map +1 -0
- package/build/src/services/variant/variant_persister_service.js +60 -0
- package/build/src/services/variant/variant_purger_service.d.ts +15 -0
- package/build/src/services/variant/variant_purger_service.d.ts.map +1 -0
- package/build/src/services/variant/variant_purger_service.js +48 -0
- package/build/src/services/variant_service.d.ts +13 -0
- package/build/src/services/variant_service.d.ts.map +1 -0
- package/build/src/services/variant_service.js +58 -0
- package/build/src/types/attachment.d.ts +3 -3
- package/build/src/types/attachment.d.ts.map +1 -1
- package/build/src/types/index.d.ts +6 -0
- package/build/src/types/index.d.ts.map +1 -1
- package/build/src/types/index.js +6 -0
- package/build/src/types/lock.d.ts +14 -0
- package/build/src/types/lock.d.ts.map +1 -0
- package/build/src/types/lock.js +7 -0
- package/build/src/types/metadata.d.ts +6 -0
- package/build/src/types/metadata.d.ts.map +1 -1
- package/build/src/types/metadata.js +6 -0
- package/build/src/types/regenerate.d.ts +6 -0
- package/build/src/types/regenerate.d.ts.map +1 -1
- package/build/src/types/regenerate.js +6 -0
- package/build/src/types/service.d.ts +6 -0
- package/build/src/types/service.d.ts.map +1 -1
- package/build/src/types/service.js +6 -0
- package/build/src/utils/hooks.js +5 -5
- package/build/stubs/config.stub +102 -4
- package/build/tsconfig.tsbuildinfo +1 -1
- package/package.json +27 -22
- 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/src/services/record_with_attachment.d.ts +0 -33
- package/build/src/services/record_with_attachment.d.ts.map +0 -1
- package/build/src/services/record_with_attachment.js +0 -303
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;
|
|
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;AAChD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAM/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;IA4BF,IAAI;CAQX"}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { configProvider } from '@adonisjs/core';
|
|
8
8
|
import { RuntimeException } from '@poppinss/utils';
|
|
9
|
+
import { verrou } from '../src/adapters/lock.js';
|
|
9
10
|
export default class AttachmentProvider {
|
|
10
11
|
app;
|
|
11
12
|
#manager = null;
|
|
@@ -18,10 +19,18 @@ export default class AttachmentProvider {
|
|
|
18
19
|
const attachmentConfig = this.app.config.get('attachment');
|
|
19
20
|
const config = await configProvider.resolve(this.app, attachmentConfig);
|
|
20
21
|
const drive = await this.app.container.make('drive.manager');
|
|
22
|
+
let lock;
|
|
23
|
+
try {
|
|
24
|
+
const lockManager = await this.app.container.make('lock.manager');
|
|
25
|
+
lock = verrou(lockManager);
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
lock = verrou();
|
|
29
|
+
}
|
|
21
30
|
if (!config) {
|
|
22
31
|
throw new RuntimeException('Invalid config exported from "config/attachment.ts" file. Make sure to use the defineConfig method');
|
|
23
32
|
}
|
|
24
|
-
this.#manager = new AttachmentManager(config, drive);
|
|
33
|
+
this.#manager = new AttachmentManager(config, drive, lock);
|
|
25
34
|
return this.#manager;
|
|
26
35
|
});
|
|
27
36
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { AttachmentRecordService } from '../src/services/attachment/index.js';
|
|
2
2
|
export default class RegenerateService {
|
|
3
3
|
#Model;
|
|
4
4
|
#row;
|
|
@@ -15,13 +15,13 @@ export default class RegenerateService {
|
|
|
15
15
|
}
|
|
16
16
|
async run() {
|
|
17
17
|
if (this.#row) {
|
|
18
|
-
const record = new
|
|
18
|
+
const record = new AttachmentRecordService(this.#row);
|
|
19
19
|
return record.regenerateVariants(this.#options);
|
|
20
20
|
}
|
|
21
21
|
else if (this.#Model) {
|
|
22
22
|
const entities = await this.#Model.all();
|
|
23
23
|
return Promise.all(entities.map(async (entity) => {
|
|
24
|
-
const record = new
|
|
24
|
+
const record = new AttachmentRecordService(entity);
|
|
25
25
|
return record.regenerateVariants(this.#options);
|
|
26
26
|
}));
|
|
27
27
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jrmc/adonis-attachment
|
|
3
|
+
*
|
|
4
|
+
* @license MIT
|
|
5
|
+
* @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
|
|
6
|
+
*/
|
|
7
|
+
import type { LockService } from '../types/lock.js';
|
|
8
|
+
export declare const verrou: (lock?: LockService) => LockService;
|
|
9
|
+
//# sourceMappingURL=lock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lock.d.ts","sourceRoot":"","sources":["../../../src/adapters/lock.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAKnD,eAAO,MAAM,MAAM,UAAW,WAAW,KAAG,WAW3C,CAAA"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jrmc/adonis-attachment
|
|
3
|
+
*
|
|
4
|
+
* @license MIT
|
|
5
|
+
* @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
|
|
6
|
+
*/
|
|
7
|
+
import { Verrou } from '@verrou/core';
|
|
8
|
+
import { memoryStore } from '@verrou/core/drivers/memory';
|
|
9
|
+
export const verrou = (lock) => {
|
|
10
|
+
if (lock) {
|
|
11
|
+
return lock;
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
return new Verrou({
|
|
15
|
+
default: 'memory',
|
|
16
|
+
stores: {
|
|
17
|
+
memory: { driver: memoryStore() }
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
};
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import type { DriveService, SignedURLOptions } from '@adonisjs/drive/types';
|
|
8
8
|
import type { MultipartFile } from '@adonisjs/core/bodyparser';
|
|
9
|
+
import type { LockService } from './types/lock.js';
|
|
9
10
|
import type { AttachmentAttributes, AttachmentBase, Attachment as AttachmentType } from './types/attachment.js';
|
|
10
11
|
import type { ResolvedAttachmentConfig } from './define_config.js';
|
|
11
12
|
import { DeferQueue } from '@poppinss/defer';
|
|
@@ -13,14 +14,14 @@ 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
|
-
constructor(config: ResolvedAttachmentConfig<KnownConverters>, drive: DriveService);
|
|
17
|
+
constructor(config: ResolvedAttachmentConfig<KnownConverters>, drive: DriveService, lock: LockService);
|
|
17
18
|
createFromDbResponse(response?: string | JSON): AttachmentType | null;
|
|
18
19
|
createFromFile(input: MultipartFile): Promise<AttachmentType>;
|
|
19
20
|
createFromFiles(inputs: MultipartFile[]): Promise<(AttachmentBase & {
|
|
20
21
|
originalName: string;
|
|
21
22
|
variants?: import("./types/attachment.js").Variant[];
|
|
22
23
|
createVariant(key: string, input: import("./types/input.js").Input): Promise<import("./types/attachment.js").Variant>;
|
|
23
|
-
getVariant(variantName: string): import("./types/attachment.js").Variant |
|
|
24
|
+
getVariant(variantName: string): import("./types/attachment.js").Variant | null;
|
|
24
25
|
getUrl(variantName?: string): Promise<string>;
|
|
25
26
|
getSignedUrl(variantNameOrOptions?: string | SignedURLOptions, signedUrlOptions?: SignedURLOptions): Promise<string>;
|
|
26
27
|
toObject(): AttachmentAttributes;
|
|
@@ -36,5 +37,6 @@ export declare class AttachmentManager<KnownConverters extends Record<string, Co
|
|
|
36
37
|
write(attachment: AttachmentBase): Promise<void>;
|
|
37
38
|
remove(attachment: AttachmentBase): Promise<void>;
|
|
38
39
|
getConfig(): ResolvedAttachmentConfig<KnownConverters>;
|
|
40
|
+
get lock(): LockService;
|
|
39
41
|
}
|
|
40
42
|
//# sourceMappingURL=attachment_manager.d.ts.map
|
|
@@ -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,aAAa,EAAE,MAAM,2BAA2B,CAAA;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAClD,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;gBAKO,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;IAyBvC,SAAS;IAIT,IAAI,IAAI,gBAEP;CAmBF"}
|
|
@@ -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 });
|
|
@@ -156,9 +158,13 @@ export class AttachmentManager {
|
|
|
156
158
|
await attachment.getDisk().delete(attachment.originalPath);
|
|
157
159
|
}
|
|
158
160
|
}
|
|
161
|
+
// getters
|
|
159
162
|
getConfig() {
|
|
160
163
|
return this.#config;
|
|
161
164
|
}
|
|
165
|
+
get lock() {
|
|
166
|
+
return this.#lock;
|
|
167
|
+
}
|
|
162
168
|
// private methods
|
|
163
169
|
#configureAttachment(attachment) {
|
|
164
170
|
if (this.#config.meta !== undefined) {
|
|
@@ -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;CA0GhD"}
|
|
@@ -1,92 +1,101 @@
|
|
|
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 VariantGeneratorService from '../services/variant/variant_generator_service.js';
|
|
7
|
+
import VariantPersisterService from '../services/variant/variant_persister_service.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
|
-
* 3. Get the variant
|
|
37
|
-
*/
|
|
38
|
-
const variant = attachment?.getVariant(format);
|
|
39
|
-
/*
|
|
40
|
-
* 4. 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 && format) {
|
|
46
|
-
let attachmentOrAttachmentsString;
|
|
47
|
-
const converter = (await attachmentManager.getConverter(format));
|
|
48
|
-
const variant = await ConverterManager.generate({
|
|
49
|
-
key: format,
|
|
50
|
-
attachment,
|
|
51
|
-
converter
|
|
52
|
-
});
|
|
53
|
-
if (isAttachments) {
|
|
54
|
-
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];
|
|
55
36
|
}
|
|
56
37
|
else {
|
|
57
|
-
|
|
38
|
+
result.folder = path.dirname(result.path);
|
|
39
|
+
currentAttachment = attachmentManager.createFromDbResponse(result);
|
|
40
|
+
if (currentAttachment) {
|
|
41
|
+
currentAttachment.setOptions(data.options);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (!currentAttachment) {
|
|
45
|
+
return response.notFound();
|
|
58
46
|
}
|
|
59
|
-
|
|
60
|
-
|
|
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 VariantGeneratorService()).generateVariant({
|
|
60
|
+
key: format,
|
|
61
|
+
attachment: currentAttachment,
|
|
62
|
+
converter
|
|
63
|
+
});
|
|
61
64
|
if (variant) {
|
|
62
|
-
|
|
65
|
+
const variantPersister = new VariantPersisterService({
|
|
66
|
+
id: data.id,
|
|
67
|
+
modelTable: data.model,
|
|
68
|
+
attributeName: data.attribute,
|
|
69
|
+
multiple
|
|
70
|
+
});
|
|
71
|
+
const attachmentsOrCurrent = attachments.length ? attachments : [currentAttachment];
|
|
72
|
+
await variantPersister.persist({ attachments: attachmentsOrCurrent, variants: [variant] });
|
|
63
73
|
}
|
|
64
|
-
});
|
|
65
|
-
try {
|
|
66
|
-
await trx.query().from(data.model).where('id', data.id).update({
|
|
67
|
-
[data.attribute]: attachmentOrAttachmentsString
|
|
68
|
-
});
|
|
69
|
-
await trx.commit();
|
|
70
74
|
}
|
|
71
|
-
|
|
72
|
-
|
|
75
|
+
/*
|
|
76
|
+
* 5. Get the stream
|
|
77
|
+
*/
|
|
78
|
+
let stream;
|
|
79
|
+
let mimeType;
|
|
80
|
+
if (variant) {
|
|
81
|
+
stream = await variant.getStream();
|
|
82
|
+
mimeType = variant.mimeType;
|
|
83
|
+
}
|
|
84
|
+
else if (currentAttachment) {
|
|
85
|
+
stream = await currentAttachment.getStream();
|
|
86
|
+
mimeType = currentAttachment.mimeType;
|
|
87
|
+
}
|
|
88
|
+
/*
|
|
89
|
+
* 6. Return the stream
|
|
90
|
+
*/
|
|
91
|
+
if (stream && mimeType) {
|
|
92
|
+
const readable = Readable.from(stream);
|
|
93
|
+
response.header('Content-Type', mimeType);
|
|
94
|
+
response.stream(readable);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
return response.notFound();
|
|
73
98
|
}
|
|
74
|
-
}
|
|
75
|
-
/*
|
|
76
|
-
* 5. Get the stream
|
|
77
|
-
*/
|
|
78
|
-
let file;
|
|
79
|
-
let mimeType;
|
|
80
|
-
if (variant) {
|
|
81
|
-
file = await variant.getStream();
|
|
82
|
-
mimeType = variant.mimeType;
|
|
83
|
-
}
|
|
84
|
-
else {
|
|
85
|
-
file = await attachment.getStream();
|
|
86
|
-
mimeType = attachment.mimeType;
|
|
87
|
-
}
|
|
88
|
-
const readable = Readable.from(file);
|
|
89
|
-
response.header('Content-Type', mimeType);
|
|
90
|
-
response.stream(readable);
|
|
99
|
+
});
|
|
91
100
|
}
|
|
92
101
|
}
|
|
@@ -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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jrmc/adonis-attachment
|
|
3
|
+
*
|
|
4
|
+
* @license MIT
|
|
5
|
+
* @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
|
|
6
|
+
*/
|
|
7
|
+
import type { RecordWithAttachment as RecordWithAttachmentImplementation } from '../../types/service.js';
|
|
8
|
+
export declare class AttachmentDetachmentService {
|
|
9
|
+
#private;
|
|
10
|
+
/**
|
|
11
|
+
* Detach dirty attachments (mark for deletion)
|
|
12
|
+
*/
|
|
13
|
+
detach(record: RecordWithAttachmentImplementation): Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* Detach all attachments (mark all for deletion)
|
|
16
|
+
*/
|
|
17
|
+
detachAll(record: RecordWithAttachmentImplementation): Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=attachment_detachment_service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attachment_detachment_service.d.ts","sourceRoot":"","sources":["../../../../src/services/attachment/attachment_detachment_service.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,oBAAoB,IAAI,kCAAkC,EAAE,MAAM,wBAAwB,CAAA;AAIxG,qBAAa,2BAA2B;;IACtC;;OAEG;IACG,MAAM,CAAC,MAAM,EAAE,kCAAkC,GAAG,OAAO,CAAC,IAAI,CAAC;IAqCvE;;OAEG;IACG,SAAS,CAAC,MAAM,EAAE,kCAAkC,GAAG,OAAO,CAAC,IAAI,CAAC;CAyB3E"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jrmc/adonis-attachment
|
|
3
|
+
*
|
|
4
|
+
* @license MIT
|
|
5
|
+
* @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
|
|
6
|
+
*/
|
|
7
|
+
import { AttachmentUtils } from './attachment_utils.js';
|
|
8
|
+
export class AttachmentDetachmentService {
|
|
9
|
+
/**
|
|
10
|
+
* Detach dirty attachments (mark for deletion)
|
|
11
|
+
*/
|
|
12
|
+
async detach(record) {
|
|
13
|
+
const attachmentAttributeNames = AttachmentUtils.getDirtyAttributeNamesOfAttachment(record.row);
|
|
14
|
+
await Promise.allSettled(attachmentAttributeNames.map((name) => {
|
|
15
|
+
let attachments = [];
|
|
16
|
+
const options = AttachmentUtils.getOptionsByAttributeName(record.row, name);
|
|
17
|
+
if (record.row.$dirty[name] === null) {
|
|
18
|
+
attachments = AttachmentUtils.getOriginalAttachmentsByAttributeName(record.row, name);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
const originalAttachments = AttachmentUtils.getOriginalAttachmentsByAttributeName(record.row, name);
|
|
22
|
+
const newAttachments = AttachmentUtils.getAttachmentsByAttributeName(record.row, name);
|
|
23
|
+
/**
|
|
24
|
+
* Clean Attachments changed
|
|
25
|
+
*/
|
|
26
|
+
for (let i = 0; i < originalAttachments.length; i++) {
|
|
27
|
+
if (newAttachments.includes(originalAttachments[i])) {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* If there was an existing file, then we must get rid of it
|
|
32
|
+
*/
|
|
33
|
+
if (originalAttachments[i]) {
|
|
34
|
+
originalAttachments[i].setOptions(options);
|
|
35
|
+
attachments.push(originalAttachments[i]);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
this.#markForDeletion(attachments, record);
|
|
40
|
+
}));
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Detach all attachments (mark all for deletion)
|
|
44
|
+
*/
|
|
45
|
+
async detachAll(record) {
|
|
46
|
+
const attachmentAttributeNames = AttachmentUtils.getAttributeNamesOfAttachment(record.row);
|
|
47
|
+
await Promise.allSettled(attachmentAttributeNames.map((name) => {
|
|
48
|
+
const options = AttachmentUtils.getOptionsByAttributeName(record.row, name);
|
|
49
|
+
const attachments = AttachmentUtils.getAttachmentsByAttributeName(record.row, name);
|
|
50
|
+
for (let i = 0; i < attachments.length; i++) {
|
|
51
|
+
attachments[i].setOptions(options);
|
|
52
|
+
}
|
|
53
|
+
this.#markForDeletion(attachments, record);
|
|
54
|
+
}));
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Mark attachments for deletion by adding them to detached array
|
|
58
|
+
*/
|
|
59
|
+
#markForDeletion(attachments, record) {
|
|
60
|
+
for (let i = 0; i < attachments.length; i++) {
|
|
61
|
+
record.row.$attachments.detached.push(attachments[i]);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jrmc/adonis-attachment
|
|
3
|
+
*
|
|
4
|
+
* @license MIT
|
|
5
|
+
* @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
|
|
6
|
+
*/
|
|
7
|
+
import type { RecordWithAttachment as RecordWithAttachmentImplementation } from '../../types/service.js';
|
|
8
|
+
export declare class AttachmentPersisterService {
|
|
9
|
+
/**
|
|
10
|
+
* Persist attachments before saving the row to the database
|
|
11
|
+
*/
|
|
12
|
+
persistAttachments(record: RecordWithAttachmentImplementation): Promise<void>;
|
|
13
|
+
/**
|
|
14
|
+
* Pre-compute URLs for all attachments
|
|
15
|
+
*/
|
|
16
|
+
preComputeUrls(record: RecordWithAttachmentImplementation): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Set key IDs for all attachments using encryption
|
|
19
|
+
*/
|
|
20
|
+
setKeyIds(record: RecordWithAttachmentImplementation): Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=attachment_persister_service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attachment_persister_service.d.ts","sourceRoot":"","sources":["../../../../src/services/attachment/attachment_persister_service.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,oBAAoB,IAAI,kCAAkC,EAAE,MAAM,wBAAwB,CAAA;AAKxG,qBAAa,0BAA0B;IACrC;;OAEG;IACG,kBAAkB,CAAC,MAAM,EAAE,kCAAkC,GAAG,OAAO,CAAC,IAAI,CAAC;IA4CnF;;OAEG;IACG,cAAc,CAAC,MAAM,EAAE,kCAAkC,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB/E;;OAEG;IACG,SAAS,CAAC,MAAM,EAAE,kCAAkC,GAAG,OAAO,CAAC,IAAI,CAAC;CA4B3E"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jrmc/adonis-attachment
|
|
3
|
+
*
|
|
4
|
+
* @license MIT
|
|
5
|
+
* @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
|
|
6
|
+
*/
|
|
7
|
+
import encryption from '@adonisjs/core/services/encryption';
|
|
8
|
+
import attachmentManager from '../../../services/main.js';
|
|
9
|
+
import { AttachmentUtils } from './attachment_utils.js';
|
|
10
|
+
export class AttachmentPersisterService {
|
|
11
|
+
/**
|
|
12
|
+
* Persist attachments before saving the row to the database
|
|
13
|
+
*/
|
|
14
|
+
async persistAttachments(record) {
|
|
15
|
+
const attachmentAttributeNames = AttachmentUtils.getDirtyAttributeNamesOfAttachment(record.row);
|
|
16
|
+
await Promise.all(attachmentAttributeNames.map(async (name) => {
|
|
17
|
+
const originalAttachments = AttachmentUtils.getOriginalAttachmentsByAttributeName(record.row, name);
|
|
18
|
+
const newAttachments = AttachmentUtils.getAttachmentsByAttributeName(record.row, name);
|
|
19
|
+
const options = AttachmentUtils.getOptionsByAttributeName(record.row, name);
|
|
20
|
+
/**
|
|
21
|
+
* Skip when the attachment attributeName hasn't been updated
|
|
22
|
+
*/
|
|
23
|
+
if (!originalAttachments && !newAttachments) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* memorise attribute name for generate variants
|
|
28
|
+
*/
|
|
29
|
+
record.row.$attachments.dirtied.push(name);
|
|
30
|
+
for (let i = 0; i < newAttachments.length; i++) {
|
|
31
|
+
if (originalAttachments.includes(newAttachments[i])) {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* If there is a new file and its local then we must save this
|
|
36
|
+
* file.
|
|
37
|
+
*/
|
|
38
|
+
if (newAttachments[i]) {
|
|
39
|
+
newAttachments[i].setOptions(options).makeFolder(record.row);
|
|
40
|
+
record.row.$attachments.attached.push(newAttachments[i]);
|
|
41
|
+
/**
|
|
42
|
+
* Also write the file to the disk right away
|
|
43
|
+
*/
|
|
44
|
+
await attachmentManager.write(newAttachments[i]);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}));
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Pre-compute URLs for all attachments
|
|
51
|
+
*/
|
|
52
|
+
async preComputeUrls(record) {
|
|
53
|
+
const attachmentAttributeNames = AttachmentUtils.getAttributeNamesOfAttachment(record.row);
|
|
54
|
+
await Promise.all(attachmentAttributeNames.map(async (name) => {
|
|
55
|
+
const options = AttachmentUtils.getOptionsByAttributeName(record.row, name);
|
|
56
|
+
if (record.row.$attributes[name]) {
|
|
57
|
+
const attachments = AttachmentUtils.getAttachmentsByAttributeName(record.row, name);
|
|
58
|
+
for (let i = 0; i < attachments.length; i++) {
|
|
59
|
+
attachments[i].setOptions(options);
|
|
60
|
+
await attachmentManager.preComputeUrl(attachments[i]);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}));
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Set key IDs for all attachments using encryption
|
|
67
|
+
*/
|
|
68
|
+
async setKeyIds(record) {
|
|
69
|
+
const attachmentAttributeNames = AttachmentUtils.getAttributeNamesOfAttachment(record.row);
|
|
70
|
+
await Promise.all(attachmentAttributeNames.map(async (name) => {
|
|
71
|
+
if (record.row.$attributes[name]) {
|
|
72
|
+
const attachments = AttachmentUtils.getAttachmentsByAttributeName(record.row, name);
|
|
73
|
+
for (let i = 0; i < attachments.length; i++) {
|
|
74
|
+
const { disk, folder, meta, rename } = attachments[i].options;
|
|
75
|
+
const model = record.row.constructor;
|
|
76
|
+
const key = encryption.encrypt({
|
|
77
|
+
model: model.table,
|
|
78
|
+
id: record.row.$attributes['id'],
|
|
79
|
+
attribute: name,
|
|
80
|
+
index: i,
|
|
81
|
+
options: {
|
|
82
|
+
disk,
|
|
83
|
+
folder,
|
|
84
|
+
meta,
|
|
85
|
+
rename
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
attachments[i].setKeyId(key);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}));
|
|
92
|
+
}
|
|
93
|
+
}
|