@atproto/aws 0.1.6 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/kms.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as aws from '@aws-sdk/client-kms';
2
2
  import * as crypto from '@atproto/crypto';
3
- export declare type KmsConfig = {
3
+ export type KmsConfig = {
4
4
  keyId: string;
5
5
  } & Omit<aws.KMSClientConfig, 'apiVersion'>;
6
6
  export declare class KmsKeypair implements crypto.Keypair {
package/dist/s3.d.ts CHANGED
@@ -3,13 +3,15 @@ import * as aws from '@aws-sdk/client-s3';
3
3
  import { BlobStore } from '@atproto/repo';
4
4
  import { CID } from 'multiformats/cid';
5
5
  import stream from 'stream';
6
- export declare type S3Config = {
6
+ export type S3Config = {
7
7
  bucket: string;
8
8
  } & Omit<aws.S3ClientConfig, 'apiVersion'>;
9
9
  export declare class S3BlobStore implements BlobStore {
10
+ did: string;
10
11
  private client;
11
12
  private bucket;
12
- constructor(cfg: S3Config);
13
+ constructor(did: string, cfg: S3Config);
14
+ static creator(cfg: S3Config): (did: string) => S3BlobStore;
13
15
  private genKey;
14
16
  private getTmpPath;
15
17
  private getStoredPath;
@@ -23,8 +25,12 @@ export declare class S3BlobStore implements BlobStore {
23
25
  getBytes(cid: CID): Promise<Uint8Array>;
24
26
  getStream(cid: CID): Promise<stream.Readable>;
25
27
  delete(cid: CID): Promise<void>;
28
+ deleteMany(cids: CID[]): Promise<void>;
26
29
  hasStored(cid: CID): Promise<boolean>;
30
+ hasTemp(key: string): Promise<boolean>;
31
+ private hasKey;
27
32
  private deleteKey;
33
+ private deleteManyKeys;
28
34
  private move;
29
35
  }
30
36
  export default S3BlobStore;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atproto/aws",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "license": "MIT",
5
5
  "description": "Shared AWS cloud API helpers for atproto services",
6
6
  "keywords": [
@@ -25,7 +25,7 @@
25
25
  "uint8arrays": "3.0.0",
26
26
  "@atproto/common": "^0.3.3",
27
27
  "@atproto/crypto": "^0.3.0",
28
- "@atproto/repo": "^0.3.6"
28
+ "@atproto/repo": "^0.3.8"
29
29
  },
30
30
  "scripts": {
31
31
  "build": "node ./build.js",
package/src/s3.ts CHANGED
@@ -17,7 +17,7 @@ export class S3BlobStore implements BlobStore {
17
17
  private client: aws.S3
18
18
  private bucket: string
19
19
 
20
- constructor(cfg: S3Config) {
20
+ constructor(public did: string, cfg: S3Config) {
21
21
  const { bucket, ...rest } = cfg
22
22
  this.bucket = bucket
23
23
  this.client = new aws.S3({
@@ -26,20 +26,26 @@ export class S3BlobStore implements BlobStore {
26
26
  })
27
27
  }
28
28
 
29
+ static creator(cfg: S3Config) {
30
+ return (did: string) => {
31
+ return new S3BlobStore(did, cfg)
32
+ }
33
+ }
34
+
29
35
  private genKey() {
30
36
  return randomStr(32, 'base32')
31
37
  }
32
38
 
33
39
  private getTmpPath(key: string): string {
34
- return `tmp/${key}`
40
+ return `tmp/${this.did}/${key}`
35
41
  }
36
42
 
37
43
  private getStoredPath(cid: CID): string {
38
- return `blocks/${cid.toString()}`
44
+ return `blocks/${this.did}/${cid.toString()}`
39
45
  }
40
46
 
41
47
  private getQuarantinedPath(cid: CID): string {
42
- return `quarantine/${cid.toString()}`
48
+ return `quarantine/${this.did}/${cid.toString()}`
43
49
  }
44
50
 
45
51
  async putTemp(bytes: Uint8Array | stream.Readable): Promise<string> {
@@ -122,11 +128,24 @@ export class S3BlobStore implements BlobStore {
122
128
  await this.deleteKey(this.getStoredPath(cid))
123
129
  }
124
130
 
131
+ async deleteMany(cids: CID[]): Promise<void> {
132
+ const keys = cids.map((cid) => this.getStoredPath(cid))
133
+ await this.deleteManyKeys(keys)
134
+ }
135
+
125
136
  async hasStored(cid: CID): Promise<boolean> {
137
+ return this.hasKey(this.getStoredPath(cid))
138
+ }
139
+
140
+ async hasTemp(key: string): Promise<boolean> {
141
+ return this.hasKey(this.getTmpPath(key))
142
+ }
143
+
144
+ private async hasKey(key: string) {
126
145
  try {
127
146
  const res = await this.client.headObject({
128
147
  Bucket: this.bucket,
129
- Key: this.getStoredPath(cid),
148
+ Key: key,
130
149
  })
131
150
  return res.$metadata.httpStatusCode === 200
132
151
  } catch (err) {
@@ -141,17 +160,38 @@ export class S3BlobStore implements BlobStore {
141
160
  })
142
161
  }
143
162
 
144
- private async move(keys: { from: string; to: string }) {
145
- await this.client.copyObject({
146
- Bucket: this.bucket,
147
- CopySource: `${this.bucket}/${keys.from}`,
148
- Key: keys.to,
149
- })
150
- await this.client.deleteObject({
163
+ private async deleteManyKeys(keys: string[]) {
164
+ await this.client.deleteObjects({
151
165
  Bucket: this.bucket,
152
- Key: keys.from,
166
+ Delete: {
167
+ Objects: keys.map((k) => ({ Key: k })),
168
+ },
153
169
  })
154
170
  }
171
+
172
+ private async move(keys: { from: string; to: string }) {
173
+ try {
174
+ await this.client.copyObject({
175
+ Bucket: this.bucket,
176
+ CopySource: `${this.bucket}/${keys.from}`,
177
+ Key: keys.to,
178
+ })
179
+ await this.client.deleteObject({
180
+ Bucket: this.bucket,
181
+ Key: keys.from,
182
+ })
183
+ } catch (err) {
184
+ handleErr(err)
185
+ }
186
+ }
187
+ }
188
+
189
+ const handleErr = (err: unknown) => {
190
+ if (err?.['Code'] === 'NoSuchKey') {
191
+ throw new BlobNotFoundError()
192
+ } else {
193
+ throw err
194
+ }
155
195
  }
156
196
 
157
197
  export default S3BlobStore