@atproto/pds 0.5.27 → 0.5.29
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/CHANGELOG.md +58 -0
- package/dist/actor-store/blob/transactor.d.ts +1 -5
- package/dist/actor-store/blob/transactor.d.ts.map +1 -1
- package/dist/actor-store/blob/transactor.js +72 -43
- package/dist/actor-store/blob/transactor.js.map +1 -1
- package/dist/api/com/atproto/identity/resolveHandle.d.ts.map +1 -1
- package/dist/api/com/atproto/identity/resolveHandle.js +1 -1
- package/dist/api/com/atproto/identity/resolveHandle.js.map +1 -1
- package/dist/api/com/atproto/repo/uploadBlob.d.ts.map +1 -1
- package/dist/api/com/atproto/repo/uploadBlob.js +1 -1
- package/dist/api/com/atproto/repo/uploadBlob.js.map +1 -1
- package/dist/auth-verifier.d.ts.map +1 -1
- package/dist/auth-verifier.js +12 -6
- package/dist/auth-verifier.js.map +1 -1
- package/dist/context.d.ts +2 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +6 -0
- package/dist/context.js.map +1 -1
- package/dist/lexicons/app/bsky/actor/contentVisibilityDeclaration.d.ts +3 -0
- package/dist/lexicons/app/bsky/actor/contentVisibilityDeclaration.d.ts.map +1 -0
- package/dist/lexicons/app/bsky/actor/contentVisibilityDeclaration.defs.d.ts +66 -0
- package/dist/lexicons/app/bsky/actor/contentVisibilityDeclaration.defs.d.ts.map +1 -0
- package/dist/lexicons/app/bsky/actor/contentVisibilityDeclaration.defs.js +26 -0
- package/dist/lexicons/app/bsky/actor/contentVisibilityDeclaration.defs.js.map +1 -0
- package/dist/lexicons/app/bsky/actor/contentVisibilityDeclaration.js +6 -0
- package/dist/lexicons/app/bsky/actor/contentVisibilityDeclaration.js.map +1 -0
- package/dist/lexicons/app/bsky/actor.d.ts +1 -0
- package/dist/lexicons/app/bsky/actor.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/actor.js +1 -0
- package/dist/lexicons/app/bsky/actor.js.map +1 -1
- package/dist/lexicons/app/bsky/authCreatePosts.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/authCreatePosts.defs.js +5 -0
- package/dist/lexicons/app/bsky/authCreatePosts.defs.js.map +1 -1
- package/dist/lexicons/app/bsky/authFullApp.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/authFullApp.defs.js +5 -0
- package/dist/lexicons/app/bsky/authFullApp.defs.js.map +1 -1
- package/dist/lexicons/app/bsky/embed/video.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/embed/video.defs.js +2 -4
- package/dist/lexicons/app/bsky/embed/video.defs.js.map +1 -1
- package/dist/lexicons/app/bsky/video/abortUpload.d.ts +3 -0
- package/dist/lexicons/app/bsky/video/abortUpload.d.ts.map +1 -0
- package/dist/lexicons/app/bsky/video/abortUpload.defs.d.ts +53 -0
- package/dist/lexicons/app/bsky/video/abortUpload.defs.d.ts.map +1 -0
- package/dist/lexicons/app/bsky/video/abortUpload.defs.js +26 -0
- package/dist/lexicons/app/bsky/video/abortUpload.defs.js.map +1 -0
- package/dist/lexicons/app/bsky/video/abortUpload.js +6 -0
- package/dist/lexicons/app/bsky/video/abortUpload.js.map +1 -0
- package/dist/lexicons/app/bsky/video/defs.defs.d.ts +1 -1
- package/dist/lexicons/app/bsky/video/defs.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/video/defs.defs.js.map +1 -1
- package/dist/lexicons/app/bsky/video/finishUpload.d.ts +3 -0
- package/dist/lexicons/app/bsky/video/finishUpload.d.ts.map +1 -0
- package/dist/lexicons/app/bsky/video/finishUpload.defs.d.ts +42 -0
- package/dist/lexicons/app/bsky/video/finishUpload.defs.d.ts.map +1 -0
- package/dist/lexicons/app/bsky/video/finishUpload.defs.js +30 -0
- package/dist/lexicons/app/bsky/video/finishUpload.defs.js.map +1 -0
- package/dist/lexicons/app/bsky/video/finishUpload.js +6 -0
- package/dist/lexicons/app/bsky/video/finishUpload.js.map +1 -0
- package/dist/lexicons/app/bsky/video/getUploadStatus.d.ts +3 -0
- package/dist/lexicons/app/bsky/video/getUploadStatus.d.ts.map +1 -0
- package/dist/lexicons/app/bsky/video/getUploadStatus.defs.d.ts +73 -0
- package/dist/lexicons/app/bsky/video/getUploadStatus.defs.d.ts.map +1 -0
- package/dist/lexicons/app/bsky/video/getUploadStatus.defs.js +30 -0
- package/dist/lexicons/app/bsky/video/getUploadStatus.defs.js.map +1 -0
- package/dist/lexicons/app/bsky/video/getUploadStatus.js +6 -0
- package/dist/lexicons/app/bsky/video/getUploadStatus.js.map +1 -0
- package/dist/lexicons/app/bsky/video/startUpload.d.ts +3 -0
- package/dist/lexicons/app/bsky/video/startUpload.d.ts.map +1 -0
- package/dist/lexicons/app/bsky/video/startUpload.defs.d.ts +63 -0
- package/dist/lexicons/app/bsky/video/startUpload.defs.d.ts.map +1 -0
- package/dist/lexicons/app/bsky/video/startUpload.defs.js +36 -0
- package/dist/lexicons/app/bsky/video/startUpload.defs.js.map +1 -0
- package/dist/lexicons/app/bsky/video/startUpload.js +6 -0
- package/dist/lexicons/app/bsky/video/startUpload.js.map +1 -0
- package/dist/lexicons/app/bsky/video/uploadPart.d.ts +3 -0
- package/dist/lexicons/app/bsky/video/uploadPart.d.ts.map +1 -0
- package/dist/lexicons/app/bsky/video/uploadPart.defs.d.ts +37 -0
- package/dist/lexicons/app/bsky/video/uploadPart.defs.d.ts.map +1 -0
- package/dist/lexicons/app/bsky/video/uploadPart.defs.js +31 -0
- package/dist/lexicons/app/bsky/video/uploadPart.defs.js.map +1 -0
- package/dist/lexicons/app/bsky/video/uploadPart.js +6 -0
- package/dist/lexicons/app/bsky/video/uploadPart.js.map +1 -0
- package/dist/lexicons/app/bsky/video.d.ts +5 -0
- package/dist/lexicons/app/bsky/video.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/video.js +5 -0
- package/dist/lexicons/app/bsky/video.js.map +1 -1
- package/dist/mailer/templates/confirm-email.js +44 -1198
- package/dist/mailer/templates/confirm-email.js.map +1 -7
- package/dist/mailer/templates/delete-account.js +25 -1190
- package/dist/mailer/templates/delete-account.js.map +1 -7
- package/dist/mailer/templates/plc-operation.js +25 -1190
- package/dist/mailer/templates/plc-operation.js.map +1 -7
- package/dist/mailer/templates/reset-password.js +33 -1190
- package/dist/mailer/templates/reset-password.js.map +1 -7
- package/dist/mailer/templates/update-email.js +25 -1190
- package/dist/mailer/templates/update-email.js.map +1 -7
- package/dist/scripts/index.d.ts +2 -2
- package/dist/scripts/index.d.ts.map +1 -1
- package/dist/scripts/index.js +2 -2
- package/dist/scripts/index.js.map +1 -1
- package/dist/telemetry.js +8 -186
- package/dist/telemetry.js.map +1 -1
- package/example.env +55 -0
- package/package.json +23 -32
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,63 @@
|
|
|
1
1
|
# @atproto/pds
|
|
2
2
|
|
|
3
|
+
## 0.5.29
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#5399](https://github.com/bluesky-social/atproto/pull/5399) [`b2d9a77`](https://github.com/bluesky-social/atproto/commit/b2d9a7715e00472b45ef6db2dc07f1e16be5bccd) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Add `example.env` file to the NPM package
|
|
8
|
+
|
|
9
|
+
- [#5390](https://github.com/bluesky-social/atproto/pull/5390) [`924bb41`](https://github.com/bluesky-social/atproto/commit/924bb410ed9fa4a60d3ed623d8fd40aef310b142) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Fix `rebuild-repo` script
|
|
10
|
+
|
|
11
|
+
- [#5397](https://github.com/bluesky-social/atproto/pull/5397) [`aff31c8`](https://github.com/bluesky-social/atproto/commit/aff31c815dd69168f3529e667e8bc6e61272bc63) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Extract the OpenTelemetry Node bootstrap out of the PDS into a new
|
|
12
|
+
`@atproto-labs/opentelemetry-node` package. It exposes a `setup()` function from
|
|
13
|
+
its main entrypoint, plus two subpath entrypoints:
|
|
14
|
+
|
|
15
|
+
- `@atproto-labs/opentelemetry-node/conventions` re-exports
|
|
16
|
+
`@opentelemetry/semantic-conventions` alongside the atproto XRPC attribute keys.
|
|
17
|
+
- `@atproto-labs/opentelemetry-node/instrumentation` exposes
|
|
18
|
+
`getDefaultAtprotoInstrumentations()`, the instrumentations common to atproto
|
|
19
|
+
services (which `setup()` registers automatically).
|
|
20
|
+
|
|
21
|
+
As part of this move, the shared runtime instrumentation now enables
|
|
22
|
+
`captureUncaughtException`, so the PDS records uncaught exceptions as OTEL runtime
|
|
23
|
+
events when telemetry is enabled.
|
|
24
|
+
|
|
25
|
+
- [#5314](https://github.com/bluesky-social/atproto/pull/5314) [`cded706`](https://github.com/bluesky-social/atproto/commit/cded70638b908c49961faa957892ed76b292c1b5) Thanks [@IdkGoodName](https://github.com/IdkGoodName)! - Fixes `com.atproto.identity.resolveHandle` route throwing `InvalidRequest` instead of `HandleNotFound` error
|
|
26
|
+
|
|
27
|
+
- [#5396](https://github.com/bluesky-social/atproto/pull/5396) [`066e1b4`](https://github.com/bluesky-social/atproto/commit/066e1b4d669eee5dfaa4f7aefb986bbf722c5629) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Add default OTEL resource attributes (name, namespace, version, env name)
|
|
28
|
+
|
|
29
|
+
- [#5350](https://github.com/bluesky-social/atproto/pull/5350) [`1f3f67c`](https://github.com/bluesky-social/atproto/commit/1f3f67c4b5bac19cc226b24f55d90ffd431d0d72) Thanks [@mary-ext](https://github.com/mary-ext)! - Remove the `maxGraphemes` and `maxLength` constraints from the `alt` field of `app.bsky.embed.video`, matching `app.bsky.embed.images`
|
|
30
|
+
|
|
31
|
+
- [#5348](https://github.com/bluesky-social/atproto/pull/5348) [`5c3b7c9`](https://github.com/bluesky-social/atproto/commit/5c3b7c9c87efa4d48d41bc55af61f5b1f1e04920) Thanks [@dreyfus92](https://github.com/dreyfus92)! - Remove `key-encoder` dependency (and its `elliptic`/`asn1.js`/`bn.js` transitive tree) in favor of Node's native JWK key import and the already-present `@noble/curves`
|
|
32
|
+
|
|
33
|
+
- Updated dependencies [[`aff31c8`](https://github.com/bluesky-social/atproto/commit/aff31c815dd69168f3529e667e8bc6e61272bc63), [`5c3b7c9`](https://github.com/bluesky-social/atproto/commit/5c3b7c9c87efa4d48d41bc55af61f5b1f1e04920)]:
|
|
34
|
+
- @atproto-labs/opentelemetry-node@0.1.0
|
|
35
|
+
- @atproto/aws@0.3.14
|
|
36
|
+
- @atproto/xrpc-server@0.12.5
|
|
37
|
+
- @atproto/oauth-provider@0.22.3
|
|
38
|
+
- @atproto-labs/xrpc-utils@0.1.18
|
|
39
|
+
|
|
40
|
+
## 0.5.28
|
|
41
|
+
|
|
42
|
+
### Patch Changes
|
|
43
|
+
|
|
44
|
+
- [#5382](https://github.com/bluesky-social/atproto/pull/5382) [`898c469`](https://github.com/bluesky-social/atproto/commit/898c469cf568b941f38f9c3371997a513ac05cfa) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Update `file-type` dependency
|
|
45
|
+
|
|
46
|
+
- [#5371](https://github.com/bluesky-social/atproto/pull/5371) [`c7f39c5`](https://github.com/bluesky-social/atproto/commit/c7f39c527758ffd5b21b896fac494ecf7e69aa37) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Remove "esbuild" dependency
|
|
47
|
+
|
|
48
|
+
- [#5382](https://github.com/bluesky-social/atproto/pull/5382) [`898c469`](https://github.com/bluesky-social/atproto/commit/898c469cf568b941f38f9c3371997a513ac05cfa) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Propagate back-pressure when uploading blobs (avoiding high memory pressure)
|
|
49
|
+
|
|
50
|
+
- [#5382](https://github.com/bluesky-social/atproto/pull/5382) [`898c469`](https://github.com/bluesky-social/atproto/commit/898c469cf568b941f38f9c3371997a513ac05cfa) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Remove unused `CidNotFound` error class
|
|
51
|
+
|
|
52
|
+
- Updated dependencies [[`898c469`](https://github.com/bluesky-social/atproto/commit/898c469cf568b941f38f9c3371997a513ac05cfa), [`898c469`](https://github.com/bluesky-social/atproto/commit/898c469cf568b941f38f9c3371997a513ac05cfa), [`898c469`](https://github.com/bluesky-social/atproto/commit/898c469cf568b941f38f9c3371997a513ac05cfa), [`898c469`](https://github.com/bluesky-social/atproto/commit/898c469cf568b941f38f9c3371997a513ac05cfa), [`898c469`](https://github.com/bluesky-social/atproto/commit/898c469cf568b941f38f9c3371997a513ac05cfa), [`898c469`](https://github.com/bluesky-social/atproto/commit/898c469cf568b941f38f9c3371997a513ac05cfa), [`898c469`](https://github.com/bluesky-social/atproto/commit/898c469cf568b941f38f9c3371997a513ac05cfa), [`4e18a00`](https://github.com/bluesky-social/atproto/commit/4e18a00b2de97e36b9b00a7fcb8713efd87964d1)]:
|
|
53
|
+
- @atproto/common@0.8.0
|
|
54
|
+
- @atproto/oauth-provider@0.22.2
|
|
55
|
+
- @atproto/aws@0.3.13
|
|
56
|
+
- @atproto/repo@0.10.11
|
|
57
|
+
- @atproto/xrpc-server@0.12.4
|
|
58
|
+
- @atproto-labs/xrpc-utils@0.1.17
|
|
59
|
+
- @atproto/lex@0.3.6
|
|
60
|
+
|
|
3
61
|
## 0.5.27
|
|
4
62
|
|
|
5
63
|
### Patch Changes
|
|
@@ -19,7 +19,7 @@ export declare class BlobTransactor extends BlobReader {
|
|
|
19
19
|
backgroundQueue: BackgroundQueue;
|
|
20
20
|
constructor(db: ActorDb, blobstore: BlobStore, backgroundQueue: BackgroundQueue);
|
|
21
21
|
insertBlobs(recordUri: string, blobs: Iterable<BlobRef>): Promise<void>;
|
|
22
|
-
uploadBlobAndGetMetadata(
|
|
22
|
+
uploadBlobAndGetMetadata(blobStream: stream.Readable, fallbackMime?: `${string}/${string}`, maxSize?: number): Promise<BlobMetadata>;
|
|
23
23
|
trackUntetheredBlob(metadata: BlobMetadata): Promise<TypedBlobRef>;
|
|
24
24
|
processWriteBlobs(rev: string, writes: PreparedWrite[]): Promise<void>;
|
|
25
25
|
updateBlobTakedownStatus(cid: Cid, takedown: com.atproto.admin.defs.StatusAttr): Promise<void>;
|
|
@@ -28,8 +28,4 @@ export declare class BlobTransactor extends BlobReader {
|
|
|
28
28
|
insertBlobMetadata(blob: TypedBlobRef): Promise<void>;
|
|
29
29
|
associateBlob(blob: TypedBlobRef, recordUri: AtUri): Promise<void>;
|
|
30
30
|
}
|
|
31
|
-
export declare class CidNotFound extends Error {
|
|
32
|
-
cid: Cid;
|
|
33
|
-
constructor(cid: Cid);
|
|
34
|
-
}
|
|
35
31
|
//# sourceMappingURL=transactor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transactor.d.ts","sourceRoot":"","sources":["../../../src/actor-store/blob/transactor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"transactor.d.ts","sourceRoot":"","sources":["../../../src/actor-store/blob/transactor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AAerC,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,GAAG,EACR,KAAK,YAAY,EAIlB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAqB,KAAK,SAAS,EAAiB,MAAM,eAAe,CAAA;AAChF,OAAO,EAAE,KAAK,KAAK,EAAyB,MAAM,iBAAiB,CAAA;AAEnE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAC1D,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,yBAAyB,CAAA;AAElD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACxD,OAAO,KAAK,EAAE,OAAO,EAAqB,MAAM,gBAAgB,CAAA;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAExC,MAAM,MAAM,YAAY,GAAG;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,GAAG,CAAA;IACR,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,qBAAa,cAAe,SAAQ,UAAU;IAEnC,EAAE,EAAE,OAAO;IACX,SAAS,EAAE,SAAS;IACpB,eAAe,EAAE,eAAe;IAHzC,YACS,EAAE,EAAE,OAAO,EACX,SAAS,EAAE,SAAS,EACpB,eAAe,EAAE,eAAe,EAGxC;IAEK,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,iBAa5D;IAEK,wBAAwB,CAC5B,UAAU,EAAE,MAAM,CAAC,QAAQ,EAC3B,YAAY,GAAE,GAAG,MAAM,IAAI,MAAM,EAA+B,EAChE,OAAO,SAAW,GACjB,OAAO,CAAC,YAAY,CAAC,CAoCvB;IAEK,mBAAmB,CAAC,QAAQ,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAoCvE;IAEK,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,iBAqC3D;IAEK,wBAAwB,CAC5B,GAAG,EAAE,GAAG,EACR,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,iBA8B5C;IAEK,uBAAuB,CAC3B,MAAM,EAAE,aAAa,EAAE,EACvB,aAAa,CAAC,EAAE,OAAO,iBAuDxB;IAEK,0BAA0B,CAC9B,IAAI,EAAE,YAAY,EAClB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CA+Cf;IAEK,kBAAkB,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAW1D;IAEK,aAAa,CAAC,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CASvE;CACF"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
const { fromStream: fileTypeFromStream } = FileType;
|
|
1
|
+
import assert from 'node:assert';
|
|
2
|
+
import { PassThrough, pipeline, } from 'node:stream';
|
|
3
|
+
import { FileTypeParser, } from 'file-type';
|
|
5
4
|
import PQueue from 'p-queue';
|
|
6
|
-
import {
|
|
5
|
+
import { fromStream } from 'strtok3';
|
|
6
|
+
import { HashPassThrough, MaxSizeChecker, SECOND, Tee } from '@atproto/common';
|
|
7
7
|
import { cidForRawHash, getBlobCidString, parseCid, } from '@atproto/lex-data';
|
|
8
8
|
import { BlobNotFoundError, WriteOpAction } from '@atproto/repo';
|
|
9
9
|
import { currentDatetimeString } from '@atproto/syntax';
|
|
@@ -30,19 +30,35 @@ export class BlobTransactor extends BlobReader {
|
|
|
30
30
|
.execute();
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
|
-
async uploadBlobAndGetMetadata(
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
33
|
+
async uploadBlobAndGetMetadata(blobStream, fallbackMime = 'application/octet-stream', maxSize = Infinity) {
|
|
34
|
+
try {
|
|
35
|
+
const hashDuplex = new HashPassThrough('sha256');
|
|
36
|
+
const sizeDuplex = new MaxSizeChecker(maxSize);
|
|
37
|
+
const typeDuplex = new FileTypePassThrough();
|
|
38
|
+
// @NOTE a pipeline of duplex streams is used to ensure that backpressure
|
|
39
|
+
// is properly propagated to the input stream. using inputStream.pipe()
|
|
40
|
+
// with several destinations does **not** propagate backpressure, and
|
|
41
|
+
// **will** high memory consumption (hash and size duplexes consume the
|
|
42
|
+
// stream faster than the blobstore can process it).
|
|
43
|
+
const streams = [blobStream, hashDuplex, sizeDuplex, typeDuplex];
|
|
44
|
+
const blobStoreStream = pipeline(streams, (_err) => {
|
|
45
|
+
// errors will be propagated to the streams, and handled (rethrown) by
|
|
46
|
+
// the blobStore.
|
|
47
|
+
}); // pipeline() returns the last stream
|
|
48
|
+
// Start the pipeline by reading its output
|
|
49
|
+
const tempKey = await this.blobstore.putTemp(blobStoreStream);
|
|
50
|
+
// Fool-proof against faulty blobstore implementations
|
|
51
|
+
assert(streams.every((s) => s.readableEnded), 'blobstore did not fully consume the stream');
|
|
52
|
+
return {
|
|
53
|
+
tempKey,
|
|
54
|
+
size: sizeDuplex.totalSize,
|
|
55
|
+
cid: cidForRawHash(hashDuplex.digest),
|
|
56
|
+
mimeType: typeDuplex.fileTypeResult?.mime || fallbackMime,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
finally {
|
|
60
|
+
blobStream.destroy();
|
|
61
|
+
}
|
|
46
62
|
}
|
|
47
63
|
async trackUntetheredBlob(metadata) {
|
|
48
64
|
const { tempKey, size, cid, mimeType } = metadata;
|
|
@@ -238,34 +254,47 @@ export class BlobTransactor extends BlobReader {
|
|
|
238
254
|
.execute();
|
|
239
255
|
}
|
|
240
256
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
try {
|
|
250
|
-
for await (const chunk of toHash) {
|
|
251
|
-
hash.write(chunk);
|
|
252
|
-
}
|
|
257
|
+
// "file-type" does not provide a duplex implementation so we create one here
|
|
258
|
+
class FileTypePassThrough extends Tee {
|
|
259
|
+
#fileTypeResult;
|
|
260
|
+
get fileTypeResult() {
|
|
261
|
+
const result = this.#fileTypeResult;
|
|
262
|
+
if (result)
|
|
263
|
+
return result.status === 'fulfilled' ? result.value : undefined;
|
|
264
|
+
throw new Error('FileTypePassThrough result is not yet available');
|
|
253
265
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
266
|
+
constructor(options) {
|
|
267
|
+
const branch = new PassThrough();
|
|
268
|
+
super(branch);
|
|
269
|
+
const parser = new FileTypeParser(options);
|
|
270
|
+
// @NOTE file-type does not support NodeJS Readable and recommends wrapping
|
|
271
|
+
// into a web steam. We use strtok3 to convert the NodeJS Readable into a
|
|
272
|
+
// tokenizer, bypassing that limitation.
|
|
273
|
+
this.fileTypePromise = fromStream(branch)
|
|
274
|
+
.then((tokenizer) => parser.fromTokenizer(tokenizer))
|
|
275
|
+
// file-type won't destroy() the stream. We need to destroy to allow
|
|
276
|
+
// the Tee's main stream to flow freely.
|
|
277
|
+
.finally(() => branch.destroy());
|
|
278
|
+
// avoids unhandled rejections (might be awaited later)
|
|
279
|
+
this.fileTypePromise.then((value) => {
|
|
280
|
+
this.#fileTypeResult = { status: 'fulfilled', value };
|
|
281
|
+
}, (reason) => {
|
|
282
|
+
this.#fileTypeResult = { status: 'rejected', reason };
|
|
283
|
+
});
|
|
257
284
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
return fileType?.mime;
|
|
285
|
+
_final(cb) {
|
|
286
|
+
// propagate the result promise to the final callback, so that the stream is
|
|
287
|
+
// not considered finished until the file type has been determined.
|
|
288
|
+
super._final((err) => {
|
|
289
|
+
this.fileTypePromise.then(() => cb(err), () => cb(err));
|
|
290
|
+
});
|
|
265
291
|
}
|
|
266
|
-
|
|
267
|
-
//
|
|
268
|
-
|
|
292
|
+
_destroy(err, cb) {
|
|
293
|
+
// propagate the result promise to the destroy callback, so that the stream
|
|
294
|
+
// is not considered finished until the file type has been determined.
|
|
295
|
+
super._destroy(err, (err) => {
|
|
296
|
+
this.fileTypePromise.then(() => cb(err), () => cb(err));
|
|
297
|
+
});
|
|
269
298
|
}
|
|
270
299
|
}
|
|
271
300
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transactor.js","sourceRoot":"","sources":["../../../src/actor-store/blob/transactor.ts"],"names":[],"mappings":"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","sourcesContent":["import crypto from 'node:crypto'\nimport type stream from 'node:stream'\nimport * as FileTypeModule from 'file-type'\nconst FileType = ((m) => m.default ?? m)(FileTypeModule)\nconst { fromStream: fileTypeFromStream } = FileType\nimport PQueue from 'p-queue'\nimport { SECOND, cloneStream, streamSize } from '@atproto/common'\nimport {\n type BlobRef,\n type Cid,\n type TypedBlobRef,\n cidForRawHash,\n getBlobCidString,\n parseCid,\n} from '@atproto/lex-data'\nimport { BlobNotFoundError, type BlobStore, WriteOpAction } from '@atproto/repo'\nimport { type AtUri, currentDatetimeString } from '@atproto/syntax'\nimport { InvalidRequestError } from '@atproto/xrpc-server'\nimport type { BackgroundQueue } from '../../background.js'\nimport type { com } from '../../lexicons/index.js'\nimport { blobStoreLogger as log } from '../../logger.js'\nimport type { PreparedWrite } from '../../repo/types.js'\nimport type { ActorDb, Blob as BlobTable } from '../db/index.js'\nimport { BlobReader } from './reader.js'\n\nexport type BlobMetadata = {\n tempKey: string\n size: number\n cid: Cid\n mimeType: string\n}\n\nexport class BlobTransactor extends BlobReader {\n constructor(\n public db: ActorDb,\n public blobstore: BlobStore,\n public backgroundQueue: BackgroundQueue,\n ) {\n super(db, blobstore)\n }\n\n async insertBlobs(recordUri: string, blobs: Iterable<BlobRef>) {\n const values = Array.from(blobs, (blob) => ({\n recordUri,\n blobCid: getBlobCidString(blob),\n }))\n\n if (values.length) {\n await this.db.db\n .insertInto('record_blob')\n .values(values)\n .onConflict((oc) => oc.doNothing())\n .execute()\n }\n }\n\n async uploadBlobAndGetMetadata(\n userSuggestedMime: string,\n blobStream: stream.Readable,\n ): Promise<BlobMetadata> {\n const [tempKey, size, sha256, sniffedMime] = await Promise.all([\n this.blobstore.putTemp(cloneStream(blobStream)),\n streamSize(cloneStream(blobStream)),\n sha256Stream(cloneStream(blobStream)),\n mimeTypeFromStream(cloneStream(blobStream)),\n ])\n\n return {\n tempKey,\n size,\n cid: cidForRawHash(sha256),\n mimeType: sniffedMime || userSuggestedMime,\n }\n }\n\n async trackUntetheredBlob(metadata: BlobMetadata): Promise<TypedBlobRef> {\n const { tempKey, size, cid, mimeType } = metadata\n const cidStr = cid.toString()\n\n const found = await this.db.db\n .selectFrom('blob')\n .selectAll()\n .where('cid', '=', cidStr)\n .executeTakeFirst()\n if (found?.takedownRef) {\n throw new InvalidRequestError('Blob has been takendown, cannot re-upload')\n }\n\n await this.db.db\n .insertInto('blob')\n .values({\n cid: cidStr,\n mimeType,\n size,\n tempKey,\n createdAt: currentDatetimeString(),\n })\n .onConflict((oc) =>\n oc\n .column('cid')\n .doUpdateSet({ tempKey })\n .where('blob.tempKey', 'is not', null),\n )\n .execute()\n\n return {\n $type: 'blob',\n ref: cid,\n mimeType,\n size,\n }\n }\n\n async processWriteBlobs(rev: string, writes: PreparedWrite[]) {\n await this.deleteDereferencedBlobs(writes)\n\n const ac = new AbortController()\n\n // Limit the number of parallel requests made to the BlobStore by using a\n // a queue with concurrency management.\n type Task = () => Promise<void>\n const tasks: Task[] = []\n\n for (const write of writes) {\n if (isCreate(write) || isUpdate(write)) {\n for (const blob of write.blobs) {\n tasks.push(async () => {\n if (ac.signal.aborted) return\n await this.associateBlob(blob, write.uri)\n await this.verifyBlobAndMakePermanent(blob, ac.signal)\n })\n }\n }\n }\n\n try {\n const queue = new PQueue({\n concurrency: 20,\n // The blob store should already limit the time of every operation. We\n // add a timeout here as an extra precaution.\n timeout: 60 * SECOND,\n throwOnTimeout: true,\n })\n\n // Will reject as soon as any task fails, causing the \"finally\" block\n // below to run, aborting every other pending tasks.\n await queue.addAll(tasks)\n } finally {\n ac.abort()\n }\n }\n\n async updateBlobTakedownStatus(\n cid: Cid,\n takedown: com.atproto.admin.defs.StatusAttr,\n ) {\n const takedownRef = takedown.applied\n ? (takedown.ref ?? currentDatetimeString())\n : null\n await this.db.db\n .updateTable('blob')\n .set({ takedownRef })\n .where('cid', '=', cid.toString())\n .executeTakeFirst()\n\n try {\n // @NOTE find a way to not perform i/o operations during the transaction\n // (typically by using a state in the \"blob\" table, and another process to\n // handle the actual i/o)\n if (takedown.applied) {\n await this.blobstore.quarantine(cid)\n } else {\n await this.blobstore.unquarantine(cid)\n }\n } catch (err) {\n if (!(err instanceof BlobNotFoundError)) {\n log.error(\n { err, cid: cid.toString() },\n 'could not update blob takedown status',\n )\n\n throw err\n }\n }\n }\n\n async deleteDereferencedBlobs(\n writes: PreparedWrite[],\n skipBlobStore?: boolean,\n ) {\n const deletes = writes.filter(isDelete)\n const updates = writes.filter(isUpdate)\n const uris = [...deletes, ...updates].map((w) => w.uri.toString())\n if (uris.length === 0) return\n\n const deletedRepoBlobs = await this.db.db\n .deleteFrom('record_blob')\n .where('recordUri', 'in', uris)\n .returning('blobCid')\n .execute()\n if (deletedRepoBlobs.length === 0) return\n\n const deletedRepoBlobCids = deletedRepoBlobs.map((row) => row.blobCid)\n const duplicateCids = await this.db.db\n .selectFrom('record_blob')\n .where('blobCid', 'in', deletedRepoBlobCids)\n .select('blobCid')\n .execute()\n\n const newBlobCids = writes\n .filter((w) => isUpdate(w) || isCreate(w))\n .flatMap((w) => w.blobs.map((b) => b.ref.toString()))\n\n const cidsToKeep = [\n ...newBlobCids,\n ...duplicateCids.map((row) => row.blobCid),\n ]\n\n const cidsToDelete = deletedRepoBlobCids.filter(\n (cid) => !cidsToKeep.includes(cid),\n )\n if (cidsToDelete.length === 0) return\n\n await this.db.db\n .deleteFrom('blob')\n .where('cid', 'in', cidsToDelete)\n .execute()\n\n if (!skipBlobStore) {\n this.db.onCommit(() => {\n this.backgroundQueue.add(async () => {\n try {\n const cids = cidsToDelete.map((cid) => parseCid(cid))\n await this.blobstore.deleteMany(cids)\n } catch (err) {\n log.error(\n { err, cids: cidsToDelete },\n 'could not delete blobs from blobstore',\n )\n }\n })\n })\n }\n }\n\n async verifyBlobAndMakePermanent(\n blob: TypedBlobRef,\n signal?: AbortSignal,\n ): Promise<void> {\n const found = await this.db.db\n .selectFrom('blob')\n .select(['tempKey', 'size', 'mimeType'])\n .where('cid', '=', blob.ref.toString())\n .where('takedownRef', 'is', null)\n .executeTakeFirst()\n\n signal?.throwIfAborted()\n\n if (!found) {\n throw new InvalidRequestError(\n `Could not find blob: ${blob.ref.toString()}`,\n 'BlobNotFound',\n )\n }\n\n if (found.tempKey) {\n verifyBlob(blob, found)\n\n // @NOTE it is less than ideal to perform async (i/o) operations during a\n // transaction. Especially since there have been instances of the actor-db\n // being locked, requiring to kick the processes.\n\n // The better solution would be to update the blob state in the database\n // (e.g. \"makeItPermanent\") and to process those updates outside of the\n // transaction.\n\n await this.blobstore\n .makePermanent(found.tempKey, blob.ref)\n .catch((err) => {\n log.error(\n { err, cid: blob.ref.toString() },\n 'could not make blob permanent',\n )\n\n throw err\n })\n\n signal?.throwIfAborted()\n\n await this.db.db\n .updateTable('blob')\n .set({ tempKey: null })\n .where('tempKey', '=', found.tempKey)\n .execute()\n }\n }\n\n async insertBlobMetadata(blob: TypedBlobRef): Promise<void> {\n await this.db.db\n .insertInto('blob')\n .values({\n cid: blob.ref.toString(),\n mimeType: blob.mimeType,\n size: blob.size,\n createdAt: currentDatetimeString(),\n })\n .onConflict((oc) => oc.doNothing())\n .execute()\n }\n\n async associateBlob(blob: TypedBlobRef, recordUri: AtUri): Promise<void> {\n await this.db.db\n .insertInto('record_blob')\n .values({\n blobCid: blob.ref.toString(),\n recordUri: recordUri.toString(),\n })\n .onConflict((oc) => oc.doNothing())\n .execute()\n }\n}\n\nexport class CidNotFound extends Error {\n cid: Cid\n constructor(cid: Cid) {\n super(`cid not found: ${cid.toString()}`)\n this.cid = cid\n }\n}\n\nasync function sha256Stream(toHash: stream.Readable): Promise<Uint8Array> {\n const hash = crypto.createHash('sha256')\n try {\n for await (const chunk of toHash) {\n hash.write(chunk)\n }\n } catch (err) {\n hash.end()\n throw err\n }\n hash.end()\n return hash.read()\n}\n\nasync function mimeTypeFromStream(\n blobStream: stream.Readable,\n): Promise<string | undefined> {\n try {\n const fileType = await fileTypeFromStream(blobStream)\n return fileType?.mime\n } finally {\n // @NOTE Draining avoids a Node pipe cleanup bug that can pause other clones.\n blobStream.resume()\n }\n}\n\n/**\n * Ensures that the blob referenced in the record matches the stored blob.\n */\nfunction verifyBlob(\n blob: TypedBlobRef,\n found: Pick<BlobTable, 'size' | 'mimeType'>,\n) {\n if (blob.mimeType !== found.mimeType) {\n throw new InvalidRequestError(\n `Referenced Mimetype does not match stored blob. Expected: ${found.mimeType}, Got: ${blob.mimeType}`,\n 'InvalidMimeType',\n )\n }\n\n if (blob.size !== found.size) {\n throw new InvalidRequestError(\n `Referenced Size does not match stored blob. Expected: ${found.size}, Got: ${blob.size}`,\n 'InvalidSize',\n )\n }\n}\n\nfunction isCreate(write: PreparedWrite) {\n return write.action === WriteOpAction.Create\n}\nfunction isUpdate(write: PreparedWrite) {\n return write.action === WriteOpAction.Update\n}\nfunction isDelete(write: PreparedWrite) {\n return write.action === WriteOpAction.Delete\n}\n"]}
|
|
1
|
+
{"version":3,"file":"transactor.js","sourceRoot":"","sources":["../../../src/actor-store/blob/transactor.ts"],"names":[],"mappings":"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","sourcesContent":["import assert from 'node:assert'\nimport type stream from 'node:stream'\nimport {\n type Duplex,\n PassThrough,\n type TransformCallback,\n pipeline,\n} from 'node:stream'\nimport {\n type FileTypeOptions,\n FileTypeParser,\n type FileTypeResult,\n} from 'file-type'\nimport PQueue from 'p-queue'\nimport { fromStream } from 'strtok3'\nimport { HashPassThrough, MaxSizeChecker, SECOND, Tee } from '@atproto/common'\nimport {\n type BlobRef,\n type Cid,\n type TypedBlobRef,\n cidForRawHash,\n getBlobCidString,\n parseCid,\n} from '@atproto/lex-data'\nimport { BlobNotFoundError, type BlobStore, WriteOpAction } from '@atproto/repo'\nimport { type AtUri, currentDatetimeString } from '@atproto/syntax'\nimport { InvalidRequestError } from '@atproto/xrpc-server'\nimport type { BackgroundQueue } from '../../background.js'\nimport type { com } from '../../lexicons/index.js'\nimport { blobStoreLogger as log } from '../../logger.js'\nimport type { PreparedWrite } from '../../repo/types.js'\nimport type { ActorDb, Blob as BlobTable } from '../db/index.js'\nimport { BlobReader } from './reader.js'\n\nexport type BlobMetadata = {\n tempKey: string\n size: number\n cid: Cid\n mimeType: string\n}\n\nexport class BlobTransactor extends BlobReader {\n constructor(\n public db: ActorDb,\n public blobstore: BlobStore,\n public backgroundQueue: BackgroundQueue,\n ) {\n super(db, blobstore)\n }\n\n async insertBlobs(recordUri: string, blobs: Iterable<BlobRef>) {\n const values = Array.from(blobs, (blob) => ({\n recordUri,\n blobCid: getBlobCidString(blob),\n }))\n\n if (values.length) {\n await this.db.db\n .insertInto('record_blob')\n .values(values)\n .onConflict((oc) => oc.doNothing())\n .execute()\n }\n }\n\n async uploadBlobAndGetMetadata(\n blobStream: stream.Readable,\n fallbackMime: `${string}/${string}` = 'application/octet-stream',\n maxSize = Infinity,\n ): Promise<BlobMetadata> {\n try {\n const hashDuplex = new HashPassThrough('sha256')\n const sizeDuplex = new MaxSizeChecker(maxSize)\n const typeDuplex = new FileTypePassThrough()\n\n // @NOTE a pipeline of duplex streams is used to ensure that backpressure\n // is properly propagated to the input stream. using inputStream.pipe()\n // with several destinations does **not** propagate backpressure, and\n // **will** high memory consumption (hash and size duplexes consume the\n // stream faster than the blobstore can process it).\n const streams = [blobStream, hashDuplex, sizeDuplex, typeDuplex]\n\n const blobStoreStream = pipeline(streams, (_err) => {\n // errors will be propagated to the streams, and handled (rethrown) by\n // the blobStore.\n }) as Duplex // pipeline() returns the last stream\n\n // Start the pipeline by reading its output\n const tempKey = await this.blobstore.putTemp(blobStoreStream)\n\n // Fool-proof against faulty blobstore implementations\n assert(\n streams.every((s) => s.readableEnded),\n 'blobstore did not fully consume the stream',\n )\n\n return {\n tempKey,\n size: sizeDuplex.totalSize,\n cid: cidForRawHash(hashDuplex.digest),\n mimeType: typeDuplex.fileTypeResult?.mime || fallbackMime,\n }\n } finally {\n blobStream.destroy()\n }\n }\n\n async trackUntetheredBlob(metadata: BlobMetadata): Promise<TypedBlobRef> {\n const { tempKey, size, cid, mimeType } = metadata\n const cidStr = cid.toString()\n\n const found = await this.db.db\n .selectFrom('blob')\n .selectAll()\n .where('cid', '=', cidStr)\n .executeTakeFirst()\n if (found?.takedownRef) {\n throw new InvalidRequestError('Blob has been takendown, cannot re-upload')\n }\n\n await this.db.db\n .insertInto('blob')\n .values({\n cid: cidStr,\n mimeType,\n size,\n tempKey,\n createdAt: currentDatetimeString(),\n })\n .onConflict((oc) =>\n oc\n .column('cid')\n .doUpdateSet({ tempKey })\n .where('blob.tempKey', 'is not', null),\n )\n .execute()\n\n return {\n $type: 'blob',\n ref: cid,\n mimeType,\n size,\n }\n }\n\n async processWriteBlobs(rev: string, writes: PreparedWrite[]) {\n await this.deleteDereferencedBlobs(writes)\n\n const ac = new AbortController()\n\n // Limit the number of parallel requests made to the BlobStore by using a\n // a queue with concurrency management.\n type Task = () => Promise<void>\n const tasks: Task[] = []\n\n for (const write of writes) {\n if (isCreate(write) || isUpdate(write)) {\n for (const blob of write.blobs) {\n tasks.push(async () => {\n if (ac.signal.aborted) return\n await this.associateBlob(blob, write.uri)\n await this.verifyBlobAndMakePermanent(blob, ac.signal)\n })\n }\n }\n }\n\n try {\n const queue = new PQueue({\n concurrency: 20,\n // The blob store should already limit the time of every operation. We\n // add a timeout here as an extra precaution.\n timeout: 60 * SECOND,\n throwOnTimeout: true,\n })\n\n // Will reject as soon as any task fails, causing the \"finally\" block\n // below to run, aborting every other pending tasks.\n await queue.addAll(tasks)\n } finally {\n ac.abort()\n }\n }\n\n async updateBlobTakedownStatus(\n cid: Cid,\n takedown: com.atproto.admin.defs.StatusAttr,\n ) {\n const takedownRef = takedown.applied\n ? (takedown.ref ?? currentDatetimeString())\n : null\n await this.db.db\n .updateTable('blob')\n .set({ takedownRef })\n .where('cid', '=', cid.toString())\n .executeTakeFirst()\n\n try {\n // @NOTE find a way to not perform i/o operations during the transaction\n // (typically by using a state in the \"blob\" table, and another process to\n // handle the actual i/o)\n if (takedown.applied) {\n await this.blobstore.quarantine(cid)\n } else {\n await this.blobstore.unquarantine(cid)\n }\n } catch (err) {\n if (!(err instanceof BlobNotFoundError)) {\n log.error(\n { err, cid: cid.toString() },\n 'could not update blob takedown status',\n )\n\n throw err\n }\n }\n }\n\n async deleteDereferencedBlobs(\n writes: PreparedWrite[],\n skipBlobStore?: boolean,\n ) {\n const deletes = writes.filter(isDelete)\n const updates = writes.filter(isUpdate)\n const uris = [...deletes, ...updates].map((w) => w.uri.toString())\n if (uris.length === 0) return\n\n const deletedRepoBlobs = await this.db.db\n .deleteFrom('record_blob')\n .where('recordUri', 'in', uris)\n .returning('blobCid')\n .execute()\n if (deletedRepoBlobs.length === 0) return\n\n const deletedRepoBlobCids = deletedRepoBlobs.map((row) => row.blobCid)\n const duplicateCids = await this.db.db\n .selectFrom('record_blob')\n .where('blobCid', 'in', deletedRepoBlobCids)\n .select('blobCid')\n .execute()\n\n const newBlobCids = writes\n .filter((w) => isUpdate(w) || isCreate(w))\n .flatMap((w) => w.blobs.map((b) => b.ref.toString()))\n\n const cidsToKeep = [\n ...newBlobCids,\n ...duplicateCids.map((row) => row.blobCid),\n ]\n\n const cidsToDelete = deletedRepoBlobCids.filter(\n (cid) => !cidsToKeep.includes(cid),\n )\n if (cidsToDelete.length === 0) return\n\n await this.db.db\n .deleteFrom('blob')\n .where('cid', 'in', cidsToDelete)\n .execute()\n\n if (!skipBlobStore) {\n this.db.onCommit(() => {\n this.backgroundQueue.add(async () => {\n try {\n const cids = cidsToDelete.map((cid) => parseCid(cid))\n await this.blobstore.deleteMany(cids)\n } catch (err) {\n log.error(\n { err, cids: cidsToDelete },\n 'could not delete blobs from blobstore',\n )\n }\n })\n })\n }\n }\n\n async verifyBlobAndMakePermanent(\n blob: TypedBlobRef,\n signal?: AbortSignal,\n ): Promise<void> {\n const found = await this.db.db\n .selectFrom('blob')\n .select(['tempKey', 'size', 'mimeType'])\n .where('cid', '=', blob.ref.toString())\n .where('takedownRef', 'is', null)\n .executeTakeFirst()\n\n signal?.throwIfAborted()\n\n if (!found) {\n throw new InvalidRequestError(\n `Could not find blob: ${blob.ref.toString()}`,\n 'BlobNotFound',\n )\n }\n\n if (found.tempKey) {\n verifyBlob(blob, found)\n\n // @NOTE it is less than ideal to perform async (i/o) operations during a\n // transaction. Especially since there have been instances of the actor-db\n // being locked, requiring to kick the processes.\n\n // The better solution would be to update the blob state in the database\n // (e.g. \"makeItPermanent\") and to process those updates outside of the\n // transaction.\n\n await this.blobstore\n .makePermanent(found.tempKey, blob.ref)\n .catch((err) => {\n log.error(\n { err, cid: blob.ref.toString() },\n 'could not make blob permanent',\n )\n\n throw err\n })\n\n signal?.throwIfAborted()\n\n await this.db.db\n .updateTable('blob')\n .set({ tempKey: null })\n .where('tempKey', '=', found.tempKey)\n .execute()\n }\n }\n\n async insertBlobMetadata(blob: TypedBlobRef): Promise<void> {\n await this.db.db\n .insertInto('blob')\n .values({\n cid: blob.ref.toString(),\n mimeType: blob.mimeType,\n size: blob.size,\n createdAt: currentDatetimeString(),\n })\n .onConflict((oc) => oc.doNothing())\n .execute()\n }\n\n async associateBlob(blob: TypedBlobRef, recordUri: AtUri): Promise<void> {\n await this.db.db\n .insertInto('record_blob')\n .values({\n blobCid: blob.ref.toString(),\n recordUri: recordUri.toString(),\n })\n .onConflict((oc) => oc.doNothing())\n .execute()\n }\n}\n\n// \"file-type\" does not provide a duplex implementation so we create one here\nclass FileTypePassThrough extends Tee {\n readonly fileTypePromise: Promise<FileTypeResult | undefined>\n\n #fileTypeResult?: PromiseSettledResult<FileTypeResult | undefined>\n get fileTypeResult(): FileTypeResult | undefined {\n const result = this.#fileTypeResult\n if (result) return result.status === 'fulfilled' ? result.value : undefined\n throw new Error('FileTypePassThrough result is not yet available')\n }\n\n constructor(options?: FileTypeOptions) {\n const branch = new PassThrough()\n super(branch)\n\n const parser = new FileTypeParser(options)\n\n // @NOTE file-type does not support NodeJS Readable and recommends wrapping\n // into a web steam. We use strtok3 to convert the NodeJS Readable into a\n // tokenizer, bypassing that limitation.\n this.fileTypePromise = fromStream(branch)\n .then((tokenizer) => parser.fromTokenizer(tokenizer))\n // file-type won't destroy() the stream. We need to destroy to allow\n // the Tee's main stream to flow freely.\n .finally(() => branch.destroy())\n\n // avoids unhandled rejections (might be awaited later)\n this.fileTypePromise.then(\n (value) => {\n this.#fileTypeResult = { status: 'fulfilled', value }\n },\n (reason) => {\n this.#fileTypeResult = { status: 'rejected', reason }\n },\n )\n }\n\n _final(cb: TransformCallback) {\n // propagate the result promise to the final callback, so that the stream is\n // not considered finished until the file type has been determined.\n super._final((err) => {\n this.fileTypePromise.then(\n () => cb(err),\n () => cb(err),\n )\n })\n }\n\n _destroy(err: Error | null, cb: (err?: Error | null) => void) {\n // propagate the result promise to the destroy callback, so that the stream\n // is not considered finished until the file type has been determined.\n super._destroy(err, (err) => {\n this.fileTypePromise.then(\n () => cb(err),\n () => cb(err),\n )\n })\n }\n}\n\n/**\n * Ensures that the blob referenced in the record matches the stored blob.\n */\nfunction verifyBlob(\n blob: TypedBlobRef,\n found: Pick<BlobTable, 'size' | 'mimeType'>,\n) {\n if (blob.mimeType !== found.mimeType) {\n throw new InvalidRequestError(\n `Referenced Mimetype does not match stored blob. Expected: ${found.mimeType}, Got: ${blob.mimeType}`,\n 'InvalidMimeType',\n )\n }\n\n if (blob.size !== found.size) {\n throw new InvalidRequestError(\n `Referenced Size does not match stored blob. Expected: ${found.size}, Got: ${blob.size}`,\n 'InvalidSize',\n )\n }\n}\n\nfunction isCreate(write: PreparedWrite) {\n return write.action === WriteOpAction.Create\n}\nfunction isUpdate(write: PreparedWrite) {\n return write.action === WriteOpAction.Update\n}\nfunction isDelete(write: PreparedWrite) {\n return write.action === WriteOpAction.Delete\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolveHandle.d.ts","sourceRoot":"","sources":["../../../../../src/api/com/atproto/identity/resolveHandle.ts"],"names":[],"mappings":"AAEA,OAAO,EAAuB,KAAK,MAAM,EAAE,MAAM,sBAAsB,CAAA;AACvE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAIxD,MAAM,CAAC,OAAO,WAAW,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"resolveHandle.d.ts","sourceRoot":"","sources":["../../../../../src/api/com/atproto/identity/resolveHandle.ts"],"names":[],"mappings":"AAEA,OAAO,EAAuB,KAAK,MAAM,EAAE,MAAM,sBAAsB,CAAA;AACvE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAIxD,MAAM,CAAC,OAAO,WAAW,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,QA2CvD"}
|
|
@@ -18,7 +18,7 @@ export default function (server, ctx) {
|
|
|
18
18
|
const supportedHandle = ctx.cfg.identity.serviceHandleDomains.some((host) => handle.endsWith(host) || handle === host.slice(1));
|
|
19
19
|
// this should be in our DB & we couldn't find it, so fail
|
|
20
20
|
if (supportedHandle) {
|
|
21
|
-
throw new InvalidRequestError('Unable to resolve handle');
|
|
21
|
+
throw new InvalidRequestError('Unable to resolve handle', 'HandleNotFound');
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
const did = ctx.bskyAppView
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolveHandle.js","sourceRoot":"","sources":["../../../../../src/api/com/atproto/identity/resolveHandle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAE1C,OAAO,EAAE,mBAAmB,EAAe,MAAM,sBAAsB,CAAA;AAEvE,OAAO,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAA;AACtE,OAAO,EAAE,GAAG,EAAE,MAAM,+BAA+B,CAAA;AAEnD,MAAM,CAAC,OAAO,WAAW,MAAc,EAAE,GAAe;IACtD,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,aAAa,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;QAClE,MAAM,MAAM,GAAG,wBAAwB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;QAEtD,qEAAqE;QACrE,uCAAuC;QACvC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;YACtB,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,cAAc,CAAC,UAAU,CAAC,MAAM,CAAC,CAAA;YACxD,IAAI,IAAI,EAAE,CAAC;gBACT,OAAO;oBACL,QAAQ,EAAE,kBAA2B;oBACrC,IAAI,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,GAAgB,EAAE;iBACrC,CAAA;YACH,CAAC;YAED,MAAM,eAAe,GAAG,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,oBAAoB,CAAC,IAAI,CAChE,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,MAAM,KAAK,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAC5D,CAAA;YACD,0DAA0D;YAC1D,IAAI,eAAe,EAAE,CAAC;gBACpB,MAAM,IAAI,mBAAmB,
|
|
1
|
+
{"version":3,"file":"resolveHandle.js","sourceRoot":"","sources":["../../../../../src/api/com/atproto/identity/resolveHandle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAE1C,OAAO,EAAE,mBAAmB,EAAe,MAAM,sBAAsB,CAAA;AAEvE,OAAO,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAA;AACtE,OAAO,EAAE,GAAG,EAAE,MAAM,+BAA+B,CAAA;AAEnD,MAAM,CAAC,OAAO,WAAW,MAAc,EAAE,GAAe;IACtD,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,aAAa,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;QAClE,MAAM,MAAM,GAAG,wBAAwB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;QAEtD,qEAAqE;QACrE,uCAAuC;QACvC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;YACtB,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,cAAc,CAAC,UAAU,CAAC,MAAM,CAAC,CAAA;YACxD,IAAI,IAAI,EAAE,CAAC;gBACT,OAAO;oBACL,QAAQ,EAAE,kBAA2B;oBACrC,IAAI,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,GAAgB,EAAE;iBACrC,CAAA;YACH,CAAC;YAED,MAAM,eAAe,GAAG,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,oBAAoB,CAAC,IAAI,CAChE,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,MAAM,KAAK,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAC5D,CAAA;YACD,0DAA0D;YAC1D,IAAI,eAAe,EAAE,CAAC;gBACpB,MAAM,IAAI,mBAAmB,CAC3B,0BAA0B,EAC1B,gBAAgB,CACjB,CAAA;YACH,CAAC;QACH,CAAC;QAED,MAAM,GAAG,GAAc,GAAG,CAAC,WAAW;YACpC,CAAC,CAAC,MAAM,GAAG,CAAC,WAAW,CAAC,MAAM;iBACzB,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,aAAa,EAAE,EAAE,MAAM,EAAE,CAAC;iBACpD,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,wBAAwB,CAAC;YACjD,CAAC,CAAC,MAAM,GAAG,CAAC,UAAU,CAAC,MAAM;iBACxB,OAAO,CAAC,MAAM,CAAC;iBACf,IAAI,CACH,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,wBAAwB,EAAE,CAAC,EAC7D,wBAAwB,CACzB,CAAA;QAEP,OAAO;YACL,QAAQ,EAAE,kBAA2B;YACrC,IAAI,EAAE,EAAE,GAAG,EAAE;SACd,CAAA;IACH,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,SAAS,wBAAwB,CAAC,KAAe;IAC/C,MAAM,IAAI,mBAAmB,CAAC,0BAA0B,EAAE,SAAS,EAAE;QACnE,KAAK;KACN,CAAC,CAAA;AACJ,CAAC","sourcesContent":["import { isDidString } from '@atproto/lex'\nimport type { DidString } from '@atproto/syntax'\nimport { InvalidRequestError, type Server } from '@atproto/xrpc-server'\nimport type { AppContext } from '../../../../context.js'\nimport { baseNormalizeAndValidate } from '../../../../handle/index.js'\nimport { com } from '../../../../lexicons/index.js'\n\nexport default function (server: Server, ctx: AppContext) {\n server.add(com.atproto.identity.resolveHandle, async ({ params }) => {\n const handle = baseNormalizeAndValidate(params.handle)\n\n // when behind an entryway, the entryway is authoritative for handles\n // rather than this pds' local accounts\n if (!ctx.cfg.entryway) {\n const user = await ctx.accountManager.getAccount(handle)\n if (user) {\n return {\n encoding: 'application/json' as const,\n body: { did: user.did as DidString },\n }\n }\n\n const supportedHandle = ctx.cfg.identity.serviceHandleDomains.some(\n (host) => handle.endsWith(host) || handle === host.slice(1),\n )\n // this should be in our DB & we couldn't find it, so fail\n if (supportedHandle) {\n throw new InvalidRequestError(\n 'Unable to resolve handle',\n 'HandleNotFound',\n )\n }\n }\n\n const did: DidString = ctx.bskyAppView\n ? await ctx.bskyAppView.client\n .call(com.atproto.identity.resolveHandle, { handle })\n .then((r) => r.did, throwInvalidRequestError)\n : await ctx.idResolver.handle\n .resolve(handle)\n .then(\n (v) => (v && isDidString(v) ? v : throwInvalidRequestError()),\n throwInvalidRequestError,\n )\n\n return {\n encoding: 'application/json' as const,\n body: { did },\n }\n })\n}\n\nfunction throwInvalidRequestError(cause?: unknown): never {\n throw new InvalidRequestError('Unable to resolve handle', undefined, {\n cause,\n })\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uploadBlob.d.ts","sourceRoot":"","sources":["../../../../../src/api/com/atproto/repo/uploadBlob.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,MAAM,EAGZ,MAAM,sBAAsB,CAAA;AAC7B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAGxD,MAAM,CAAC,OAAO,WAAW,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"uploadBlob.d.ts","sourceRoot":"","sources":["../../../../../src/api/com/atproto/repo/uploadBlob.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,MAAM,EAGZ,MAAM,sBAAsB,CAAA;AAC7B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAGxD,MAAM,CAAC,OAAO,WAAW,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,QA+CvD"}
|
|
@@ -18,7 +18,7 @@ export default function (server, ctx) {
|
|
|
18
18
|
const requester = auth.credentials.did;
|
|
19
19
|
const blob = await ctx.actorStore.writeNoTransaction(requester, async (store) => {
|
|
20
20
|
const metadata = await store.repo.blob
|
|
21
|
-
.uploadBlobAndGetMetadata(input.
|
|
21
|
+
.uploadBlobAndGetMetadata(input.body, input.encoding, ctx.cfg.service.blobUploadLimit)
|
|
22
22
|
.catch(throwAbortAsUpstreamError);
|
|
23
23
|
return store.transact(async (actorTxn) => {
|
|
24
24
|
const blobRef = await actorTxn.repo.blob.trackUntetheredBlob(metadata);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uploadBlob.js","sourceRoot":"","sources":["../../../../../src/api/com/atproto/repo/uploadBlob.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAA;AACrC,OAAO,EAEL,oBAAoB,EACpB,gBAAgB,GACjB,MAAM,sBAAsB,CAAA;AAE7B,OAAO,EAAE,GAAG,EAAE,MAAM,+BAA+B,CAAA;AAEnD,MAAM,CAAC,OAAO,WAAW,MAAc,EAAE,GAAe;IACtD,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE;QACtC,IAAI,EAAE,GAAG,CAAC,YAAY,CAAC,8BAA8B,CAAC;YACpD,aAAa,EAAE,IAAI;YACnB,SAAS,EAAE,CAAC,WAAW,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;gBAClC,MAAM,QAAQ,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAA;gBACtC,WAAW,CAAC,UAAU,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAA;YAC5C,CAAC;SACF,CAAC;QACF,SAAS,EAAE;YACT,UAAU,EAAE,GAAG;YACf,MAAM,EAAE,IAAI;SACb;QACD,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE;YACjC,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAA;YAEtC,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,UAAU,CAAC,kBAAkB,CAClD,SAAS,EACT,KAAK,EAAE,KAAK,EAAE,EAAE;gBACd,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI;qBACnC,wBAAwB,CAAC,KAAK,CAAC,QAAQ,
|
|
1
|
+
{"version":3,"file":"uploadBlob.js","sourceRoot":"","sources":["../../../../../src/api/com/atproto/repo/uploadBlob.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAA;AACrC,OAAO,EAEL,oBAAoB,EACpB,gBAAgB,GACjB,MAAM,sBAAsB,CAAA;AAE7B,OAAO,EAAE,GAAG,EAAE,MAAM,+BAA+B,CAAA;AAEnD,MAAM,CAAC,OAAO,WAAW,MAAc,EAAE,GAAe;IACtD,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE;QACtC,IAAI,EAAE,GAAG,CAAC,YAAY,CAAC,8BAA8B,CAAC;YACpD,aAAa,EAAE,IAAI;YACnB,SAAS,EAAE,CAAC,WAAW,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;gBAClC,MAAM,QAAQ,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAA;gBACtC,WAAW,CAAC,UAAU,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAA;YAC5C,CAAC;SACF,CAAC;QACF,SAAS,EAAE;YACT,UAAU,EAAE,GAAG;YACf,MAAM,EAAE,IAAI;SACb;QACD,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE;YACjC,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAA;YAEtC,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,UAAU,CAAC,kBAAkB,CAClD,SAAS,EACT,KAAK,EAAE,KAAK,EAAE,EAAE;gBACd,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI;qBACnC,wBAAwB,CACvB,KAAK,CAAC,IAAI,EACV,KAAK,CAAC,QAAQ,EACd,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,eAAe,CAChC;qBACA,KAAK,CAAC,yBAAyB,CAAC,CAAA;gBAEnC,OAAO,KAAK,CAAC,QAAQ,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE;oBACvC,MAAM,OAAO,GACX,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAA;oBAExD,qEAAqE;oBACrE,IAAI,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;wBAC5D,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,0BAA0B,CAAC,OAAO,CAAC,CAAA;oBAC9D,CAAC;oBAED,OAAO,OAAO,CAAA;gBAChB,CAAC,CAAC,CAAA;YACJ,CAAC,CACF,CAAA;YAED,OAAO;gBACL,QAAQ,EAAE,kBAA2B;gBACrC,IAAI,EAAE,EAAE,IAAI,EAAE;aACf,CAAA;QACH,CAAC;KACF,CAAC,CAAA;AACJ,CAAC;AAED,SAAS,yBAAyB,CAAC,GAAY;IAC7C,sEAAsE;IACtE,wEAAwE;IACxE,sEAAsE;IACtE,qDAAqD;IACrD,KAAK,IAAI,CAAC,GAAG,GAAG,EAAE,KAAK,GAAG,CAAC,EAAE,CAAC,YAAY,KAAK,IAAI,KAAK,GAAG,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC;QACvE,IAAI,CAAC,CAAC,IAAI,KAAK,YAAY,IAAI,CAAC,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;YACzD,MAAM,IAAI,oBAAoB,CAAC,wCAAwC,CAAC,CAAA;QAC1E,CAAC;QACD,CAAC,GAAG,CAAC,CAAC,KAAK,CAAA;IACb,CAAC;IACD,MAAM,GAAG,CAAA;AACX,CAAC","sourcesContent":["import { DAY } from '@atproto/common'\nimport {\n type Server,\n UpstreamTimeoutError,\n parseReqEncoding,\n} from '@atproto/xrpc-server'\nimport type { AppContext } from '../../../../context.js'\nimport { com } from '../../../../lexicons/index.js'\n\nexport default function (server: Server, ctx: AppContext) {\n server.add(com.atproto.repo.uploadBlob, {\n auth: ctx.authVerifier.authorizationOrUserServiceAuth({\n checkTakedown: true,\n authorize: (permissions, { req }) => {\n const encoding = parseReqEncoding(req)\n permissions.assertBlob({ mime: encoding })\n },\n }),\n rateLimit: {\n durationMs: DAY,\n points: 1000,\n },\n handler: async ({ auth, input }) => {\n const requester = auth.credentials.did\n\n const blob = await ctx.actorStore.writeNoTransaction(\n requester,\n async (store) => {\n const metadata = await store.repo.blob\n .uploadBlobAndGetMetadata(\n input.body,\n input.encoding,\n ctx.cfg.service.blobUploadLimit,\n )\n .catch(throwAbortAsUpstreamError)\n\n return store.transact(async (actorTxn) => {\n const blobRef =\n await actorTxn.repo.blob.trackUntetheredBlob(metadata)\n\n // make the blob permanent if an associated record is already indexed\n if (await actorTxn.repo.blob.hasRecordsForBlob(blobRef.ref)) {\n await actorTxn.repo.blob.verifyBlobAndMakePermanent(blobRef)\n }\n\n return blobRef\n })\n },\n )\n\n return {\n encoding: 'application/json' as const,\n body: { blob },\n }\n },\n })\n}\n\nfunction throwAbortAsUpstreamError(err: unknown): never {\n // Blob store timeouts surface as AbortError (total upload timeout) or\n // TimeoutError (stalled connection), possibly wrapped as an error cause\n // (e.g. S3BlobStore's \"Blob upload timed out\" error). The depth limit\n // guards against (pathological) cyclic cause chains.\n for (let e = err, depth = 0; e instanceof Error && depth < 10; depth++) {\n if (e.name === 'AbortError' || e.name === 'TimeoutError') {\n throw new UpstreamTimeoutError('Operation timed out, please try again.')\n }\n e = e.cause\n }\n throw err\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-verifier.d.ts","sourceRoot":"","sources":["../src/auth-verifier.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAoC,MAAM,aAAa,CAAA;AAC9E,OAAO,KAAK,EAAE,eAAe,EAAkB,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"auth-verifier.d.ts","sourceRoot":"","sources":["../src/auth-verifier.ts"],"names":[],"mappings":"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"}
|
package/dist/auth-verifier.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createPublicKey, createSecretKey } from 'node:crypto';
|
|
2
|
+
import { secp256k1 } from '@noble/curves/secp256k1';
|
|
2
3
|
import * as jose from 'jose';
|
|
3
|
-
import KeyEncoderModule from 'key-encoder';
|
|
4
4
|
import { getVerificationMaterial } from '@atproto/common';
|
|
5
5
|
import { getDidKeyFromMultibase } from '@atproto/identity';
|
|
6
6
|
import { isDidString, } from '@atproto/lex';
|
|
@@ -10,8 +10,6 @@ import { AuthRequiredError, ForbiddenError, InvalidRequestError, XRPCError, pars
|
|
|
10
10
|
import { ACCESS_STANDARD, AuthScope, isAuthScope } from './auth-scope.js';
|
|
11
11
|
import { softDeleted } from './db/index.js';
|
|
12
12
|
import { appendVary } from './util/http.js';
|
|
13
|
-
// key-encoder is CJS with exports.default; Node ESM interop wraps it as { default: Class }
|
|
14
|
-
const KeyEncoder = ((m) => m.default ?? m)(KeyEncoderModule);
|
|
15
13
|
export class AuthVerifier {
|
|
16
14
|
constructor(accountManager, idResolver, oauthVerifier, opts) {
|
|
17
15
|
this.accountManager = accountManager;
|
|
@@ -435,10 +433,18 @@ const parseBasicAuth = (req) => {
|
|
|
435
433
|
export const createSecretKeyObject = (secret) => {
|
|
436
434
|
return createSecretKey(Buffer.from(secret));
|
|
437
435
|
};
|
|
438
|
-
const keyEncoder = new KeyEncoder('secp256k1');
|
|
439
436
|
export const createPublicKeyObject = (publicKeyHex) => {
|
|
440
|
-
|
|
441
|
-
|
|
437
|
+
// accepts compressed or uncompressed keys; validates the point is on-curve
|
|
438
|
+
const point = secp256k1.ProjectivePoint.fromHex(publicKeyHex).toRawBytes(false);
|
|
439
|
+
return createPublicKey({
|
|
440
|
+
format: 'jwk',
|
|
441
|
+
key: {
|
|
442
|
+
kty: 'EC',
|
|
443
|
+
crv: 'secp256k1',
|
|
444
|
+
x: Buffer.from(point.subarray(1, 33)).toString('base64url'),
|
|
445
|
+
y: Buffer.from(point.subarray(33, 65)).toString('base64url'),
|
|
446
|
+
},
|
|
447
|
+
});
|
|
442
448
|
};
|
|
443
449
|
function setAuthHeaders(res) {
|
|
444
450
|
res.setHeader('Cache-Control', 'private');
|