@jrmc/adonis-attachment 5.0.0-beta.3 → 5.0.0-beta.6
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/providers/attachment_provider.d.ts.map +1 -1
- package/build/providers/attachment_provider.js +9 -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/adapters/poppler.js +0 -1
- package/build/src/attachment_manager.d.ts +7 -3
- package/build/src/attachment_manager.d.ts.map +1 -1
- package/build/src/attachment_manager.js +15 -50
- package/build/src/attachments/attachment.d.ts +9 -4
- package/build/src/attachments/attachment.d.ts.map +1 -1
- package/build/src/attachments/attachment.js +53 -9
- package/build/src/attachments/attachment_base.d.ts +11 -2
- package/build/src/attachments/attachment_base.d.ts.map +1 -1
- package/build/src/attachments/attachment_base.js +60 -3
- package/build/src/controllers/attachments_controller.d.ts.map +1 -1
- package/build/src/controllers/attachments_controller.js +21 -12
- 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 +66 -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 +95 -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 +121 -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 +46 -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.d.ts → variant_generator_service.d.ts} +8 -2
- package/build/src/services/variant/variant_generator_service.d.ts.map +1 -0
- package/build/src/services/variant/{variant_generator.js → variant_generator_service.js} +7 -1
- package/build/src/services/variant/{variant_persister.d.ts → variant_persister_service.d.ts} +8 -2
- package/build/src/services/variant/variant_persister_service.d.ts.map +1 -0
- package/build/src/services/variant/{variant_persister.js → variant_persister_service.js} +7 -1
- package/build/src/services/variant/{variant_purger.d.ts → variant_purger_service.d.ts} +7 -1
- package/build/src/services/variant/variant_purger_service.d.ts.map +1 -0
- package/build/src/services/variant/{variant_purger.js → variant_purger_service.js} +6 -0
- package/build/src/services/variant_service.d.ts +6 -0
- package/build/src/services/variant_service.d.ts.map +1 -1
- package/build/src/services/variant_service.js +13 -7
- package/build/src/types/attachment.d.ts +12 -3
- package/build/src/types/attachment.d.ts.map +1 -1
- package/build/src/types/config.d.ts +2 -1
- package/build/src/types/config.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 +7 -1
- 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/tsconfig.tsbuildinfo +1 -1
- package/package.json +32 -23
- 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 -316
- package/build/src/services/variant/variant_generator.d.ts.map +0 -1
- package/build/src/services/variant/variant_persister.d.ts.map +0 -1
- package/build/src/services/variant/variant_purger.d.ts.map +0 -1
|
@@ -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;IA8CnF;;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,95 @@
|
|
|
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);
|
|
40
|
+
await newAttachments[i].makeFolder(record.row);
|
|
41
|
+
await newAttachments[i].makeName(record.row, name, newAttachments[i].originalName);
|
|
42
|
+
record.row.$attachments.attached.push(newAttachments[i]);
|
|
43
|
+
/**
|
|
44
|
+
* Also write the file to the disk right away
|
|
45
|
+
*/
|
|
46
|
+
await attachmentManager.write(newAttachments[i]);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}));
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Pre-compute URLs for all attachments
|
|
53
|
+
*/
|
|
54
|
+
async preComputeUrls(record) {
|
|
55
|
+
const attachmentAttributeNames = AttachmentUtils.getAttributeNamesOfAttachment(record.row);
|
|
56
|
+
await Promise.all(attachmentAttributeNames.map(async (name) => {
|
|
57
|
+
const options = AttachmentUtils.getOptionsByAttributeName(record.row, name);
|
|
58
|
+
if (record.row.$attributes[name]) {
|
|
59
|
+
const attachments = AttachmentUtils.getAttachmentsByAttributeName(record.row, name);
|
|
60
|
+
for (let i = 0; i < attachments.length; i++) {
|
|
61
|
+
attachments[i].setOptions(options);
|
|
62
|
+
await attachmentManager.preComputeUrl(attachments[i]);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}));
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Set key IDs for all attachments using encryption
|
|
69
|
+
*/
|
|
70
|
+
async setKeyIds(record) {
|
|
71
|
+
const attachmentAttributeNames = AttachmentUtils.getAttributeNamesOfAttachment(record.row);
|
|
72
|
+
await Promise.all(attachmentAttributeNames.map(async (name) => {
|
|
73
|
+
if (record.row.$attributes[name]) {
|
|
74
|
+
const attachments = AttachmentUtils.getAttachmentsByAttributeName(record.row, name);
|
|
75
|
+
for (let i = 0; i < attachments.length; i++) {
|
|
76
|
+
const { disk, folder, meta, rename } = attachments[i].options;
|
|
77
|
+
const model = record.row.constructor;
|
|
78
|
+
const key = encryption.encrypt({
|
|
79
|
+
model: model.table,
|
|
80
|
+
id: record.row.$attributes['id'],
|
|
81
|
+
attribute: name,
|
|
82
|
+
index: i,
|
|
83
|
+
options: {
|
|
84
|
+
disk,
|
|
85
|
+
folder,
|
|
86
|
+
meta,
|
|
87
|
+
rename
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
attachments[i].setKeyId(key);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jrmc/adonis-attachment
|
|
3
|
+
*
|
|
4
|
+
* @license MIT
|
|
5
|
+
* @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
|
|
6
|
+
*/
|
|
7
|
+
import type { RowWithAttachment } from '../../types/mixin.js';
|
|
8
|
+
import type { Attachment as AttachmentType } from '../../types/attachment.js';
|
|
9
|
+
import type { RecordWithAttachment as RecordWithAttachmentImplementation } from '../../types/service.js';
|
|
10
|
+
import type { RegenerateOptions } from '../../types/regenerate.js';
|
|
11
|
+
import type { TransactionOptions } from './attachment_transaction_service.js';
|
|
12
|
+
export default class AttachmentRecorderService implements RecordWithAttachmentImplementation {
|
|
13
|
+
#private;
|
|
14
|
+
constructor(row: RowWithAttachment);
|
|
15
|
+
/**
|
|
16
|
+
* During commit, we should cleanup the old detached files
|
|
17
|
+
*/
|
|
18
|
+
commit(): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* During rollback we should remove the attached files.
|
|
21
|
+
*/
|
|
22
|
+
rollback(): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Persist attachments before saving the row to the database
|
|
25
|
+
*/
|
|
26
|
+
persist(): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Handle transaction lifecycle
|
|
29
|
+
*/
|
|
30
|
+
transaction(options?: TransactionOptions): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Pre-compute URLs for all attachments
|
|
33
|
+
*/
|
|
34
|
+
preComputeUrl(): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* Set key IDs for all attachments
|
|
37
|
+
*/
|
|
38
|
+
setKeyId(): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Generate variants for all dirty attachment attributes
|
|
41
|
+
*/
|
|
42
|
+
generateVariants(): Promise<void>;
|
|
43
|
+
/**
|
|
44
|
+
* Regenerate variants with specific options
|
|
45
|
+
*/
|
|
46
|
+
regenerateVariants(options?: RegenerateOptions): Promise<void>;
|
|
47
|
+
/**
|
|
48
|
+
* Detach dirty attachments (mark for deletion)
|
|
49
|
+
*/
|
|
50
|
+
detach(): Promise<void>;
|
|
51
|
+
/**
|
|
52
|
+
* Detach all attachments (mark all for deletion)
|
|
53
|
+
*/
|
|
54
|
+
detachAll(): Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* Get row instance
|
|
57
|
+
*/
|
|
58
|
+
get row(): RowWithAttachment;
|
|
59
|
+
/**
|
|
60
|
+
* Get attachments with specific options
|
|
61
|
+
*/
|
|
62
|
+
getAttachments(options: {
|
|
63
|
+
attributeName: string;
|
|
64
|
+
requiredOriginal?: boolean;
|
|
65
|
+
requiredDirty?: boolean;
|
|
66
|
+
}): Promise<AttachmentType[]>;
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=attachment_recorder_service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attachment_recorder_service.d.ts","sourceRoot":"","sources":["../../../../src/services/attachment/attachment_recorder_service.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;AAC7D,OAAO,KAAK,EAAE,UAAU,IAAI,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAC7E,OAAO,KAAK,EAAE,oBAAoB,IAAI,kCAAkC,EAAE,MAAM,wBAAwB,CAAA;AACxG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAA;AAClE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAA;AAS7E,MAAM,CAAC,OAAO,OAAO,yBAA0B,YAAW,kCAAkC;;gBAO9E,GAAG,EAAE,iBAAiB;IAclC;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAI7B;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAI/B;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAI9B;;OAEG;IACG,WAAW,CAAC,OAAO,GAAE,kBAA8C,GAAG,OAAO,CAAC,IAAI,CAAC;IAIzF;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAIpC;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAI/B;;OAEG;IACG,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IAIvC;;OAEG;IACG,kBAAkB,CAAC,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC;IAIxE;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAI7B;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAIhC;;OAEG;IACH,IAAI,GAAG,IAAI,iBAAiB,CAE3B;IAED;;OAEG;IACG,cAAc,CAAC,OAAO,EAAE;QAC5B,aAAa,EAAE,MAAM,CAAA;QACrB,gBAAgB,CAAC,EAAE,OAAO,CAAA;QAC1B,aAAa,CAAC,EAAE,OAAO,CAAA;KACxB,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;CAsB9B"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jrmc/adonis-attachment
|
|
3
|
+
*
|
|
4
|
+
* @license MIT
|
|
5
|
+
* @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
|
|
6
|
+
*/
|
|
7
|
+
import { defaultStateAttributeMixin } from '../../utils/default_values.js';
|
|
8
|
+
import { AttachmentTransactionService } from './attachment_transaction_service.js';
|
|
9
|
+
import { AttachmentPersisterService } from './attachment_persister_service.js';
|
|
10
|
+
import { AttachmentVariantService } from './attachment_variant_service.js';
|
|
11
|
+
import { AttachmentDetachmentService } from './attachment_detachment_service.js';
|
|
12
|
+
import { AttachmentUtils } from './attachment_utils.js';
|
|
13
|
+
export default class AttachmentRecorderService {
|
|
14
|
+
#row;
|
|
15
|
+
#transactionService = new AttachmentTransactionService();
|
|
16
|
+
#persistenceService = new AttachmentPersisterService();
|
|
17
|
+
#variantService = new AttachmentVariantService();
|
|
18
|
+
#detachmentService = new AttachmentDetachmentService();
|
|
19
|
+
constructor(row) {
|
|
20
|
+
this.#row = row;
|
|
21
|
+
this.#initializeAttachments();
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Initialize attachments state if not exists
|
|
25
|
+
*/
|
|
26
|
+
#initializeAttachments() {
|
|
27
|
+
if (!this.#row.$attachments) {
|
|
28
|
+
this.#row.$attachments = structuredClone(defaultStateAttributeMixin);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* During commit, we should cleanup the old detached files
|
|
33
|
+
*/
|
|
34
|
+
async commit() {
|
|
35
|
+
await this.#transactionService.commit(this.#row.$attachments.detached);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* During rollback we should remove the attached files.
|
|
39
|
+
*/
|
|
40
|
+
async rollback() {
|
|
41
|
+
await this.#transactionService.rollback(this.#row.$attachments.attached);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Persist attachments before saving the row to the database
|
|
45
|
+
*/
|
|
46
|
+
async persist() {
|
|
47
|
+
await this.#persistenceService.persistAttachments(this);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Handle transaction lifecycle
|
|
51
|
+
*/
|
|
52
|
+
async transaction(options = { enabledRollback: true }) {
|
|
53
|
+
await this.#transactionService.handleTransaction(this, options);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Pre-compute URLs for all attachments
|
|
57
|
+
*/
|
|
58
|
+
async preComputeUrl() {
|
|
59
|
+
await this.#persistenceService.preComputeUrls(this);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Set key IDs for all attachments
|
|
63
|
+
*/
|
|
64
|
+
async setKeyId() {
|
|
65
|
+
await this.#persistenceService.setKeyIds(this);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Generate variants for all dirty attachment attributes
|
|
69
|
+
*/
|
|
70
|
+
async generateVariants() {
|
|
71
|
+
await this.#variantService.generateVariants(this);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Regenerate variants with specific options
|
|
75
|
+
*/
|
|
76
|
+
async regenerateVariants(options = {}) {
|
|
77
|
+
await this.#variantService.regenerateVariants(this, options);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Detach dirty attachments (mark for deletion)
|
|
81
|
+
*/
|
|
82
|
+
async detach() {
|
|
83
|
+
await this.#detachmentService.detach(this);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Detach all attachments (mark all for deletion)
|
|
87
|
+
*/
|
|
88
|
+
async detachAll() {
|
|
89
|
+
await this.#detachmentService.detachAll(this);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Get row instance
|
|
93
|
+
*/
|
|
94
|
+
get row() {
|
|
95
|
+
return this.#row;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Get attachments with specific options
|
|
99
|
+
*/
|
|
100
|
+
async getAttachments(options) {
|
|
101
|
+
let attachments;
|
|
102
|
+
if (options.requiredOriginal) {
|
|
103
|
+
attachments = AttachmentUtils.getOriginalAttachmentsByAttributeName(this.#row, options.attributeName);
|
|
104
|
+
}
|
|
105
|
+
else if (options.requiredDirty) {
|
|
106
|
+
attachments = AttachmentUtils.getDirtyAttachmentsByAttributeName(this.#row, options.attributeName);
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
attachments = AttachmentUtils.getAttachmentsByAttributeName(this.#row, options.attributeName);
|
|
110
|
+
}
|
|
111
|
+
const opts = AttachmentUtils.getOptionsByAttributeName(this.#row, options.attributeName);
|
|
112
|
+
attachments.forEach(async (attachment) => {
|
|
113
|
+
if (attachment) {
|
|
114
|
+
attachment.setOptions(opts);
|
|
115
|
+
await attachment.makeFolder(this.#row);
|
|
116
|
+
// await attachment.makeName(this.#row, options.attributeName, attachment.originalName)
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
return attachments;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jrmc/adonis-attachment
|
|
3
|
+
*
|
|
4
|
+
* @license MIT
|
|
5
|
+
* @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
|
|
6
|
+
*/
|
|
7
|
+
import type { Attachment as AttachmentType } from '../../types/attachment.js';
|
|
8
|
+
import type { RecordWithAttachment as RecordWithAttachmentImplementation } from '../../types/service.js';
|
|
9
|
+
export interface TransactionOptions {
|
|
10
|
+
enabledRollback?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare class AttachmentTransactionService {
|
|
13
|
+
/**
|
|
14
|
+
* During commit, we should cleanup the old detached files
|
|
15
|
+
*/
|
|
16
|
+
commit(detachedAttachments: AttachmentType[]): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* During rollback we should remove the attached files.
|
|
19
|
+
*/
|
|
20
|
+
rollback(attachedAttachments: AttachmentType[]): Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* Handle transaction lifecycle with commit and rollback hooks
|
|
23
|
+
*/
|
|
24
|
+
handleTransaction(record: RecordWithAttachmentImplementation, options?: TransactionOptions): Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=attachment_transaction_service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attachment_transaction_service.d.ts","sourceRoot":"","sources":["../../../../src/services/attachment/attachment_transaction_service.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,UAAU,IAAI,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAC7E,OAAO,KAAK,EAAE,oBAAoB,IAAI,kCAAkC,EAAE,MAAM,wBAAwB,CAAA;AAGxG,MAAM,WAAW,kBAAkB;IACjC,eAAe,CAAC,EAAE,OAAO,CAAA;CAC1B;AAED,qBAAa,4BAA4B;IACvC;;OAEG;IACG,MAAM,CAAC,mBAAmB,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAQlE;;OAEG;IACG,QAAQ,CAAC,mBAAmB,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IASpE;;OAEG;IACG,iBAAiB,CACrB,MAAM,EAAE,kCAAkC,EAC1C,OAAO,GAAE,kBAA8C,GACtD,OAAO,CAAC,IAAI,CAAC;CAiBjB"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jrmc/adonis-attachment
|
|
3
|
+
*
|
|
4
|
+
* @license MIT
|
|
5
|
+
* @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
|
|
6
|
+
*/
|
|
7
|
+
import attachmentManager from '../../../services/main.js';
|
|
8
|
+
export class AttachmentTransactionService {
|
|
9
|
+
/**
|
|
10
|
+
* During commit, we should cleanup the old detached files
|
|
11
|
+
*/
|
|
12
|
+
async commit(detachedAttachments) {
|
|
13
|
+
await Promise.allSettled(detachedAttachments.map((attachment) => attachmentManager.remove(attachment)));
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* During rollback we should remove the attached files.
|
|
17
|
+
*/
|
|
18
|
+
async rollback(attachedAttachments) {
|
|
19
|
+
await Promise.allSettled(attachedAttachments.map(async (attachment) => {
|
|
20
|
+
await attachment.rollbackMoveFileForDelete();
|
|
21
|
+
await attachmentManager.remove(attachment);
|
|
22
|
+
}));
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Handle transaction lifecycle with commit and rollback hooks
|
|
26
|
+
*/
|
|
27
|
+
async handleTransaction(record, options = { enabledRollback: true }) {
|
|
28
|
+
try {
|
|
29
|
+
if (record.row.$trx) {
|
|
30
|
+
record.row.$trx.after('commit', () => this.commit(record.row.$attachments.detached));
|
|
31
|
+
if (options.enabledRollback) {
|
|
32
|
+
record.row.$trx.after('rollback', () => this.rollback(record.row.$attachments.attached));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
await this.commit(record.row.$attachments.detached);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
if (options.enabledRollback) {
|
|
41
|
+
await this.rollback(record.row.$attachments.attached);
|
|
42
|
+
}
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jrmc/adonis-attachment
|
|
3
|
+
*
|
|
4
|
+
* @license MIT
|
|
5
|
+
* @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
|
|
6
|
+
*/
|
|
7
|
+
import type { RowWithAttachment } from '../../types/mixin.js';
|
|
8
|
+
import type { Attachment as AttachmentType, LucidOptions } from '../../types/attachment.js';
|
|
9
|
+
export declare class AttachmentUtils {
|
|
10
|
+
/**
|
|
11
|
+
* Get attachments by attribute name, handling both single and array values
|
|
12
|
+
*/
|
|
13
|
+
static getAttachmentsByAttributeName(row: RowWithAttachment, name: string): AttachmentType[];
|
|
14
|
+
/**
|
|
15
|
+
* Get original attachments by attribute name, handling both single and array values
|
|
16
|
+
*/
|
|
17
|
+
static getOriginalAttachmentsByAttributeName(row: RowWithAttachment, name: string): AttachmentType[];
|
|
18
|
+
/**
|
|
19
|
+
* Get dirty attachments by attribute name, handling both single and array values
|
|
20
|
+
*/
|
|
21
|
+
static getDirtyAttachmentsByAttributeName(row: RowWithAttachment, name: string): AttachmentType[];
|
|
22
|
+
/**
|
|
23
|
+
* Get options by attribute name from the model prototype
|
|
24
|
+
*/
|
|
25
|
+
static getOptionsByAttributeName(row: RowWithAttachment, name: string): LucidOptions;
|
|
26
|
+
/**
|
|
27
|
+
* Get all attribute names that contain attachments
|
|
28
|
+
*/
|
|
29
|
+
static getAttributeNamesOfAttachment(row: RowWithAttachment): string[];
|
|
30
|
+
/**
|
|
31
|
+
* Get dirty attribute names that contain attachments
|
|
32
|
+
*/
|
|
33
|
+
static getDirtyAttributeNamesOfAttachment(row: RowWithAttachment): string[];
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=attachment_utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attachment_utils.d.ts","sourceRoot":"","sources":["../../../../src/services/attachment/attachment_utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;AAC7D,OAAO,KAAK,EAAE,UAAU,IAAI,cAAc,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AAI3F,qBAAa,eAAe;IAC1B;;OAEG;IACH,MAAM,CAAC,6BAA6B,CAAC,GAAG,EAAE,iBAAiB,EAAE,IAAI,EAAE,MAAM,GAAG,cAAc,EAAE;IAO5F;;OAEG;IACH,MAAM,CAAC,qCAAqC,CAAC,GAAG,EAAE,iBAAiB,EAAE,IAAI,EAAE,MAAM,GAAG,cAAc,EAAE;IAOpG;;OAEG;IACH,MAAM,CAAC,kCAAkC,CAAC,GAAG,EAAE,iBAAiB,EAAE,IAAI,EAAE,MAAM,GAAG,cAAc,EAAE;IAOjG;;OAEG;IACH,MAAM,CAAC,yBAAyB,CAAC,GAAG,EAAE,iBAAiB,EAAE,IAAI,EAAE,MAAM,GAAG,YAAY;IAIpF;;OAEG;IACH,MAAM,CAAC,6BAA6B,CAAC,GAAG,EAAE,iBAAiB,GAAG,MAAM,EAAE;IAUtE;;OAEG;IACH,MAAM,CAAC,kCAAkC,CAAC,GAAG,EAAE,iBAAiB,GAAG,MAAM,EAAE;CAoB5E"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jrmc/adonis-attachment
|
|
3
|
+
*
|
|
4
|
+
* @license MIT
|
|
5
|
+
* @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
|
|
6
|
+
*/
|
|
7
|
+
import { Attachment } from '../../attachments/attachment.js';
|
|
8
|
+
import { optionsSym } from '../../utils/symbols.js';
|
|
9
|
+
export class AttachmentUtils {
|
|
10
|
+
/**
|
|
11
|
+
* Get attachments by attribute name, handling both single and array values
|
|
12
|
+
*/
|
|
13
|
+
static getAttachmentsByAttributeName(row, name) {
|
|
14
|
+
if (Array.isArray(row.$attributes[name])) {
|
|
15
|
+
return row.$attributes[name];
|
|
16
|
+
}
|
|
17
|
+
return [row.$attributes[name]];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Get original attachments by attribute name, handling both single and array values
|
|
21
|
+
*/
|
|
22
|
+
static getOriginalAttachmentsByAttributeName(row, name) {
|
|
23
|
+
if (Array.isArray(row.$original[name])) {
|
|
24
|
+
return row.$original[name];
|
|
25
|
+
}
|
|
26
|
+
return [row.$original[name]];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Get dirty attachments by attribute name, handling both single and array values
|
|
30
|
+
*/
|
|
31
|
+
static getDirtyAttachmentsByAttributeName(row, name) {
|
|
32
|
+
if (Array.isArray(row.$dirty[name])) {
|
|
33
|
+
return row.$dirty[name];
|
|
34
|
+
}
|
|
35
|
+
return [row.$dirty[name]];
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Get options by attribute name from the model prototype
|
|
39
|
+
*/
|
|
40
|
+
static getOptionsByAttributeName(row, name) {
|
|
41
|
+
return row.constructor.prototype[optionsSym]?.[name];
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Get all attribute names that contain attachments
|
|
45
|
+
*/
|
|
46
|
+
static getAttributeNamesOfAttachment(row) {
|
|
47
|
+
return Object.keys(row.$attributes).filter((name) => {
|
|
48
|
+
const value = row.$attributes[name];
|
|
49
|
+
return (value instanceof Attachment ||
|
|
50
|
+
(Array.isArray(value) && value.every((item) => item instanceof Attachment)));
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Get dirty attribute names that contain attachments
|
|
55
|
+
*/
|
|
56
|
+
static getDirtyAttributeNamesOfAttachment(row) {
|
|
57
|
+
return Object.keys(row.$dirty).filter((name) => {
|
|
58
|
+
const dirtyValue = row.$dirty[name];
|
|
59
|
+
const originalValue = row.$original[name]; // if dirtyValue is null, check original type
|
|
60
|
+
const isDirtyAttachment = dirtyValue instanceof Attachment ||
|
|
61
|
+
(Array.isArray(dirtyValue) &&
|
|
62
|
+
dirtyValue.length &&
|
|
63
|
+
dirtyValue.every((item) => item instanceof Attachment));
|
|
64
|
+
const isOriginalAttachment = originalValue instanceof Attachment ||
|
|
65
|
+
(Array.isArray(originalValue) &&
|
|
66
|
+
originalValue.length &&
|
|
67
|
+
originalValue.every((item) => item instanceof Attachment));
|
|
68
|
+
return isDirtyAttachment || isOriginalAttachment;
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { RecordWithAttachment as RecordWithAttachmentImplementation } from '../../types/service.js';
|
|
2
|
+
import type { RegenerateOptions } from '../../types/regenerate.js';
|
|
3
|
+
export interface VariantOptions {
|
|
4
|
+
variants?: string[];
|
|
5
|
+
}
|
|
6
|
+
export declare class AttachmentVariantService {
|
|
7
|
+
#private;
|
|
8
|
+
/**
|
|
9
|
+
* Generate variants for all dirty attachment attributes
|
|
10
|
+
*/
|
|
11
|
+
generateVariants(record: RecordWithAttachmentImplementation): Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* Regenerate variants with specific options
|
|
14
|
+
*/
|
|
15
|
+
regenerateVariants(record: RecordWithAttachmentImplementation, options?: RegenerateOptions): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=attachment_variant_service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attachment_variant_service.d.ts","sourceRoot":"","sources":["../../../../src/services/attachment/attachment_variant_service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,IAAI,kCAAkC,EAAE,MAAM,wBAAwB,CAAA;AACxG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAA;AAOlE,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;CACpB;AAED,qBAAa,wBAAwB;;IACnC;;OAEG;IACG,gBAAgB,CAAC,MAAM,EAAE,kCAAkC,GAAG,OAAO,CAAC,IAAI,CAAC;IAajF;;OAEG;IACG,kBAAkB,CACtB,MAAM,EAAE,kCAAkC,EAC1C,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,IAAI,CAAC;CAyDjB"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import logger from '@adonisjs/core/services/logger';
|
|
2
|
+
import attachmentManager from '../../../services/main.js';
|
|
3
|
+
import VariantService from '../variant_service.js';
|
|
4
|
+
import { AttachmentUtils } from './attachment_utils.js';
|
|
5
|
+
import { E_CANNOT_CREATE_VARIANT } from '../../errors.js';
|
|
6
|
+
export class AttachmentVariantService {
|
|
7
|
+
/**
|
|
8
|
+
* Generate variants for all dirty attachment attributes
|
|
9
|
+
*/
|
|
10
|
+
async generateVariants(record) {
|
|
11
|
+
/* this.#row.$dirty is not available in afterSave hooks */
|
|
12
|
+
const attachmentAttributeNames = record.row.$attachments.dirtied;
|
|
13
|
+
for await (const name of attachmentAttributeNames) {
|
|
14
|
+
if (!record.row.$attributes[name]) {
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
this.#queueVariantGeneration(name, record, {});
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Regenerate variants with specific options
|
|
22
|
+
*/
|
|
23
|
+
async regenerateVariants(record, options = {}) {
|
|
24
|
+
let attachmentAttributeNames;
|
|
25
|
+
if (options.attributes?.length) {
|
|
26
|
+
attachmentAttributeNames = options.attributes;
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
attachmentAttributeNames = AttachmentUtils.getAttributeNamesOfAttachment(record.row);
|
|
30
|
+
}
|
|
31
|
+
for await (const name of attachmentAttributeNames) {
|
|
32
|
+
if (!record.row.$attributes[name]) {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
this.#queueVariantGeneration(name, record, { variants: options.variants });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Queue variant generation with error handling
|
|
40
|
+
*/
|
|
41
|
+
#queueVariantGeneration(name, record, options) {
|
|
42
|
+
attachmentManager.queue.push({
|
|
43
|
+
name: `${record.row.constructor.name}-${name}`,
|
|
44
|
+
async run() {
|
|
45
|
+
const model = record.row.constructor;
|
|
46
|
+
await attachmentManager.lock.createLock(`attachment.${model.table}-${name}`).run(async () => {
|
|
47
|
+
const variantService = new VariantService({
|
|
48
|
+
record,
|
|
49
|
+
attributeName: name,
|
|
50
|
+
options: AttachmentUtils.getOptionsByAttributeName(record.row, name),
|
|
51
|
+
filters: {
|
|
52
|
+
variants: options.variants
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
await variantService.run();
|
|
56
|
+
});
|
|
57
|
+
},
|
|
58
|
+
})
|
|
59
|
+
.onError = (error) => this.#handleVariantError(error);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Handle variant generation errors
|
|
63
|
+
*/
|
|
64
|
+
#handleVariantError(error) {
|
|
65
|
+
if (error.message) {
|
|
66
|
+
logger.error(error.message);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
throw new E_CANNOT_CREATE_VARIANT([error]);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jrmc/adonis-attachment
|
|
3
|
+
*
|
|
4
|
+
* @license MIT
|
|
5
|
+
* @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
|
|
6
|
+
*/
|
|
7
|
+
export { default as AttachmentRecordService } from './attachment_recorder_service.js';
|
|
8
|
+
export { AttachmentTransactionService } from './attachment_transaction_service.js';
|
|
9
|
+
export { AttachmentPersisterService } from './attachment_persister_service.js';
|
|
10
|
+
export { AttachmentVariantService } from './attachment_variant_service.js';
|
|
11
|
+
export { AttachmentDetachmentService } from './attachment_detachment_service.js';
|
|
12
|
+
export { AttachmentUtils } from './attachment_utils.js';
|
|
13
|
+
export type { TransactionOptions } from './attachment_transaction_service.js';
|
|
14
|
+
export type { VariantOptions } from './attachment_variant_service.js';
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/services/attachment/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,OAAO,IAAI,uBAAuB,EAAE,MAAM,kCAAkC,CAAA;AAGrF,OAAO,EAAE,4BAA4B,EAAE,MAAM,qCAAqC,CAAA;AAClF,OAAO,EAAE,0BAA0B,EAAE,MAAM,mCAAmC,CAAA;AAC9E,OAAO,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAA;AAC1E,OAAO,EAAE,2BAA2B,EAAE,MAAM,oCAAoC,CAAA;AAGhF,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAGvD,YAAY,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAA;AAC7E,YAAY,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAA"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jrmc/adonis-attachment
|
|
3
|
+
*
|
|
4
|
+
* @license MIT
|
|
5
|
+
* @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
|
|
6
|
+
*/
|
|
7
|
+
// Main service
|
|
8
|
+
export { default as AttachmentRecordService } from './attachment_recorder_service.js';
|
|
9
|
+
// Specialized services
|
|
10
|
+
export { AttachmentTransactionService } from './attachment_transaction_service.js';
|
|
11
|
+
export { AttachmentPersisterService } from './attachment_persister_service.js';
|
|
12
|
+
export { AttachmentVariantService } from './attachment_variant_service.js';
|
|
13
|
+
export { AttachmentDetachmentService } from './attachment_detachment_service.js';
|
|
14
|
+
// Utilities
|
|
15
|
+
export { AttachmentUtils } from './attachment_utils.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attachment_service.d.ts","sourceRoot":"","sources":["../../../src/services/attachment_service.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,uBAAuB,IAAI,OAAO,EAAE,MAAM,uBAAuB,CAAA"}
|