@bitblit/ratchet-aws 4.0.80-alpha

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 (241) hide show
  1. package/dist/cjs/batch/aws-batch-background-processor.js +44 -0
  2. package/dist/cjs/batch/aws-batch-ratchet.js +55 -0
  3. package/dist/cjs/build/ratchet-aws-info.js +18 -0
  4. package/dist/cjs/cache/dynamo-db-storage-provider.js +111 -0
  5. package/dist/cjs/cache/s3-storage-provider.js +44 -0
  6. package/dist/cjs/cache/simple-cache-object-wrapper.js +2 -0
  7. package/dist/cjs/cache/simple-cache-read-options.js +2 -0
  8. package/dist/cjs/cache/simple-cache-storage-provider.js +2 -0
  9. package/dist/cjs/cache/simple-cache.js +66 -0
  10. package/dist/cjs/cloudwatch/cloud-watch-log-group-ratchet.js +73 -0
  11. package/dist/cjs/cloudwatch/cloud-watch-logs-ratchet.js +173 -0
  12. package/dist/cjs/cloudwatch/cloud-watch-metrics-ratchet.js +57 -0
  13. package/dist/cjs/daemon/daemon-like.js +2 -0
  14. package/dist/cjs/daemon/daemon-process-create-options.js +2 -0
  15. package/dist/cjs/daemon/daemon-process-state-public-token.js +2 -0
  16. package/dist/cjs/daemon/daemon-process-state.js +2 -0
  17. package/dist/cjs/daemon/daemon-util.js +152 -0
  18. package/dist/cjs/daemon/daemon.js +126 -0
  19. package/dist/cjs/dao/prototype-dao-config.js +2 -0
  20. package/dist/cjs/dao/prototype-dao-db.js +2 -0
  21. package/dist/cjs/dao/prototype-dao-provider.js +2 -0
  22. package/dist/cjs/dao/prototype-dao.js +88 -0
  23. package/dist/cjs/dao/s3-prototype-dao-provider.js +28 -0
  24. package/dist/cjs/dao/s3-simple-dao.js +78 -0
  25. package/dist/cjs/dao/simple-dao-item.js +2 -0
  26. package/dist/cjs/dynamodb/dynamo-ratchet-like.js +2 -0
  27. package/dist/cjs/dynamodb/dynamo-ratchet.js +667 -0
  28. package/dist/cjs/dynamodb/dynamo-table-ratchet.js +91 -0
  29. package/dist/cjs/dynamodb/hash-spreader.js +62 -0
  30. package/dist/cjs/ec2/ec2-ratchet.js +107 -0
  31. package/dist/cjs/environment/cascade-environment-service-provider.js +27 -0
  32. package/dist/cjs/environment/env-var-environment-service-provider.js +33 -0
  33. package/dist/cjs/environment/environment-service-config.js +2 -0
  34. package/dist/cjs/environment/environment-service-provider.js +2 -0
  35. package/dist/cjs/environment/environment-service.js +52 -0
  36. package/dist/cjs/environment/fixed-environment-service-provider.js +24 -0
  37. package/dist/cjs/environment/s3-environment-service-provider.js +29 -0
  38. package/dist/cjs/environment/ssm-environment-service-provider.js +61 -0
  39. package/dist/cjs/expiring-code/dynamo-expiring-code-provider.js +26 -0
  40. package/dist/cjs/expiring-code/expiring-code-params.js +2 -0
  41. package/dist/cjs/expiring-code/expiring-code-provider.js +2 -0
  42. package/dist/cjs/expiring-code/expiring-code-ratchet.js +37 -0
  43. package/dist/cjs/expiring-code/expiring-code.js +2 -0
  44. package/dist/cjs/expiring-code/s3-expiring-code-provider.js +49 -0
  45. package/dist/cjs/iam/aws-credentials-ratchet.js +21 -0
  46. package/dist/cjs/index.js +81 -0
  47. package/dist/cjs/lambda/lambda-event-detector.js +42 -0
  48. package/dist/cjs/lambda/lambda-event-type-guards.js +28 -0
  49. package/dist/cjs/model/cloud-watch-metrics-minute-level-dynamo-count-request.js +2 -0
  50. package/dist/cjs/model/cloud-watch-metrics-unit.js +33 -0
  51. package/dist/cjs/model/dynamo/doc-put-item-command-input.js +2 -0
  52. package/dist/cjs/model/dynamo/doc-query-command-input.js +2 -0
  53. package/dist/cjs/model/dynamo/doc-scan-command-input.js +2 -0
  54. package/dist/cjs/model/dynamo/doc-update-item-command-input.js +2 -0
  55. package/dist/cjs/model/dynamo-count-result.js +2 -0
  56. package/dist/cjs/route53/route-53-ratchet.js +57 -0
  57. package/dist/cjs/runtime-parameter/cached-stored-runtime-parameter.js +2 -0
  58. package/dist/cjs/runtime-parameter/dynamo-runtime-parameter-provider.js +38 -0
  59. package/dist/cjs/runtime-parameter/global-variable-override-runtime-parameter-provider.js +54 -0
  60. package/dist/cjs/runtime-parameter/memory-runtime-parameter-provider.js +30 -0
  61. package/dist/cjs/runtime-parameter/runtime-parameter-provider.js +2 -0
  62. package/dist/cjs/runtime-parameter/runtime-parameter-ratchet.js +74 -0
  63. package/dist/cjs/runtime-parameter/stored-runtime-parameter.js +2 -0
  64. package/dist/cjs/s3/s3-cache-ratchet.js +332 -0
  65. package/dist/cjs/s3/s3-cache-to-local-disk-ratchet.js +105 -0
  66. package/dist/cjs/s3/s3-location-sync-ratchet.js +142 -0
  67. package/dist/cjs/s3/s3-ratchet.js +26 -0
  68. package/dist/cjs/ses/email-attachment.js +2 -0
  69. package/dist/cjs/ses/mailer-config.js +2 -0
  70. package/dist/cjs/ses/mailer-like.js +2 -0
  71. package/dist/cjs/ses/mailer.js +208 -0
  72. package/dist/cjs/ses/ratchet-template-renderer.js +2 -0
  73. package/dist/cjs/ses/ready-to-send-email.js +2 -0
  74. package/dist/cjs/ses/remote-handlebars-template-renderer.js +79 -0
  75. package/dist/cjs/ses/resolved-ready-to-send-email.js +2 -0
  76. package/dist/cjs/sns/sns-ratchet.js +47 -0
  77. package/dist/cjs/sync-lock/dynamo-db-sync-lock.js +69 -0
  78. package/dist/cjs/sync-lock/memory-sync-lock.js +40 -0
  79. package/dist/cjs/sync-lock/sync-lock-provider.js +2 -0
  80. package/dist/es/batch/aws-batch-background-processor.js +40 -0
  81. package/dist/es/batch/aws-batch-ratchet.js +51 -0
  82. package/dist/es/build/ratchet-aws-info.js +14 -0
  83. package/dist/es/cache/dynamo-db-storage-provider.js +107 -0
  84. package/dist/es/cache/s3-storage-provider.js +40 -0
  85. package/dist/es/cache/simple-cache-object-wrapper.js +1 -0
  86. package/dist/es/cache/simple-cache-read-options.js +1 -0
  87. package/dist/es/cache/simple-cache-storage-provider.js +1 -0
  88. package/dist/es/cache/simple-cache.js +62 -0
  89. package/dist/es/cloudwatch/cloud-watch-log-group-ratchet.js +69 -0
  90. package/dist/es/cloudwatch/cloud-watch-logs-ratchet.js +169 -0
  91. package/dist/es/cloudwatch/cloud-watch-metrics-ratchet.js +53 -0
  92. package/dist/es/daemon/daemon-like.js +1 -0
  93. package/dist/es/daemon/daemon-process-create-options.js +1 -0
  94. package/dist/es/daemon/daemon-process-state-public-token.js +1 -0
  95. package/dist/es/daemon/daemon-process-state.js +1 -0
  96. package/dist/es/daemon/daemon-util.js +148 -0
  97. package/dist/es/daemon/daemon.js +122 -0
  98. package/dist/es/dao/prototype-dao-config.js +1 -0
  99. package/dist/es/dao/prototype-dao-db.js +1 -0
  100. package/dist/es/dao/prototype-dao-provider.js +1 -0
  101. package/dist/es/dao/prototype-dao.js +84 -0
  102. package/dist/es/dao/s3-prototype-dao-provider.js +24 -0
  103. package/dist/es/dao/s3-simple-dao.js +74 -0
  104. package/dist/es/dao/simple-dao-item.js +1 -0
  105. package/dist/es/dynamodb/dynamo-ratchet-like.js +1 -0
  106. package/dist/es/dynamodb/dynamo-ratchet.js +663 -0
  107. package/dist/es/dynamodb/dynamo-table-ratchet.js +87 -0
  108. package/dist/es/dynamodb/hash-spreader.js +58 -0
  109. package/dist/es/ec2/ec2-ratchet.js +103 -0
  110. package/dist/es/environment/cascade-environment-service-provider.js +23 -0
  111. package/dist/es/environment/env-var-environment-service-provider.js +29 -0
  112. package/dist/es/environment/environment-service-config.js +1 -0
  113. package/dist/es/environment/environment-service-provider.js +1 -0
  114. package/dist/es/environment/environment-service.js +48 -0
  115. package/dist/es/environment/fixed-environment-service-provider.js +20 -0
  116. package/dist/es/environment/s3-environment-service-provider.js +25 -0
  117. package/dist/es/environment/ssm-environment-service-provider.js +56 -0
  118. package/dist/es/expiring-code/dynamo-expiring-code-provider.js +22 -0
  119. package/dist/es/expiring-code/expiring-code-params.js +1 -0
  120. package/dist/es/expiring-code/expiring-code-provider.js +1 -0
  121. package/dist/es/expiring-code/expiring-code-ratchet.js +33 -0
  122. package/dist/es/expiring-code/expiring-code.js +1 -0
  123. package/dist/es/expiring-code/s3-expiring-code-provider.js +45 -0
  124. package/dist/es/iam/aws-credentials-ratchet.js +17 -0
  125. package/dist/es/index.js +78 -0
  126. package/dist/es/lambda/lambda-event-detector.js +38 -0
  127. package/dist/es/lambda/lambda-event-type-guards.js +24 -0
  128. package/dist/es/model/cloud-watch-metrics-minute-level-dynamo-count-request.js +1 -0
  129. package/dist/es/model/cloud-watch-metrics-unit.js +30 -0
  130. package/dist/es/model/dynamo/doc-put-item-command-input.js +1 -0
  131. package/dist/es/model/dynamo/doc-query-command-input.js +1 -0
  132. package/dist/es/model/dynamo/doc-scan-command-input.js +1 -0
  133. package/dist/es/model/dynamo/doc-update-item-command-input.js +1 -0
  134. package/dist/es/model/dynamo-count-result.js +1 -0
  135. package/dist/es/route53/route-53-ratchet.js +53 -0
  136. package/dist/es/runtime-parameter/cached-stored-runtime-parameter.js +1 -0
  137. package/dist/es/runtime-parameter/dynamo-runtime-parameter-provider.js +34 -0
  138. package/dist/es/runtime-parameter/global-variable-override-runtime-parameter-provider.js +49 -0
  139. package/dist/es/runtime-parameter/memory-runtime-parameter-provider.js +26 -0
  140. package/dist/es/runtime-parameter/runtime-parameter-provider.js +1 -0
  141. package/dist/es/runtime-parameter/runtime-parameter-ratchet.js +70 -0
  142. package/dist/es/runtime-parameter/stored-runtime-parameter.js +1 -0
  143. package/dist/es/s3/s3-cache-ratchet.js +328 -0
  144. package/dist/es/s3/s3-cache-to-local-disk-ratchet.js +100 -0
  145. package/dist/es/s3/s3-location-sync-ratchet.js +137 -0
  146. package/dist/es/s3/s3-ratchet.js +22 -0
  147. package/dist/es/ses/email-attachment.js +1 -0
  148. package/dist/es/ses/mailer-config.js +1 -0
  149. package/dist/es/ses/mailer-like.js +1 -0
  150. package/dist/es/ses/mailer.js +204 -0
  151. package/dist/es/ses/ratchet-template-renderer.js +1 -0
  152. package/dist/es/ses/ready-to-send-email.js +1 -0
  153. package/dist/es/ses/remote-handlebars-template-renderer.js +74 -0
  154. package/dist/es/ses/resolved-ready-to-send-email.js +1 -0
  155. package/dist/es/sns/sns-ratchet.js +43 -0
  156. package/dist/es/sync-lock/dynamo-db-sync-lock.js +65 -0
  157. package/dist/es/sync-lock/memory-sync-lock.js +36 -0
  158. package/dist/es/sync-lock/sync-lock-provider.js +1 -0
  159. package/dist/tsconfig.cjs.tsbuildinfo +1 -0
  160. package/dist/tsconfig.es.tsbuildinfo +1 -0
  161. package/dist/tsconfig.types.tsbuildinfo +1 -0
  162. package/dist/types/batch/aws-batch-background-processor.d.ts +12 -0
  163. package/dist/types/batch/aws-batch-ratchet.d.ts +16 -0
  164. package/dist/types/build/ratchet-aws-info.d.ts +5 -0
  165. package/dist/types/cache/dynamo-db-storage-provider.d.ts +25 -0
  166. package/dist/types/cache/s3-storage-provider.d.ts +14 -0
  167. package/dist/types/cache/simple-cache-object-wrapper.d.ts +7 -0
  168. package/dist/types/cache/simple-cache-read-options.d.ts +5 -0
  169. package/dist/types/cache/simple-cache-storage-provider.d.ts +8 -0
  170. package/dist/types/cache/simple-cache.d.ts +14 -0
  171. package/dist/types/cloudwatch/cloud-watch-log-group-ratchet.d.ts +9 -0
  172. package/dist/types/cloudwatch/cloud-watch-logs-ratchet.d.ts +14 -0
  173. package/dist/types/cloudwatch/cloud-watch-metrics-ratchet.d.ts +10 -0
  174. package/dist/types/daemon/daemon-like.d.ts +17 -0
  175. package/dist/types/daemon/daemon-process-create-options.d.ts +7 -0
  176. package/dist/types/daemon/daemon-process-state-public-token.d.ts +4 -0
  177. package/dist/types/daemon/daemon-process-state.d.ts +13 -0
  178. package/dist/types/daemon/daemon-util.d.ts +24 -0
  179. package/dist/types/daemon/daemon.d.ts +33 -0
  180. package/dist/types/dao/prototype-dao-config.d.ts +8 -0
  181. package/dist/types/dao/prototype-dao-db.d.ts +4 -0
  182. package/dist/types/dao/prototype-dao-provider.d.ts +5 -0
  183. package/dist/types/dao/prototype-dao.d.ts +15 -0
  184. package/dist/types/dao/s3-prototype-dao-provider.d.ts +10 -0
  185. package/dist/types/dao/s3-simple-dao.d.ts +15 -0
  186. package/dist/types/dao/simple-dao-item.d.ts +5 -0
  187. package/dist/types/dynamodb/dynamo-ratchet-like.d.ts +27 -0
  188. package/dist/types/dynamodb/dynamo-ratchet.d.ts +36 -0
  189. package/dist/types/dynamodb/dynamo-table-ratchet.d.ts +11 -0
  190. package/dist/types/dynamodb/hash-spreader.d.ts +15 -0
  191. package/dist/types/ec2/ec2-ratchet.d.ts +25 -0
  192. package/dist/types/environment/cascade-environment-service-provider.d.ts +9 -0
  193. package/dist/types/environment/env-var-environment-service-provider.d.ts +10 -0
  194. package/dist/types/environment/environment-service-config.d.ts +7 -0
  195. package/dist/types/environment/environment-service-provider.d.ts +7 -0
  196. package/dist/types/environment/environment-service.d.ts +14 -0
  197. package/dist/types/environment/fixed-environment-service-provider.d.ts +10 -0
  198. package/dist/types/environment/s3-environment-service-provider.d.ts +18 -0
  199. package/dist/types/environment/ssm-environment-service-provider.d.ts +12 -0
  200. package/dist/types/expiring-code/dynamo-expiring-code-provider.d.ts +12 -0
  201. package/dist/types/expiring-code/expiring-code-params.d.ts +7 -0
  202. package/dist/types/expiring-code/expiring-code-provider.d.ts +5 -0
  203. package/dist/types/expiring-code/expiring-code-ratchet.d.ts +13 -0
  204. package/dist/types/expiring-code/expiring-code.d.ts +6 -0
  205. package/dist/types/expiring-code/s3-expiring-code-provider.d.ts +17 -0
  206. package/dist/types/iam/aws-credentials-ratchet.d.ts +9 -0
  207. package/dist/types/index.d.ts +81 -0
  208. package/dist/types/lambda/lambda-event-detector.d.ts +14 -0
  209. package/dist/types/lambda/lambda-event-type-guards.d.ts +10 -0
  210. package/dist/types/model/cloud-watch-metrics-minute-level-dynamo-count-request.d.ts +12 -0
  211. package/dist/types/model/cloud-watch-metrics-unit.d.ts +29 -0
  212. package/dist/types/model/dynamo/doc-put-item-command-input.d.ts +4 -0
  213. package/dist/types/model/dynamo/doc-query-command-input.d.ts +5 -0
  214. package/dist/types/model/dynamo/doc-scan-command-input.d.ts +5 -0
  215. package/dist/types/model/dynamo/doc-update-item-command-input.d.ts +5 -0
  216. package/dist/types/model/dynamo-count-result.d.ts +5 -0
  217. package/dist/types/route53/route-53-ratchet.d.ts +7 -0
  218. package/dist/types/runtime-parameter/cached-stored-runtime-parameter.d.ts +4 -0
  219. package/dist/types/runtime-parameter/dynamo-runtime-parameter-provider.d.ts +11 -0
  220. package/dist/types/runtime-parameter/global-variable-override-runtime-parameter-provider.d.ts +24 -0
  221. package/dist/types/runtime-parameter/memory-runtime-parameter-provider.d.ts +13 -0
  222. package/dist/types/runtime-parameter/runtime-parameter-provider.d.ts +11 -0
  223. package/dist/types/runtime-parameter/runtime-parameter-ratchet.d.ts +15 -0
  224. package/dist/types/runtime-parameter/stored-runtime-parameter.d.ts +6 -0
  225. package/dist/types/s3/s3-cache-ratchet.d.ts +38 -0
  226. package/dist/types/s3/s3-cache-to-local-disk-ratchet.d.ts +21 -0
  227. package/dist/types/s3/s3-location-sync-ratchet.d.ts +21 -0
  228. package/dist/types/s3/s3-ratchet.d.ts +5 -0
  229. package/dist/types/ses/email-attachment.d.ts +23 -0
  230. package/dist/types/ses/mailer-config.d.ts +15 -0
  231. package/dist/types/ses/mailer-like.d.ts +18 -0
  232. package/dist/types/ses/mailer.d.ts +26 -0
  233. package/dist/types/ses/ratchet-template-renderer.d.ts +8 -0
  234. package/dist/types/ses/ready-to-send-email.d.ts +66 -0
  235. package/dist/types/ses/remote-handlebars-template-renderer.d.ts +15 -0
  236. package/dist/types/ses/resolved-ready-to-send-email.d.ts +16 -0
  237. package/dist/types/sns/sns-ratchet.d.ts +8 -0
  238. package/dist/types/sync-lock/dynamo-db-sync-lock.d.ts +10 -0
  239. package/dist/types/sync-lock/memory-sync-lock.d.ts +11 -0
  240. package/dist/types/sync-lock/sync-lock-provider.d.ts +5 -0
  241. package/package.json +112 -0
@@ -0,0 +1,328 @@
1
+ import { CopyObjectCommand, DeleteObjectCommand, GetObjectCommand, HeadObjectCommand, ListObjectsCommand, NoSuchKey, } from '@aws-sdk/client-s3';
2
+ import { Logger } from '@bitblit/ratchet-common';
3
+ import { RequireRatchet } from '@bitblit/ratchet-common';
4
+ import { StopWatch } from '@bitblit/ratchet-common';
5
+ import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
6
+ import { Upload } from '@aws-sdk/lib-storage';
7
+ import { StringRatchet } from '@bitblit/ratchet-common';
8
+ import { StreamRatchet } from '@bitblit/ratchet-common';
9
+ export class S3CacheRatchet {
10
+ constructor(s3, defaultBucket = null) {
11
+ this.s3 = s3;
12
+ this.defaultBucket = defaultBucket;
13
+ RequireRatchet.notNullOrUndefined(this.s3, 's3');
14
+ }
15
+ static applyCacheControlMaxAge(input, seconds) {
16
+ if (input && seconds) {
17
+ input.CacheControl = 'max-age=' + seconds;
18
+ }
19
+ return input;
20
+ }
21
+ static applyUserMetaData(input, key, value) {
22
+ if (input && StringRatchet.trimToNull(key) && StringRatchet.trimToNull(value)) {
23
+ input.Metadata = input.Metadata || {};
24
+ input.Metadata[key] = value;
25
+ }
26
+ return input;
27
+ }
28
+ getDefaultBucket() {
29
+ return this.defaultBucket;
30
+ }
31
+ getS3Client() {
32
+ return this.s3;
33
+ }
34
+ async fileExists(key, bucket = null) {
35
+ try {
36
+ const head = await this.fetchMetaForCacheFile(key, this.bucketVal(bucket));
37
+ return !!head;
38
+ }
39
+ catch (err) {
40
+ Logger.silly('Error calling file exists (as expected) %s', err);
41
+ return false;
42
+ }
43
+ }
44
+ async fetchCacheFileAsS3GetObjectCommandOutput(key, bucket = null) {
45
+ let rval = null;
46
+ try {
47
+ const params = {
48
+ Bucket: this.bucketVal(bucket),
49
+ Key: key,
50
+ };
51
+ rval = await this.s3.send(new GetObjectCommand(params));
52
+ }
53
+ catch (err) {
54
+ if (err instanceof NoSuchKey) {
55
+ Logger.debug('Key %s not found - returning null', key);
56
+ rval = null;
57
+ }
58
+ else {
59
+ throw err;
60
+ }
61
+ }
62
+ return rval;
63
+ }
64
+ async fetchCacheFileAsReadableStream(key, bucket = null) {
65
+ const out = await this.fetchCacheFileAsS3GetObjectCommandOutput(key, bucket);
66
+ return out.Body.transformToWebStream();
67
+ }
68
+ async fetchCacheFileAsBuffer(key, bucket = null) {
69
+ let rval = null;
70
+ const out = await this.fetchCacheFileAsS3GetObjectCommandOutput(key, bucket);
71
+ if (out?.Body) {
72
+ const tmp = await out.Body.transformToByteArray();
73
+ rval = Buffer.from(tmp);
74
+ }
75
+ return rval;
76
+ }
77
+ async fetchCacheFileAsString(key, bucket = null) {
78
+ let rval = null;
79
+ const out = await this.fetchCacheFileAsS3GetObjectCommandOutput(key, bucket);
80
+ if (out?.Body) {
81
+ rval = await out.Body.transformToString();
82
+ }
83
+ return rval;
84
+ }
85
+ async fetchCacheFileAsObject(key, bucket = null) {
86
+ const value = await this.fetchCacheFileAsString(key, bucket);
87
+ return value ? JSON.parse(value) : null;
88
+ }
89
+ async removeCacheFile(key, bucket = null) {
90
+ let rval = null;
91
+ const params = {
92
+ Bucket: this.bucketVal(bucket),
93
+ Key: key,
94
+ };
95
+ try {
96
+ rval = await this.s3.send(new DeleteObjectCommand(params));
97
+ }
98
+ catch (err) {
99
+ if (err && err['statusCode'] == 404) {
100
+ Logger.info('Swallowing 404 deleting missing object %s %s', bucket, key);
101
+ rval = null;
102
+ }
103
+ else {
104
+ throw err;
105
+ }
106
+ }
107
+ return rval;
108
+ }
109
+ async writeObjectToCacheFile(key, dataObject, template, bucket) {
110
+ const json = JSON.stringify(dataObject);
111
+ return this.writeStringToCacheFile(key, json, template, bucket);
112
+ }
113
+ async writeStringToCacheFile(key, dataString, template, bucket) {
114
+ const stream = StreamRatchet.stringToWebReadableStream(dataString);
115
+ return this.writeStreamToCacheFile(key, stream, template, bucket);
116
+ }
117
+ async writeStreamToCacheFile(key, data, template, bucket) {
118
+ const params = Object.assign({}, template || {}, {
119
+ Bucket: this.bucketVal(bucket),
120
+ Key: key,
121
+ Body: data,
122
+ });
123
+ const upload = new Upload({
124
+ client: this.s3,
125
+ params: params,
126
+ tags: [],
127
+ queueSize: 4,
128
+ partSize: 1024 * 1024 * 5,
129
+ leavePartsOnError: false,
130
+ });
131
+ upload.on('httpUploadProgress', (progress) => {
132
+ Logger.info('Uploading : %s', progress);
133
+ });
134
+ const result = await upload.done();
135
+ return result;
136
+ }
137
+ async synchronize(srcPrefix, targetPrefix, targetRatchet = this, recurseSubFolders = false) {
138
+ RequireRatchet.notNullOrUndefined(srcPrefix, 'srcPrefix');
139
+ RequireRatchet.notNullOrUndefined(targetPrefix, 'targetPrefix');
140
+ RequireRatchet.true(srcPrefix.endsWith('/'), 'srcPrefix must end in /');
141
+ RequireRatchet.true(targetPrefix.endsWith('/'), 'targetPrefix must end in /');
142
+ let rval = [];
143
+ const sourceFiles = await this.directChildrenOfPrefix(srcPrefix);
144
+ const targetFiles = await targetRatchet.directChildrenOfPrefix(targetPrefix);
145
+ const sw = new StopWatch();
146
+ for (let i = 0; i < sourceFiles.length; i++) {
147
+ const sourceFile = sourceFiles[i];
148
+ Logger.info('Processing %s : %s', sourceFile, sw.dumpExpected(i / sourceFiles.length));
149
+ if (sourceFile.endsWith('/')) {
150
+ if (recurseSubFolders) {
151
+ Logger.info('%s is a subfolder - recursing');
152
+ const subs = await this.synchronize(srcPrefix + sourceFile, targetPrefix + sourceFile, targetRatchet, recurseSubFolders);
153
+ Logger.info('Got %d back from %s', subs.length, sourceFile);
154
+ rval = rval.concat(subs);
155
+ }
156
+ else {
157
+ Logger.info('%s is a subfolder and recurse not specified - skipping', sourceFile);
158
+ }
159
+ }
160
+ else {
161
+ let shouldCopy = true;
162
+ const srcMeta = await this.fetchMetaForCacheFile(srcPrefix + sourceFile);
163
+ if (targetFiles.includes(sourceFile)) {
164
+ const targetMeta = await targetRatchet.fetchMetaForCacheFile(targetPrefix + sourceFile);
165
+ if (srcMeta.ETag === targetMeta.ETag) {
166
+ Logger.debug('Skipping - identical');
167
+ shouldCopy = false;
168
+ }
169
+ }
170
+ if (shouldCopy) {
171
+ Logger.debug('Copying...');
172
+ const srcStream = await this.fetchCacheFileAsReadableStream(srcPrefix + sourceFile);
173
+ try {
174
+ const written = await targetRatchet.writeStreamToCacheFile(targetPrefix + sourceFile, srcStream, srcMeta, undefined);
175
+ Logger.silly('Write result : %j', written);
176
+ rval.push(sourceFile);
177
+ }
178
+ catch (err) {
179
+ Logger.error('Failed to sync : %s : %s', sourceFile, err);
180
+ }
181
+ }
182
+ }
183
+ }
184
+ Logger.info('Found %d files, copied %d', sourceFiles.length, rval.length);
185
+ sw.log();
186
+ return rval;
187
+ }
188
+ async fetchMetaForCacheFile(key, bucket = null) {
189
+ let rval = null;
190
+ try {
191
+ rval = await this.s3.send(new HeadObjectCommand({
192
+ Bucket: this.bucketVal(bucket),
193
+ Key: key,
194
+ }));
195
+ }
196
+ catch (err) {
197
+ if (err && err['statusCode'] == 404) {
198
+ Logger.info('Cache file %s %s not found returning null', this.bucketVal(bucket), key);
199
+ rval = null;
200
+ }
201
+ else {
202
+ Logger.error('Unrecognized error, rethrowing : %s', err, err);
203
+ throw err;
204
+ }
205
+ }
206
+ return rval;
207
+ }
208
+ async cacheFileAgeInSeconds(key, bucket = null) {
209
+ try {
210
+ const res = await this.fetchMetaForCacheFile(key, bucket);
211
+ if (res && res.LastModified) {
212
+ return Math.floor((new Date().getTime() - res.LastModified.getTime()) / 1000);
213
+ }
214
+ else {
215
+ Logger.warn('Cache file %s %s had no last modified returning null', this.bucketVal(bucket), key);
216
+ return null;
217
+ }
218
+ }
219
+ catch (err) {
220
+ if (err && err['statusCode'] == 404) {
221
+ Logger.warn('Cache file %s %s not found returning null', this.bucketVal(bucket), key);
222
+ return null;
223
+ }
224
+ else {
225
+ throw err;
226
+ }
227
+ }
228
+ }
229
+ async copyFile(srcKey, dstKey, srcBucket = null, dstBucket = null) {
230
+ const params = {
231
+ CopySource: '/' + this.bucketVal(srcBucket) + '/' + srcKey,
232
+ Bucket: this.bucketVal(dstBucket),
233
+ Key: dstKey,
234
+ MetadataDirective: 'COPY',
235
+ };
236
+ const rval = await this.s3.send(new CopyObjectCommand(params));
237
+ return rval;
238
+ }
239
+ async quietCopyFile(srcKey, dstKey, srcBucket = null, dstBucket = null) {
240
+ let rval = false;
241
+ try {
242
+ const tmp = await this.copyFile(srcKey, dstKey, srcBucket, dstBucket);
243
+ rval = true;
244
+ }
245
+ catch (err) {
246
+ Logger.silly('Failed to copy file in S3 : %s', err);
247
+ }
248
+ return rval;
249
+ }
250
+ async preSignedDownloadUrlForCacheFile(key, expirationSeconds = 3600, bucket = null) {
251
+ const getCommand = {
252
+ Bucket: this.bucketVal(bucket),
253
+ Key: key,
254
+ };
255
+ const link = await getSignedUrl(this.s3, new GetObjectCommand(getCommand), { expiresIn: expirationSeconds });
256
+ return link;
257
+ }
258
+ async directChildrenOfPrefix(prefix, expandFiles = false, bucket = null, maxToReturn = null) {
259
+ const returnValue = [];
260
+ const params = {
261
+ Bucket: this.bucketVal(bucket),
262
+ Prefix: prefix,
263
+ Delimiter: '/',
264
+ };
265
+ let response = null;
266
+ do {
267
+ response = await this.s3.send(new ListObjectsCommand(params));
268
+ const prefixLength = prefix.length;
269
+ if (response['CommonPrefixes']) {
270
+ response['CommonPrefixes'].forEach((cp) => {
271
+ if (!maxToReturn || returnValue.length < maxToReturn) {
272
+ const value = cp['Prefix'].substring(prefixLength);
273
+ returnValue.push(value);
274
+ }
275
+ });
276
+ }
277
+ if (response['Contents']) {
278
+ await Promise.all(response['Contents'].map(async (cp) => {
279
+ if (!maxToReturn || returnValue.length < maxToReturn) {
280
+ if (expandFiles) {
281
+ const expanded = {
282
+ link: await this.preSignedDownloadUrlForCacheFile(cp['Key'], 3600, bucket),
283
+ name: cp['Key'].substring(prefixLength),
284
+ size: cp['Size'],
285
+ };
286
+ returnValue.push(expanded);
287
+ }
288
+ else {
289
+ returnValue.push(cp['Key'].substring(prefixLength));
290
+ }
291
+ }
292
+ }));
293
+ }
294
+ params.Marker = response.NextMarker;
295
+ } while (params.Marker && (!maxToReturn || returnValue.length < maxToReturn));
296
+ return returnValue;
297
+ }
298
+ async allSubFoldersOfPrefix(prefix, bucket = null) {
299
+ const returnValue = [prefix];
300
+ let idx = 0;
301
+ while (idx < returnValue.length) {
302
+ const next = returnValue[idx++];
303
+ Logger.debug('Pulling %s (%d remaining)', next, returnValue.length - idx);
304
+ const req = {
305
+ Bucket: this.bucketVal(bucket),
306
+ Prefix: next,
307
+ Delimiter: '/',
308
+ };
309
+ let resp = null;
310
+ do {
311
+ req.ContinuationToken = resp ? resp.NextContinuationToken : null;
312
+ resp = await this.s3.send(new ListObjectsCommand(req));
313
+ resp.CommonPrefixes.forEach((p) => {
314
+ returnValue.push(p.Prefix);
315
+ });
316
+ Logger.debug('g:%j', resp);
317
+ } while (resp.NextContinuationToken);
318
+ }
319
+ return returnValue;
320
+ }
321
+ bucketVal(explicitBucket) {
322
+ const rval = explicitBucket ? explicitBucket : this.defaultBucket;
323
+ if (!rval) {
324
+ throw 'You must set either the default bucket or pass it explicitly';
325
+ }
326
+ return rval;
327
+ }
328
+ }
@@ -0,0 +1,100 @@
1
+ import crypto from 'crypto';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import { Logger, RequireRatchet, StringRatchet } from '@bitblit/ratchet-common';
5
+ export class S3CacheToLocalDiskRatchet {
6
+ constructor(s3, tmpFolder, cacheTimeoutSeconds = S3CacheToLocalDiskRatchet.DEFAULT_CACHE_TIMEOUT_SEC) {
7
+ this.s3 = s3;
8
+ this.tmpFolder = tmpFolder;
9
+ this.cacheTimeoutSeconds = cacheTimeoutSeconds;
10
+ this.currentlyLoading = new Map();
11
+ RequireRatchet.notNullOrUndefined(s3, 's3');
12
+ RequireRatchet.notNullOrUndefined(StringRatchet.trimToNull(tmpFolder));
13
+ RequireRatchet.true(fs.existsSync(tmpFolder), 'folder must exist : ' + tmpFolder);
14
+ }
15
+ async getFileString(key) {
16
+ const buf = await this.getFileBuffer(key);
17
+ return buf ? buf.toString() : null;
18
+ }
19
+ keyToLocalCachePath(key) {
20
+ const cachedHash = this.generateCacheHash(this.s3.getDefaultBucket() + '/' + key);
21
+ const rval = path.join(this.tmpFolder, cachedHash);
22
+ return rval;
23
+ }
24
+ removeCacheFileForKey(key) {
25
+ const localCachePath = this.keyToLocalCachePath(key);
26
+ Logger.info('Removing cache file for %s : %s', key, localCachePath);
27
+ if (fs.existsSync(localCachePath)) {
28
+ fs.unlinkSync(localCachePath);
29
+ }
30
+ else {
31
+ Logger.debug('Skipping delete for %s - does not exist', localCachePath);
32
+ }
33
+ }
34
+ async getFileBuffer(key) {
35
+ const localCachePath = this.keyToLocalCachePath(key);
36
+ let rval = null;
37
+ rval = this.getCacheFileAsBuffer(localCachePath);
38
+ if (!rval) {
39
+ Logger.info('No cache. Downloading File s3://%s/%s to %s', this.s3.getDefaultBucket(), key, localCachePath);
40
+ try {
41
+ let prom = this.currentlyLoading.get(key);
42
+ if (prom) {
43
+ Logger.info('Already running - wait for that');
44
+ }
45
+ else {
46
+ Logger.info('Not running - start');
47
+ prom = this.updateLocalCacheFile(key, localCachePath);
48
+ this.currentlyLoading.set(key, prom);
49
+ }
50
+ rval = await prom;
51
+ this.currentlyLoading.delete(key);
52
+ }
53
+ catch (err) {
54
+ Logger.warn('File %s/%s does not exist. Err code: %s', this.s3.getDefaultBucket(), key, err);
55
+ }
56
+ }
57
+ else {
58
+ Logger.info('Found cache file for s3://%s/%s. Local path %s', this.s3.getDefaultBucket(), key, localCachePath);
59
+ }
60
+ return rval;
61
+ }
62
+ async updateLocalCacheFile(key, localCachePath) {
63
+ const rval = await this.s3.fetchCacheFileAsBuffer(key);
64
+ if (rval && rval.length > 0) {
65
+ Logger.info('Saving %d bytes to disk for cache', rval.length);
66
+ fs.writeFileSync(localCachePath, rval);
67
+ }
68
+ return rval;
69
+ }
70
+ getCacheFileAsString(filePath) {
71
+ const buf = this.getCacheFileAsBuffer(filePath);
72
+ return buf ? buf.toString() : null;
73
+ }
74
+ getCacheFileAsBuffer(filePath) {
75
+ if (!fs.existsSync(filePath)) {
76
+ return null;
77
+ }
78
+ try {
79
+ const stats = fs.statSync(filePath);
80
+ const now = new Date().getTime();
81
+ const duration = (now - stats.ctimeMs) / 1000;
82
+ if (duration >= this.cacheTimeoutSeconds) {
83
+ return null;
84
+ }
85
+ else {
86
+ const rval = fs.readFileSync(filePath);
87
+ return rval;
88
+ }
89
+ }
90
+ catch (err) {
91
+ Logger.warn('Error getting s3 cache file %s', err);
92
+ }
93
+ return null;
94
+ }
95
+ generateCacheHash(hashVal) {
96
+ const rval = crypto.createHash('md5').update(hashVal).digest('hex');
97
+ return rval;
98
+ }
99
+ }
100
+ S3CacheToLocalDiskRatchet.DEFAULT_CACHE_TIMEOUT_SEC = 7 * 24 * 3600;
@@ -0,0 +1,137 @@
1
+ import { CopyObjectCommand, GetObjectCommand, ListObjectsV2Command, } from '@aws-sdk/client-s3';
2
+ import _ from 'lodash';
3
+ import { Logger, PromiseRatchet, RequireRatchet } from '@bitblit/ratchet-common';
4
+ import { Upload } from '@aws-sdk/lib-storage';
5
+ export class S3LocationSyncRatchet {
6
+ constructor(config) {
7
+ RequireRatchet.notNullOrUndefined(config, 'config');
8
+ this.config = config;
9
+ if (!this.config.maxNumThreads) {
10
+ this.config.maxNumThreads = 15;
11
+ }
12
+ if (!this.config.maxRetries) {
13
+ this.config.maxRetries = 5;
14
+ }
15
+ }
16
+ updateSrcPrefix(prefix) {
17
+ this.config.srcPrefix = prefix;
18
+ }
19
+ updateDstPrefix(prefix) {
20
+ this.config.dstPrefix = prefix;
21
+ }
22
+ async copyObject(key, size, express = false) {
23
+ const dstKey = key.replace(this.config.srcPrefix, this.config.dstPrefix);
24
+ let completedCopying = false;
25
+ let retries = 0;
26
+ while (!completedCopying && retries < this.config.maxRetries) {
27
+ Logger.debug(`${retries > 0 ? `Retry ${retries} ` : ''}${express ? 'Express' : 'Slow'} copying
28
+ [${[this.config.srcBucket, key].join('/')} ---> ${[this.config.dstBucket, dstKey].join('/')}]`);
29
+ try {
30
+ if (express) {
31
+ const params = {
32
+ CopySource: encodeURIComponent([this.config.srcBucket, key].join('/')),
33
+ Bucket: this.config.dstBucket,
34
+ Key: dstKey,
35
+ MetadataDirective: 'COPY',
36
+ };
37
+ await this.config.dstS3.send(new CopyObjectCommand(params));
38
+ }
39
+ else {
40
+ const fetched = await this.config.srcS3.send(new GetObjectCommand({ Bucket: this.config.srcBucket, Key: key }));
41
+ const params = {
42
+ Bucket: this.config.dstBucket,
43
+ Key: dstKey,
44
+ Body: fetched.Body,
45
+ ContentLength: size,
46
+ };
47
+ const upload = new Upload({
48
+ client: this.config.dstS3,
49
+ params: params,
50
+ tags: [],
51
+ queueSize: 4,
52
+ partSize: 1024 * 1024 * 5,
53
+ leavePartsOnError: false,
54
+ });
55
+ upload.on('httpUploadProgress', (progress) => {
56
+ Logger.info('Uploading : %s', progress);
57
+ });
58
+ await upload.done();
59
+ }
60
+ completedCopying = true;
61
+ }
62
+ catch (err) {
63
+ Logger.warn(`Can't ${express ? 'express' : 'slow'} copy
64
+ [${[this.config.srcBucket, key].join('/')} ---> ${[this.config.dstBucket, dstKey].join('/')}]: %j`, err);
65
+ retries++;
66
+ }
67
+ }
68
+ Logger.debug(`Finished ${express ? 'express' : 'slow'} copying
69
+ [${[this.config.srcBucket, key].join('/')} ---> ${[this.config.dstBucket, dstKey].join('/')}]`);
70
+ }
71
+ async listObjects(bucket, prefix, s3) {
72
+ Logger.info(`Scanning bucket [${[bucket, prefix].join('/')}]`);
73
+ const params = {
74
+ Bucket: bucket,
75
+ Prefix: prefix,
76
+ };
77
+ let more = true;
78
+ const rval = {};
79
+ while (more) {
80
+ const response = await s3.send(new ListObjectsV2Command(params));
81
+ more = response.IsTruncated;
82
+ _.each(response.Contents, (obj) => {
83
+ rval[obj.Key] = { Key: obj.Key, LastModified: obj.LastModified, ETag: obj.ETag, Size: obj.Size };
84
+ });
85
+ if (more) {
86
+ params.ContinuationToken = response.NextContinuationToken;
87
+ }
88
+ }
89
+ return rval;
90
+ }
91
+ async startSyncing() {
92
+ Logger.info(`Syncing [${this.config.srcBucket}/${this.config.srcPrefix}
93
+ ---> ${this.config.dstBucket}/${this.config.dstPrefix}]`);
94
+ const cp = async (obj) => {
95
+ await this.copyObject(obj.Key, obj.Size);
96
+ };
97
+ let cmpResult = await this.compareSrcAndDst();
98
+ if (cmpResult.needCopy.length > 0 || cmpResult.diff.length > 0) {
99
+ await PromiseRatchet.runBoundedParallelSingleParam(cp, cmpResult.needCopy, this, this.config.maxNumThreads);
100
+ await PromiseRatchet.runBoundedParallelSingleParam(cp, cmpResult.diff, this, this.config.maxNumThreads);
101
+ Logger.info('Verifying...');
102
+ cmpResult = await this.compareSrcAndDst();
103
+ Logger.debug('Compare result %j', cmpResult);
104
+ }
105
+ return cmpResult.needCopy.length === 0 && cmpResult.diff.length === 0;
106
+ }
107
+ async compareSrcAndDst() {
108
+ const getSrc = this.listObjects(this.config.srcBucket, this.config.srcPrefix, this.config.srcS3);
109
+ const getDst = this.listObjects(this.config.dstBucket, this.config.dstPrefix, this.config.dstS3);
110
+ const srcObjs = await getSrc;
111
+ const dstObjs = await getDst;
112
+ const rval = {
113
+ needCopy: [],
114
+ existed: [],
115
+ diff: [],
116
+ };
117
+ await PromiseRatchet.runBoundedParallelSingleParam((key) => {
118
+ const sObj = srcObjs[key];
119
+ const dstKey = key.replace(this.config.srcPrefix, this.config.dstPrefix);
120
+ const dObj = dstObjs.hasOwnProperty(dstKey) ? dstObjs[dstKey] : undefined;
121
+ if (!dObj) {
122
+ rval.needCopy.push(sObj);
123
+ return;
124
+ }
125
+ if (sObj.Size !== dObj.Size) {
126
+ rval.diff.push(sObj);
127
+ return;
128
+ }
129
+ if (sObj.LastModified.getTime() <= dObj.LastModified.getTime()) {
130
+ rval.existed.push(sObj);
131
+ return;
132
+ }
133
+ rval.diff.push(sObj);
134
+ }, Object.keys(srcObjs), this, this.config.maxNumThreads);
135
+ return rval;
136
+ }
137
+ }
@@ -0,0 +1,22 @@
1
+ import { RequireRatchet } from '@bitblit/ratchet-common';
2
+ export class S3Ratchet {
3
+ static checkS3UrlForValidity(value) {
4
+ let rval = false;
5
+ if (value) {
6
+ rval = value.startsWith('s3://') && value.trim().length > 5;
7
+ }
8
+ return rval;
9
+ }
10
+ static extractBucketFromURL(value) {
11
+ RequireRatchet.true(S3Ratchet.checkS3UrlForValidity(value), 'invalid s3 url');
12
+ const idx1 = value.indexOf('/', 5);
13
+ const rval = idx1 > 0 ? value.substring(5, idx1) : value.substring(5);
14
+ return rval;
15
+ }
16
+ static extractKeyFromURL(value) {
17
+ RequireRatchet.true(S3Ratchet.checkS3UrlForValidity(value), 'invalid s3 url');
18
+ const idx1 = value.indexOf('/', 5);
19
+ const rval = idx1 > 0 ? value.substring(idx1 + 1) : null;
20
+ return rval;
21
+ }
22
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};