@jrmc/adonis-attachment 5.0.0-beta.3 → 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.
Files changed (71) hide show
  1. package/build/providers/attachment_provider.d.ts.map +1 -1
  2. package/build/providers/attachment_provider.js +9 -1
  3. package/build/services/regenerate_service.js +3 -3
  4. package/build/src/adapters/lock.d.ts +9 -0
  5. package/build/src/adapters/lock.d.ts.map +1 -0
  6. package/build/src/adapters/lock.js +21 -0
  7. package/build/src/attachment_manager.d.ts +2 -2
  8. package/build/src/attachment_manager.d.ts.map +1 -1
  9. package/build/src/attachment_manager.js +6 -2
  10. package/build/src/controllers/attachments_controller.d.ts.map +1 -1
  11. package/build/src/controllers/attachments_controller.js +21 -12
  12. package/build/src/services/attachment/attachment_detachment_service.d.ts +19 -0
  13. package/build/src/services/attachment/attachment_detachment_service.d.ts.map +1 -0
  14. package/build/src/services/attachment/attachment_detachment_service.js +64 -0
  15. package/build/src/services/attachment/attachment_persister_service.d.ts +22 -0
  16. package/build/src/services/attachment/attachment_persister_service.d.ts.map +1 -0
  17. package/build/src/services/attachment/attachment_persister_service.js +93 -0
  18. package/build/src/services/attachment/attachment_recorder_service.d.ts +68 -0
  19. package/build/src/services/attachment/attachment_recorder_service.d.ts.map +1 -0
  20. package/build/src/services/attachment/attachment_recorder_service.js +119 -0
  21. package/build/src/services/attachment/attachment_transaction_service.d.ts +26 -0
  22. package/build/src/services/attachment/attachment_transaction_service.d.ts.map +1 -0
  23. package/build/src/services/attachment/attachment_transaction_service.js +43 -0
  24. package/build/src/services/attachment/attachment_utils.d.ts +35 -0
  25. package/build/src/services/attachment/attachment_utils.d.ts.map +1 -0
  26. package/build/src/services/attachment/attachment_utils.js +71 -0
  27. package/build/src/services/attachment/attachment_variant_service.d.ts +17 -0
  28. package/build/src/services/attachment/attachment_variant_service.d.ts.map +1 -0
  29. package/build/src/services/attachment/attachment_variant_service.js +72 -0
  30. package/build/src/services/attachment/index.d.ts +15 -0
  31. package/build/src/services/attachment/index.d.ts.map +1 -0
  32. package/build/src/services/attachment/index.js +15 -0
  33. package/build/src/services/attachment_service.d.ts +8 -0
  34. package/build/src/services/attachment_service.d.ts.map +1 -0
  35. package/build/src/services/attachment_service.js +7 -0
  36. package/build/src/services/variant/{variant_generator.d.ts → variant_generator_service.d.ts} +8 -2
  37. package/build/src/services/variant/variant_generator_service.d.ts.map +1 -0
  38. package/build/src/services/variant/{variant_generator.js → variant_generator_service.js} +7 -1
  39. package/build/src/services/variant/{variant_persister.d.ts → variant_persister_service.d.ts} +8 -2
  40. package/build/src/services/variant/variant_persister_service.d.ts.map +1 -0
  41. package/build/src/services/variant/{variant_persister.js → variant_persister_service.js} +7 -1
  42. package/build/src/services/variant/{variant_purger.d.ts → variant_purger_service.d.ts} +7 -1
  43. package/build/src/services/variant/variant_purger_service.d.ts.map +1 -0
  44. package/build/src/services/variant/{variant_purger.js → variant_purger_service.js} +6 -0
  45. package/build/src/services/variant_service.d.ts +6 -0
  46. package/build/src/services/variant_service.d.ts.map +1 -1
  47. package/build/src/services/variant_service.js +13 -7
  48. package/build/src/types/index.d.ts +6 -0
  49. package/build/src/types/index.d.ts.map +1 -1
  50. package/build/src/types/index.js +6 -0
  51. package/build/src/types/lock.d.ts +14 -0
  52. package/build/src/types/lock.d.ts.map +1 -0
  53. package/build/src/types/lock.js +7 -0
  54. package/build/src/types/metadata.d.ts +6 -0
  55. package/build/src/types/metadata.d.ts.map +1 -1
  56. package/build/src/types/metadata.js +6 -0
  57. package/build/src/types/regenerate.d.ts +6 -0
  58. package/build/src/types/regenerate.d.ts.map +1 -1
  59. package/build/src/types/regenerate.js +6 -0
  60. package/build/src/types/service.d.ts +6 -0
  61. package/build/src/types/service.d.ts.map +1 -1
  62. package/build/src/types/service.js +6 -0
  63. package/build/src/utils/hooks.js +5 -5
  64. package/build/tsconfig.tsbuildinfo +1 -1
  65. package/package.json +32 -23
  66. package/build/src/services/record_with_attachment.d.ts +0 -33
  67. package/build/src/services/record_with_attachment.d.ts.map +0 -1
  68. package/build/src/services/record_with_attachment.js +0 -316
  69. package/build/src/services/variant/variant_generator.d.ts.map +0 -1
  70. package/build/src/services/variant/variant_persister.d.ts.map +0 -1
  71. package/build/src/services/variant/variant_purger.d.ts.map +0 -1
@@ -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;IAqBF,IAAI;CAQX"}
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,7 +19,14 @@ 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');
21
- const lock = await this.app.container.make('lock.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
+ }
22
30
  if (!config) {
23
31
  throw new RuntimeException('Invalid config exported from "config/attachment.ts" file. Make sure to use the defineConfig method');
24
32
  }
@@ -1,4 +1,4 @@
1
- import RecordWithAttachment from '../src/services/record_with_attachment.js';
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 RecordWithAttachment(this.#row);
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 RecordWithAttachment(entity);
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
+ };
@@ -5,8 +5,8 @@
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';
9
8
  import type { MultipartFile } from '@adonisjs/core/bodyparser';
9
+ import type { LockService } from './types/lock.js';
10
10
  import type { AttachmentAttributes, AttachmentBase, Attachment as AttachmentType } from './types/attachment.js';
11
11
  import type { ResolvedAttachmentConfig } from './define_config.js';
12
12
  import { DeferQueue } from '@poppinss/defer';
@@ -14,7 +14,6 @@ import Converter from './converters/converter.js';
14
14
  export declare class AttachmentManager<KnownConverters extends Record<string, Converter>> {
15
15
  #private;
16
16
  queue: DeferQueue;
17
- lock: LockService;
18
17
  constructor(config: ResolvedAttachmentConfig<KnownConverters>, drive: DriveService, lock: LockService);
19
18
  createFromDbResponse(response?: string | JSON): AttachmentType | null;
20
19
  createFromFile(input: MultipartFile): Promise<AttachmentType>;
@@ -38,5 +37,6 @@ export declare class AttachmentManager<KnownConverters extends Record<string, Co
38
37
  write(attachment: AttachmentBase): Promise<void>;
39
38
  remove(attachment: AttachmentBase): Promise<void>;
40
39
  getConfig(): ResolvedAttachmentConfig<KnownConverters>;
40
+ get lock(): LockService;
41
41
  }
42
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,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"}
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,10 +17,10 @@ export class AttachmentManager {
17
17
  queue;
18
18
  #config;
19
19
  #drive;
20
- lock;
20
+ #lock;
21
21
  constructor(config, drive, lock) {
22
22
  this.#drive = drive;
23
- this.lock = lock;
23
+ this.#lock = lock;
24
24
  this.#config = config;
25
25
  const concurrency = this.#config.queue?.concurrency || 1;
26
26
  this.queue = new DeferQueue({ concurrency });
@@ -158,9 +158,13 @@ export class AttachmentManager {
158
158
  await attachment.getDisk().delete(attachment.originalPath);
159
159
  }
160
160
  }
161
+ // getters
161
162
  getConfig() {
162
163
  return this.#config;
163
164
  }
165
+ get lock() {
166
+ return this.#lock;
167
+ }
164
168
  // private methods
165
169
  #configureAttachment(attachment) {
166
170
  if (this.#config.meta !== undefined) {
@@ -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;AAoBtD,MAAM,CAAC,OAAO,OAAO,qBAAqB;IAElC,MAAM,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,WAAW;CAgGhD"}
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"}
@@ -3,8 +3,8 @@ import { Readable } from 'node:stream';
3
3
  import encryption from '@adonisjs/core/services/encryption';
4
4
  import db from '@adonisjs/lucid/services/db';
5
5
  import { attachmentManager } from '@jrmc/adonis-attachment';
6
- import VariantGenerator from '../services/variant/variant_generator.js';
7
- import VariantPersister from '../services/variant/variant_persister.js';
6
+ import VariantGeneratorService from '../services/variant/variant_generator_service.js';
7
+ import VariantPersisterService from '../services/variant/variant_persister_service.js';
8
8
  export default class AttachmentsController {
9
9
  async handle({ request, response }) {
10
10
  const { key } = request.params();
@@ -56,37 +56,46 @@ export default class AttachmentsController {
56
56
  */
57
57
  if (!variant && format) {
58
58
  const converter = (await attachmentManager.getConverter(format));
59
- variant = await (new VariantGenerator()).generateVariant({
59
+ variant = await (new VariantGeneratorService()).generateVariant({
60
60
  key: format,
61
61
  attachment: currentAttachment,
62
62
  converter
63
63
  });
64
64
  if (variant) {
65
- const variantPersister = new VariantPersister({
65
+ const variantPersister = new VariantPersisterService({
66
66
  id: data.id,
67
67
  modelTable: data.model,
68
68
  attributeName: data.attribute,
69
69
  multiple
70
70
  });
71
- await variantPersister.persist({ attachments: attachments ?? [currentAttachment], variants: [variant] });
71
+ const attachmentsOrCurrent = attachments.length ? attachments : [currentAttachment];
72
+ await variantPersister.persist({ attachments: attachmentsOrCurrent, variants: [variant] });
72
73
  }
73
74
  }
74
75
  /*
75
76
  * 5. Get the stream
76
77
  */
77
- let file;
78
+ let stream;
78
79
  let mimeType;
79
80
  if (variant) {
80
- file = await variant.getStream();
81
+ stream = await variant.getStream();
81
82
  mimeType = variant.mimeType;
82
83
  }
83
- else {
84
- file = await currentAttachment.getStream();
84
+ else if (currentAttachment) {
85
+ stream = await currentAttachment.getStream();
85
86
  mimeType = currentAttachment.mimeType;
86
87
  }
87
- const readable = Readable.from(file);
88
- response.header('Content-Type', mimeType);
89
- response.stream(readable);
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();
98
+ }
90
99
  });
91
100
  }
92
101
  }
@@ -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
+ }
@@ -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
+ }): 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;IACH,cAAc,CAAC,OAAO,EAAE;QACtB,aAAa,EAAE,MAAM,CAAA;QACrB,gBAAgB,CAAC,EAAE,OAAO,CAAA;QAC1B,aAAa,CAAC,EAAE,OAAO,CAAA;KACxB,GAAG,cAAc,EAAE;CAoBrB"}
@@ -0,0 +1,119 @@
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
+ 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((attachment) => {
113
+ if (attachment) {
114
+ attachment.setOptions(opts).makeFolder(this.#row);
115
+ }
116
+ });
117
+ return attachments;
118
+ }
119
+ }
@@ -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;IAQpE;;OAEG;IACG,iBAAiB,CACrB,MAAM,EAAE,kCAAkC,EAC1C,OAAO,GAAE,kBAA8C,GACtD,OAAO,CAAC,IAAI,CAAC;CAiBjB"}