@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.
Files changed (81) hide show
  1. package/build/index.d.ts +0 -1
  2. package/build/index.d.ts.map +1 -1
  3. package/build/index.js +0 -1
  4. package/build/providers/attachment_provider.d.ts.map +1 -1
  5. package/build/providers/attachment_provider.js +10 -1
  6. package/build/services/regenerate_service.js +3 -3
  7. package/build/src/adapters/lock.d.ts +9 -0
  8. package/build/src/adapters/lock.d.ts.map +1 -0
  9. package/build/src/adapters/lock.js +21 -0
  10. package/build/src/attachment_manager.d.ts +4 -2
  11. package/build/src/attachment_manager.d.ts.map +1 -1
  12. package/build/src/attachment_manager.js +7 -1
  13. package/build/src/attachments/attachment.d.ts +1 -1
  14. package/build/src/attachments/attachment.js +1 -1
  15. package/build/src/controllers/attachments_controller.d.ts.map +1 -1
  16. package/build/src/controllers/attachments_controller.js +83 -74
  17. package/build/src/decorators/attachment.d.ts +3 -3
  18. package/build/src/decorators/attachment.d.ts.map +1 -1
  19. package/build/src/services/attachment/attachment_detachment_service.d.ts +19 -0
  20. package/build/src/services/attachment/attachment_detachment_service.d.ts.map +1 -0
  21. package/build/src/services/attachment/attachment_detachment_service.js +64 -0
  22. package/build/src/services/attachment/attachment_persister_service.d.ts +22 -0
  23. package/build/src/services/attachment/attachment_persister_service.d.ts.map +1 -0
  24. package/build/src/services/attachment/attachment_persister_service.js +93 -0
  25. package/build/src/services/attachment/attachment_recorder_service.d.ts +68 -0
  26. package/build/src/services/attachment/attachment_recorder_service.d.ts.map +1 -0
  27. package/build/src/services/attachment/attachment_recorder_service.js +119 -0
  28. package/build/src/services/attachment/attachment_transaction_service.d.ts +26 -0
  29. package/build/src/services/attachment/attachment_transaction_service.d.ts.map +1 -0
  30. package/build/src/services/attachment/attachment_transaction_service.js +43 -0
  31. package/build/src/services/attachment/attachment_utils.d.ts +35 -0
  32. package/build/src/services/attachment/attachment_utils.d.ts.map +1 -0
  33. package/build/src/services/attachment/attachment_utils.js +71 -0
  34. package/build/src/services/attachment/attachment_variant_service.d.ts +17 -0
  35. package/build/src/services/attachment/attachment_variant_service.d.ts.map +1 -0
  36. package/build/src/services/attachment/attachment_variant_service.js +72 -0
  37. package/build/src/services/attachment/index.d.ts +15 -0
  38. package/build/src/services/attachment/index.d.ts.map +1 -0
  39. package/build/src/services/attachment/index.js +15 -0
  40. package/build/src/services/attachment_service.d.ts +8 -0
  41. package/build/src/services/attachment_service.d.ts.map +1 -0
  42. package/build/src/services/attachment_service.js +7 -0
  43. package/build/src/services/variant/variant_generator_service.d.ts +24 -0
  44. package/build/src/services/variant/variant_generator_service.d.ts.map +1 -0
  45. package/build/src/services/variant/variant_generator_service.js +97 -0
  46. package/build/src/services/variant/variant_persister_service.d.ts +23 -0
  47. package/build/src/services/variant/variant_persister_service.d.ts.map +1 -0
  48. package/build/src/services/variant/variant_persister_service.js +60 -0
  49. package/build/src/services/variant/variant_purger_service.d.ts +15 -0
  50. package/build/src/services/variant/variant_purger_service.d.ts.map +1 -0
  51. package/build/src/services/variant/variant_purger_service.js +48 -0
  52. package/build/src/services/variant_service.d.ts +13 -0
  53. package/build/src/services/variant_service.d.ts.map +1 -0
  54. package/build/src/services/variant_service.js +58 -0
  55. package/build/src/types/attachment.d.ts +3 -3
  56. package/build/src/types/attachment.d.ts.map +1 -1
  57. package/build/src/types/index.d.ts +6 -0
  58. package/build/src/types/index.d.ts.map +1 -1
  59. package/build/src/types/index.js +6 -0
  60. package/build/src/types/lock.d.ts +14 -0
  61. package/build/src/types/lock.d.ts.map +1 -0
  62. package/build/src/types/lock.js +7 -0
  63. package/build/src/types/metadata.d.ts +6 -0
  64. package/build/src/types/metadata.d.ts.map +1 -1
  65. package/build/src/types/metadata.js +6 -0
  66. package/build/src/types/regenerate.d.ts +6 -0
  67. package/build/src/types/regenerate.d.ts.map +1 -1
  68. package/build/src/types/regenerate.js +6 -0
  69. package/build/src/types/service.d.ts +6 -0
  70. package/build/src/types/service.d.ts.map +1 -1
  71. package/build/src/types/service.js +6 -0
  72. package/build/src/utils/hooks.js +5 -5
  73. package/build/stubs/config.stub +102 -4
  74. package/build/tsconfig.tsbuildinfo +1 -1
  75. package/package.json +27 -22
  76. package/build/src/converter_manager.d.ts +0 -13
  77. package/build/src/converter_manager.d.ts.map +0 -1
  78. package/build/src/converter_manager.js +0 -121
  79. package/build/src/services/record_with_attachment.d.ts +0 -33
  80. package/build/src/services/record_with_attachment.d.ts.map +0 -1
  81. 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';
@@ -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,gBAAgB,EAAE,MAAM,4BAA4B,CAAA;AAC7D,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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,iBAAiB,MAAM,oBAAoB,CAAA;AAClD,OAAO,iBAAiB,MAAM,kCAAkC,CAAA;AAEhE,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAA;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAA;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAA;AACrD,OAAO,KAAK,MAAM,MAAM,iBAAiB,CAAA;AACzC,OAAO,EAAE,iBAAiB,EAAE,CAAA;AAC5B,OAAO,EAAE,iBAAiB,EAAE,CAAA;AAC5B,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,uBAAuB,CAAA"}
package/build/index.js CHANGED
@@ -1,7 +1,6 @@
1
1
  import attachmentManager from './services/main.js';
2
2
  import RegenerateService from './services/regenerate_service.js';
3
3
  export { configure } from './configure.js';
4
- export { ConverterManager } from './src/converter_manager.js';
5
4
  export { Attachment } from './src/attachments/attachment.js';
6
5
  export { attachment } from './src/decorators/attachment.js';
7
6
  export { attachments } from './src/decorators/attachment.js';
@@ -1 +1 @@
1
- {"version":3,"file":"attachment_provider.d.ts","sourceRoot":"","sources":["../../providers/attachment_provider.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AAC9D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAA;AAIhD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAE/D,OAAO,QAAQ,sBAAsB,CAAC;IACpC,UAAiB,iBAAiB;QAChC,iBAAiB,EAAE,iBAAiB,CAAA;KACrC;CACF;AAED,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,MAAM;QACd,WAAW,EAAE,CAAC,OAAO,CAAC,EAAE,MAAM,KAAK,KAAK,CAAA;KACzC;CACF;AAED,MAAM,CAAC,OAAO,OAAO,kBAAkB;;IAGzB,SAAS,CAAC,GAAG,EAAE,kBAAkB;gBAAvB,GAAG,EAAE,kBAAkB;IAE7C,QAAQ;IAoBF,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,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 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
+ };
@@ -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 | undefined;
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;gBAIO,MAAM,EAAE,wBAAwB,CAAC,eAAe,CAAC,EAAE,KAAK,EAAE,YAAY;IASlF,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,8 +17,10 @@ export class AttachmentManager {
17
17
  queue;
18
18
  #config;
19
19
  #drive;
20
- constructor(config, drive) {
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 | undefined;
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;AAkBtD,MAAM,CAAC,OAAO,OAAO,qBAAqB;IAElC,MAAM,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,WAAW;CAkGhD"}
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 { ConverterManager } from '../converter_manager.js';
6
- import { Readable } from 'node:stream';
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
- const index = request.qs()?.index;
12
- let isAttachments = false;
12
+ let multiple = false;
13
13
  const data = encryption.decrypt(key);
14
- const queryWithTableSelection = await db
15
- .from(data.model)
16
- .select(data.attribute)
17
- .where('id', data.id).first();
18
- /*
19
- * 1. Get the entity
20
- */
21
- let result = JSON.parse(queryWithTableSelection[data.attribute]);
22
- if (Array.isArray(result)) {
23
- isAttachments = true;
24
- result = result[index || 0];
25
- }
26
- result.folder = path.dirname(result.path);
27
- /*
28
- * 2. Get the attachment
29
- */
30
- const attachment = attachmentManager.createFromDbResponse(result);
31
- attachment?.setOptions(data?.options);
32
- if (!attachment) {
33
- return response.notFound();
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
- attachmentOrAttachmentsString = JSON.stringify(attachment.toObject());
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
- const trx = await db.transaction();
60
- trx.after('rollback', () => {
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
- attachmentManager.remove(variant);
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
- catch (error) {
72
- await trx.rollback();
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;AAC7D,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,aAtBkD,YAAY,cACxD,GAAG,iBAAiB,MAAM,SAqBJ,CAAA;AACnD,eAAO,MAAM,WAAW,aAvBiD,YAAY,cACxD,GAAG,iBAAiB,MAAM,SAiCpD,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
+ }