@fedify/fedify 2.4.0-dev.1599 → 2.4.0-dev.1618
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/{builder-BvCI7Xch.mjs → builder-C4a7TAvq.mjs} +24 -2
- package/dist/compat/mod.d.cts +1 -1
- package/dist/compat/mod.d.ts +1 -1
- package/dist/compat/transformers.test.mjs +1 -1
- package/dist/{context-C7Sbmci2.mjs → context-BJXxPElm.mjs} +2 -1
- package/dist/{context-GfQA-CqB.d.ts → context-Zii1f5xS.d.ts} +97 -1
- package/dist/{context-BLe0NPtZ.d.cts → context-lxX6I7as.d.cts} +97 -1
- package/dist/{deno-BCt09TJn.mjs → deno-DepnPEgI.mjs} +1 -1
- package/dist/{docloader-BrRjFwOp.mjs → docloader-C_IefWRb.mjs} +2 -2
- package/dist/federation/builder.test.mjs +11 -1
- package/dist/federation/handler.test.mjs +4 -4
- package/dist/federation/idempotency.test.mjs +2 -2
- package/dist/federation/metrics.test.mjs +1 -1
- package/dist/federation/middleware.test.mjs +456 -8
- package/dist/federation/mod.cjs +1 -1
- package/dist/federation/mod.d.cts +3 -3
- package/dist/federation/mod.d.ts +3 -3
- package/dist/federation/mod.js +1 -1
- package/dist/federation/send.test.mjs +3 -3
- package/dist/federation/tasks/enqueue.test.mjs +2 -2
- package/dist/federation/tasks/tasks.test.mjs +3 -3
- package/dist/federation/temporal.test.mjs +1 -1
- package/dist/federation/webfinger.test.mjs +2 -2
- package/dist/{http-yainOC9t.mjs → http-CT7tcVBN.mjs} +3 -3
- package/dist/{http-zJsVCnKu.cjs → http-D5ylizT7.cjs} +1 -1
- package/dist/{http--wLYmH1x.js → http-DD07Erm7.js} +1 -1
- package/dist/{key-DcLwG7x4.mjs → key-C3t-PS3M.mjs} +2 -2
- package/dist/{kv-cache-0TGAGm8S.js → kv-cache-Bvv7Wp8h.js} +1 -1
- package/dist/{kv-cache-SZL9uu3V.mjs → kv-cache-CK273Baz.mjs} +1 -1
- package/dist/{kv-cache-BPu_M3-N.cjs → kv-cache-DbFdquuM.cjs} +1 -1
- package/dist/{ld-B-jskR-q.mjs → ld-D8wAPjxC.mjs} +3 -3
- package/dist/{metrics-C-pKLuAU.mjs → metrics-D1NrDWq-.mjs} +1 -1
- package/dist/{middleware-DGEQ07xT.mjs → middleware-BRunoF2v.mjs} +232 -12
- package/dist/{middleware-HFQkD-r8.mjs → middleware-CaP0k3AD.mjs} +1 -1
- package/dist/{middleware-D1POKL1d.js → middleware-kQ-qwTDh.js} +245 -3
- package/dist/{middleware-DLzOFLD5.cjs → middleware-nmfhGFtV.cjs} +245 -3
- package/dist/{mod-Cmr39iWq.d.ts → mod-DLc2Ot6f.d.ts} +1 -1
- package/dist/{mod-B8Efb9qp.d.cts → mod-gOlPDuT8.d.cts} +1 -1
- package/dist/mod.cjs +4 -4
- package/dist/mod.d.cts +4 -4
- package/dist/mod.d.ts +4 -4
- package/dist/mod.js +4 -4
- package/dist/nodeinfo/handler.test.mjs +2 -2
- package/dist/{owner-CeJuB8wq.mjs → owner-BjdNNmKF.mjs} +2 -2
- package/dist/{proof-Bri1zUVM.js → proof--HiDnLAE.js} +14 -3
- package/dist/{proof-917XYw_e.mjs → proof-1Xy5Ur-T.mjs} +16 -4
- package/dist/{proof-BkQosXrd.cjs → proof-DcOiiLDF.cjs} +13 -2
- package/dist/{send-CDi3XQ0V.mjs → send-ClJ4UcZL.mjs} +3 -3
- package/dist/sig/http.test.mjs +2 -2
- package/dist/sig/key.test.mjs +1 -1
- package/dist/sig/ld.test.mjs +2 -2
- package/dist/sig/mod.cjs +2 -2
- package/dist/sig/mod.js +2 -2
- package/dist/sig/owner.test.mjs +1 -1
- package/dist/sig/proof.test.mjs +138 -1
- package/dist/{temporal-jusSJxbN.mjs → temporal-wA-b1IAz.mjs} +1 -1
- package/dist/testing/mod.d.mts +96 -0
- package/dist/testing/mod.mjs +2 -2
- package/dist/{testing-C3vgUK0b.mjs → testing-CO7ftO8A.mjs} +1 -1
- package/dist/utils/docloader.test.mjs +2 -2
- package/dist/utils/kv-cache.test.mjs +1 -1
- package/dist/utils/mod.cjs +1 -1
- package/dist/utils/mod.js +1 -1
- package/package.json +6 -6
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Temporal } from "temporal-polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { n as version, t as name } from "./deno-
|
|
5
|
-
import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-
|
|
4
|
+
import { n as version, t as name } from "./deno-DepnPEgI.mjs";
|
|
5
|
+
import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-D1NrDWq-.mjs";
|
|
6
6
|
import { i as validateAcceptSignature, n as fulfillAcceptSignature, r as parseAcceptSignature } from "./accept-8PiolDP7.mjs";
|
|
7
|
-
import { o as validateCryptoKey, r as fetchKeyDetailed } from "./key-
|
|
7
|
+
import { o as validateCryptoKey, r as fetchKeyDetailed } from "./key-C3t-PS3M.mjs";
|
|
8
8
|
import { getLogger } from "@logtape/logtape";
|
|
9
9
|
import { CryptographicKey } from "@fedify/vocab";
|
|
10
10
|
import { SpanStatusCode, trace } from "@opentelemetry/api";
|
|
@@ -11,7 +11,7 @@ let _opentelemetry_semantic_conventions = require("@opentelemetry/semantic-conve
|
|
|
11
11
|
let byte_encodings_base64 = require("byte-encodings/base64");
|
|
12
12
|
//#region deno.json
|
|
13
13
|
var name = "@fedify/fedify";
|
|
14
|
-
var version = "2.4.0-dev.
|
|
14
|
+
var version = "2.4.0-dev.1618+58858243";
|
|
15
15
|
//#endregion
|
|
16
16
|
//#region src/sig/accept.ts
|
|
17
17
|
/**
|
|
@@ -10,7 +10,7 @@ import { ATTR_HTTP_REQUEST_HEADER, ATTR_HTTP_REQUEST_METHOD, ATTR_URL_FULL } fro
|
|
|
10
10
|
import { decodeBase64, encodeBase64 } from "byte-encodings/base64";
|
|
11
11
|
//#region deno.json
|
|
12
12
|
var name = "@fedify/fedify";
|
|
13
|
-
var version = "2.4.0-dev.
|
|
13
|
+
var version = "2.4.0-dev.1618+58858243";
|
|
14
14
|
//#endregion
|
|
15
15
|
//#region src/sig/accept.ts
|
|
16
16
|
/**
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import "temporal-polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { n as version, t as name } from "./deno-
|
|
5
|
-
import { _ as recordKeyLookup, n as getDurationMs, t as classifyFetchError } from "./metrics-
|
|
4
|
+
import { n as version, t as name } from "./deno-DepnPEgI.mjs";
|
|
5
|
+
import { _ as recordKeyLookup, n as getDurationMs, t as classifyFetchError } from "./metrics-D1NrDWq-.mjs";
|
|
6
6
|
import { getLogger } from "@logtape/logtape";
|
|
7
7
|
import { CryptographicKey, Multikey, Object as Object$1, isActor } from "@fedify/vocab";
|
|
8
8
|
import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Temporal } from "temporal-polyfill";
|
|
2
2
|
import { URLPattern } from "urlpattern-polyfill";
|
|
3
|
-
import { C as recordDocumentCache, d as validateCryptoKey, t as doubleKnock } from "./http
|
|
3
|
+
import { C as recordDocumentCache, d as validateCryptoKey, t as doubleKnock } from "./http-DD07Erm7.js";
|
|
4
4
|
import { getLogger } from "@logtape/logtape";
|
|
5
5
|
import { curry } from "es-toolkit";
|
|
6
6
|
import { UrlError, createActivityPubRequest, getRemoteDocument, logRequest, preloadedContexts, validatePublicUrl } from "@fedify/vocab-runtime";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Temporal } from "temporal-polyfill";
|
|
2
2
|
import { URLPattern } from "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { p as recordDocumentCache } from "./metrics-
|
|
4
|
+
import { p as recordDocumentCache } from "./metrics-D1NrDWq-.mjs";
|
|
5
5
|
import { getLogger } from "@logtape/logtape";
|
|
6
6
|
import { preloadedContexts } from "@fedify/vocab-runtime";
|
|
7
7
|
//#region src/utils/kv-cache.ts
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const { URLPattern } = require("urlpattern-polyfill");
|
|
2
2
|
require("./chunk-Czpeue4M.cjs");
|
|
3
|
-
const require_http = require("./http-
|
|
3
|
+
const require_http = require("./http-D5ylizT7.cjs");
|
|
4
4
|
const require_temporal_polyfill = require("./temporal-polyfill-BCdTHexd.cjs");
|
|
5
5
|
let _logtape_logtape = require("@logtape/logtape");
|
|
6
6
|
let es_toolkit = require("es-toolkit");
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import "temporal-polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { n as version, t as name } from "./deno-
|
|
5
|
-
import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-
|
|
6
|
-
import { n as fetchKey, o as validateCryptoKey } from "./key-
|
|
4
|
+
import { n as version, t as name } from "./deno-DepnPEgI.mjs";
|
|
5
|
+
import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-D1NrDWq-.mjs";
|
|
6
|
+
import { n as fetchKey, o as validateCryptoKey } from "./key-C3t-PS3M.mjs";
|
|
7
7
|
import { getLogger } from "@logtape/logtape";
|
|
8
8
|
import { Activity, CryptographicKey, Object as Object$1, getTypeId } from "@fedify/vocab";
|
|
9
9
|
import { SpanStatusCode, trace } from "@opentelemetry/api";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "temporal-polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { n as version, t as name } from "./deno-
|
|
4
|
+
import { n as version, t as name } from "./deno-DepnPEgI.mjs";
|
|
5
5
|
import { metrics } from "@opentelemetry/api";
|
|
6
6
|
import { FetchError } from "@fedify/vocab-runtime";
|
|
7
7
|
//#region src/federation/metrics.ts
|
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
import { Temporal } from "temporal-polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { n as version, t as name } from "./deno-
|
|
5
|
-
import { a as instrumentDocumentLoader, b as recordWebFingerHandle, c as recordCircuitBreakerStateChange, d as recordCollectionRequest, f as recordCollectionTotalItems, g as recordInboxActivity, h as recordFanoutRecipients, i as getRemoteHost, l as recordCollectionDispatchDuration, n as getDurationMs, o as isAbortError, r as getFederationMetrics, u as recordCollectionPageItems, v as recordOutboxActivity, x as registerQueueDepthGauge, y as recordOutboxEnqueue } from "./metrics-
|
|
4
|
+
import { n as version, t as name } from "./deno-DepnPEgI.mjs";
|
|
5
|
+
import { a as instrumentDocumentLoader, b as recordWebFingerHandle, c as recordCircuitBreakerStateChange, d as recordCollectionRequest, f as recordCollectionTotalItems, g as recordInboxActivity, h as recordFanoutRecipients, i as getRemoteHost, l as recordCollectionDispatchDuration, n as getDurationMs, o as isAbortError, r as getFederationMetrics, u as recordCollectionPageItems, v as recordOutboxActivity, x as registerQueueDepthGauge, y as recordOutboxEnqueue } from "./metrics-D1NrDWq-.mjs";
|
|
6
6
|
import { t as formatAcceptSignature } from "./accept-8PiolDP7.mjs";
|
|
7
|
-
import { a as importJwk, o as validateCryptoKey, t as exportJwk } from "./key-
|
|
8
|
-
import { l as verifyRequest, o as parseRfc9421SignatureInput, u as verifyRequestDetailed } from "./http-
|
|
9
|
-
import { t as getAuthenticatedDocumentLoader } from "./docloader-
|
|
10
|
-
import { n as kvCache } from "./kv-cache-
|
|
11
|
-
import { _ as wrapContextLoaderForJsonLd, a as compactJsonLd, c as getNormalizationContextLoader, d as isClearlyMalformedContextReference, f as isInvalidUrlTypeError, l as hasSignature, m as verifyCompactJsonLd, p as signJsonLd, r as assertSafeJsonLd, s as detachSignature, t as InvalidContextReferenceError, u as hasSignatureLike } from "./ld-
|
|
12
|
-
import { n as getKeyOwner, t as doesActorOwnKey } from "./owner-
|
|
7
|
+
import { a as importJwk, o as validateCryptoKey, t as exportJwk } from "./key-C3t-PS3M.mjs";
|
|
8
|
+
import { l as verifyRequest, o as parseRfc9421SignatureInput, u as verifyRequestDetailed } from "./http-CT7tcVBN.mjs";
|
|
9
|
+
import { t as getAuthenticatedDocumentLoader } from "./docloader-C_IefWRb.mjs";
|
|
10
|
+
import { n as kvCache } from "./kv-cache-CK273Baz.mjs";
|
|
11
|
+
import { _ as wrapContextLoaderForJsonLd, a as compactJsonLd, c as getNormalizationContextLoader, d as isClearlyMalformedContextReference, f as isInvalidUrlTypeError, l as hasSignature, m as verifyCompactJsonLd, p as signJsonLd, r as assertSafeJsonLd, s as detachSignature, t as InvalidContextReferenceError, u as hasSignatureLike } from "./ld-D8wAPjxC.mjs";
|
|
12
|
+
import { n as getKeyOwner, t as doesActorOwnKey } from "./owner-BjdNNmKF.mjs";
|
|
13
13
|
import { r as normalizeOutgoingActivityJsonLd } from "./outgoing-jsonld-URuqsICz.mjs";
|
|
14
|
-
import { i as verifyObject, n as hasProofLike, r as signObject } from "./proof-
|
|
14
|
+
import { i as verifyObject, n as hasProofLike, r as signObject } from "./proof-1Xy5Ur-T.mjs";
|
|
15
15
|
import { t as getNodeInfo } from "./client-CjF5IYQS.mjs";
|
|
16
16
|
import { t as nodeInfoToJson } from "./types-CrtdVuDP.mjs";
|
|
17
|
-
import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "./send-
|
|
18
|
-
import { n as FederationBuilderImpl, t as ACTOR_ALIAS_PREFIX } from "./builder-
|
|
17
|
+
import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "./send-ClJ4UcZL.mjs";
|
|
18
|
+
import { n as FederationBuilderImpl, t as ACTOR_ALIAS_PREFIX } from "./builder-C4a7TAvq.mjs";
|
|
19
19
|
import { t as CircuitBreaker } from "./circuit-breaker-CBGyz5F7.mjs";
|
|
20
20
|
import { t as buildCollectionSynchronizationHeader } from "./collection-BIhlldqK.mjs";
|
|
21
21
|
import { t as KvKeyCache } from "./keycache-_DHqz5z_.mjs";
|
|
22
22
|
import { t as acceptsJsonLd } from "./negotiation-RuwrPFqi.mjs";
|
|
23
|
-
import { t as hasMalformedKnownTemporalLiteral } from "./temporal-
|
|
23
|
+
import { t as hasMalformedKnownTemporalLiteral } from "./temporal-wA-b1IAz.mjs";
|
|
24
24
|
import { t as createExponentialBackoffPolicy } from "./retry-DgFqeS5k.mjs";
|
|
25
25
|
import { t as TaskCodec } from "./codec-BBga-FWI.mjs";
|
|
26
26
|
import { n as ParallelMessageQueue } from "./mq-llHKivST.mjs";
|
|
@@ -1025,6 +1025,193 @@ async function handleOutbox(request, { identifier, context: ctx, outboxContextFa
|
|
|
1025
1025
|
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
1026
1026
|
});
|
|
1027
1027
|
}
|
|
1028
|
+
const plainTextHeaders = { "Content-Type": "text/plain; charset=utf-8" };
|
|
1029
|
+
/**
|
|
1030
|
+
* Handles a media upload request (the [ActivityPub Media Upload
|
|
1031
|
+
* extension](https://www.w3.org/wiki/SocialCG/ActivityPub/MediaUpload)).
|
|
1032
|
+
* @template TContextData The context data to pass to the context.
|
|
1033
|
+
* @param request The HTTP request.
|
|
1034
|
+
* @param parameters The parameters for handling the request.
|
|
1035
|
+
* @returns A promise that resolves to an HTTP response.
|
|
1036
|
+
* @since 2.4.0
|
|
1037
|
+
*/
|
|
1038
|
+
async function handleMediaUpload(request, { identifier, context: ctx, mediaUploaderCallback, actorDispatcher, authorizePredicate, isRegisteredObjectUri, onUnauthorized, onNotFound }) {
|
|
1039
|
+
const logger = getLogger([
|
|
1040
|
+
"fedify",
|
|
1041
|
+
"federation",
|
|
1042
|
+
"mediaUploader"
|
|
1043
|
+
]);
|
|
1044
|
+
if (request.bodyUsed) {
|
|
1045
|
+
logger.error("Request body has already been read.", { identifier });
|
|
1046
|
+
return new Response("Internal server error.", {
|
|
1047
|
+
status: 500,
|
|
1048
|
+
headers: plainTextHeaders
|
|
1049
|
+
});
|
|
1050
|
+
} else if (request.body?.locked) {
|
|
1051
|
+
logger.error("Request body is locked.", { identifier });
|
|
1052
|
+
return new Response("Internal server error.", {
|
|
1053
|
+
status: 500,
|
|
1054
|
+
headers: plainTextHeaders
|
|
1055
|
+
});
|
|
1056
|
+
}
|
|
1057
|
+
const contentType = request.headers.get("content-type");
|
|
1058
|
+
if (contentType == null || !contentType.toLowerCase().startsWith("multipart/form-data")) {
|
|
1059
|
+
logger.error("The media upload request is not multipart/form-data (got {contentType}).", {
|
|
1060
|
+
identifier,
|
|
1061
|
+
contentType
|
|
1062
|
+
});
|
|
1063
|
+
return new Response("Unsupported media type.", {
|
|
1064
|
+
status: 415,
|
|
1065
|
+
headers: plainTextHeaders
|
|
1066
|
+
});
|
|
1067
|
+
}
|
|
1068
|
+
if (actorDispatcher == null) {
|
|
1069
|
+
logger.error("Actor dispatcher is not set.", { identifier });
|
|
1070
|
+
return await onNotFound(request);
|
|
1071
|
+
}
|
|
1072
|
+
const actor = await actorDispatcher(ctx, identifier);
|
|
1073
|
+
if (actor == null || actor instanceof Tombstone) {
|
|
1074
|
+
logger.error("Actor {identifier} not found.", { identifier });
|
|
1075
|
+
return await onNotFound(request);
|
|
1076
|
+
}
|
|
1077
|
+
if (authorizePredicate != null) {
|
|
1078
|
+
let body;
|
|
1079
|
+
try {
|
|
1080
|
+
body = await request.arrayBuffer();
|
|
1081
|
+
} catch (error) {
|
|
1082
|
+
logger.error("Failed to read the multipart/form-data body:\n{error}", {
|
|
1083
|
+
identifier,
|
|
1084
|
+
error
|
|
1085
|
+
});
|
|
1086
|
+
return new Response("Invalid multipart/form-data body.", {
|
|
1087
|
+
status: 400,
|
|
1088
|
+
headers: plainTextHeaders
|
|
1089
|
+
});
|
|
1090
|
+
}
|
|
1091
|
+
const rebuild = () => new Request(request.url, {
|
|
1092
|
+
method: request.method,
|
|
1093
|
+
headers: request.headers,
|
|
1094
|
+
body: body.byteLength > 0 ? body : void 0
|
|
1095
|
+
});
|
|
1096
|
+
const authorizeContext = ctx.clone(ctx.data);
|
|
1097
|
+
authorizeContext.request = rebuild();
|
|
1098
|
+
if (!await authorizePredicate(authorizeContext, identifier)) return await onUnauthorized(rebuild());
|
|
1099
|
+
request = rebuild();
|
|
1100
|
+
}
|
|
1101
|
+
let form;
|
|
1102
|
+
try {
|
|
1103
|
+
form = await request.formData();
|
|
1104
|
+
} catch (error) {
|
|
1105
|
+
logger.error("Failed to parse the multipart/form-data body:\n{error}", {
|
|
1106
|
+
identifier,
|
|
1107
|
+
error
|
|
1108
|
+
});
|
|
1109
|
+
return new Response("Invalid multipart/form-data body.", {
|
|
1110
|
+
status: 400,
|
|
1111
|
+
headers: plainTextHeaders
|
|
1112
|
+
});
|
|
1113
|
+
}
|
|
1114
|
+
const file = form.get("file");
|
|
1115
|
+
if (!(file instanceof File)) {
|
|
1116
|
+
logger.error("The media upload request has no file field.", { identifier });
|
|
1117
|
+
return new Response("Missing file field.", {
|
|
1118
|
+
status: 400,
|
|
1119
|
+
headers: plainTextHeaders
|
|
1120
|
+
});
|
|
1121
|
+
}
|
|
1122
|
+
const objectField = form.get("object");
|
|
1123
|
+
if (objectField == null) {
|
|
1124
|
+
logger.error("The media upload request has no object field.", { identifier });
|
|
1125
|
+
return new Response("Missing object field.", {
|
|
1126
|
+
status: 400,
|
|
1127
|
+
headers: plainTextHeaders
|
|
1128
|
+
});
|
|
1129
|
+
}
|
|
1130
|
+
let object;
|
|
1131
|
+
try {
|
|
1132
|
+
const objectText = typeof objectField === "string" ? objectField : await objectField.text();
|
|
1133
|
+
object = await Object$1.fromJsonLd(JSON.parse(objectText), ctx);
|
|
1134
|
+
} catch (error) {
|
|
1135
|
+
logger.error("Failed to parse the object field:\n{error}", {
|
|
1136
|
+
identifier,
|
|
1137
|
+
error
|
|
1138
|
+
});
|
|
1139
|
+
return new Response("Invalid object field.", {
|
|
1140
|
+
status: 400,
|
|
1141
|
+
headers: plainTextHeaders
|
|
1142
|
+
});
|
|
1143
|
+
}
|
|
1144
|
+
let result;
|
|
1145
|
+
try {
|
|
1146
|
+
result = await mediaUploaderCallback(ctx, identifier, file, object);
|
|
1147
|
+
} catch (error) {
|
|
1148
|
+
logger.error("Failed to process the media upload:\n{error}", {
|
|
1149
|
+
identifier,
|
|
1150
|
+
error
|
|
1151
|
+
});
|
|
1152
|
+
return new Response("Internal server error.", {
|
|
1153
|
+
status: 500,
|
|
1154
|
+
headers: plainTextHeaders
|
|
1155
|
+
});
|
|
1156
|
+
}
|
|
1157
|
+
if (result == null) {
|
|
1158
|
+
logger.error("The media uploader callback for {identifier} returned null or undefined; it must return an object (201 Created) or a URL (202 Accepted).", { identifier });
|
|
1159
|
+
return new Response("Internal server error.", {
|
|
1160
|
+
status: 500,
|
|
1161
|
+
headers: plainTextHeaders
|
|
1162
|
+
});
|
|
1163
|
+
}
|
|
1164
|
+
const warnUnlessRegistered = (target) => {
|
|
1165
|
+
if (isRegisteredObjectUri(target)) return;
|
|
1166
|
+
logger.warn("The media uploader callback for {identifier} returned {target}, which does not point at a registered object dispatcher route; derive the returned id/URL from Context.getObjectUri(). The upload still succeeded.", {
|
|
1167
|
+
identifier,
|
|
1168
|
+
target: target.href
|
|
1169
|
+
});
|
|
1170
|
+
};
|
|
1171
|
+
if (result instanceof URL) {
|
|
1172
|
+
warnUnlessRegistered(result);
|
|
1173
|
+
logger.info("The media upload for {identifier} is still being processed; responding 202 Accepted.", {
|
|
1174
|
+
identifier,
|
|
1175
|
+
location: result.href
|
|
1176
|
+
});
|
|
1177
|
+
return new Response(null, {
|
|
1178
|
+
status: 202,
|
|
1179
|
+
headers: { Location: result.href }
|
|
1180
|
+
});
|
|
1181
|
+
}
|
|
1182
|
+
if (result.id == null) {
|
|
1183
|
+
logger.error("The media uploader callback for {identifier} returned an object without an id, so a 201 Created response cannot include the required Location header. Set the object's id with Context.getObjectUri(), or return the eventual URL to respond 202 Accepted instead.", { identifier });
|
|
1184
|
+
return new Response("Internal server error.", {
|
|
1185
|
+
status: 500,
|
|
1186
|
+
headers: plainTextHeaders
|
|
1187
|
+
});
|
|
1188
|
+
}
|
|
1189
|
+
warnUnlessRegistered(result.id);
|
|
1190
|
+
let jsonLd;
|
|
1191
|
+
try {
|
|
1192
|
+
jsonLd = await result.toJsonLd(ctx);
|
|
1193
|
+
} catch (error) {
|
|
1194
|
+
logger.error("Failed to serialize the uploaded object to JSON-LD:\n{error}", {
|
|
1195
|
+
identifier,
|
|
1196
|
+
error
|
|
1197
|
+
});
|
|
1198
|
+
return new Response("Internal server error.", {
|
|
1199
|
+
status: 500,
|
|
1200
|
+
headers: plainTextHeaders
|
|
1201
|
+
});
|
|
1202
|
+
}
|
|
1203
|
+
logger.info("The media upload for {identifier} is ready; responding 201 Created.", {
|
|
1204
|
+
identifier,
|
|
1205
|
+
location: result.id.href
|
|
1206
|
+
});
|
|
1207
|
+
return new Response(JSON.stringify(jsonLd), {
|
|
1208
|
+
status: 201,
|
|
1209
|
+
headers: {
|
|
1210
|
+
"Content-Type": "application/activity+json",
|
|
1211
|
+
Location: result.id.href
|
|
1212
|
+
}
|
|
1213
|
+
});
|
|
1214
|
+
}
|
|
1028
1215
|
/**
|
|
1029
1216
|
* Handles an inbox request for ActivityPub activities.
|
|
1030
1217
|
* @template TContextData The context data to pass to the context.
|
|
@@ -2641,6 +2828,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
2641
2828
|
benchmarkMode;
|
|
2642
2829
|
benchmarkMetricReader;
|
|
2643
2830
|
benchmarkTriggerOptions;
|
|
2831
|
+
#mediaUploaderNoAuthWarned = false;
|
|
2644
2832
|
#queueDepthGaugeSourceId = `fedify-${(++nextQueueDepthGaugeSourceId).toString(36)}`;
|
|
2645
2833
|
#queueDepthGaugeEntries = [];
|
|
2646
2834
|
#queueDepthGaugeMeterProvider;
|
|
@@ -3956,6 +4144,32 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
3956
4144
|
});
|
|
3957
4145
|
case "benchmarkStats": return await handleBenchmarkStats(request, this.benchmarkMetricReader);
|
|
3958
4146
|
case "benchmarkTrigger": return await handleBenchmarkTrigger(request, context, this.benchmarkTriggerOptions);
|
|
4147
|
+
case "mediaUploader":
|
|
4148
|
+
if (request.method !== "POST" || this.mediaUploaderCallback == null) return new Response("Method not allowed.", {
|
|
4149
|
+
status: 405,
|
|
4150
|
+
headers: {
|
|
4151
|
+
Allow: "POST",
|
|
4152
|
+
"Content-Type": "text/plain; charset=utf-8"
|
|
4153
|
+
}
|
|
4154
|
+
});
|
|
4155
|
+
if (this.mediaUploaderAuthorizePredicate == null && !this.#mediaUploaderNoAuthWarned) {
|
|
4156
|
+
this.#mediaUploaderNoAuthWarned = true;
|
|
4157
|
+
getLogger([
|
|
4158
|
+
"fedify",
|
|
4159
|
+
"federation",
|
|
4160
|
+
"mediaUploader"
|
|
4161
|
+
]).warn("The media uploader is registered without an authorize() hook, so it accepts uploads from anyone who can reach the endpoint. Protect it with .authorize() unless a public upload endpoint is intended.");
|
|
4162
|
+
}
|
|
4163
|
+
return await handleMediaUpload(request, {
|
|
4164
|
+
identifier: route.values.identifier,
|
|
4165
|
+
context,
|
|
4166
|
+
mediaUploaderCallback: this.mediaUploaderCallback,
|
|
4167
|
+
actorDispatcher: this.actorCallbacks?.dispatcher,
|
|
4168
|
+
authorizePredicate: this.mediaUploaderAuthorizePredicate,
|
|
4169
|
+
isRegisteredObjectUri: (uri) => context.parseUri(uri)?.type === "object",
|
|
4170
|
+
onUnauthorized,
|
|
4171
|
+
onNotFound
|
|
4172
|
+
});
|
|
3959
4173
|
}
|
|
3960
4174
|
if (request.method !== "POST" && !acceptsJsonLd(request)) {
|
|
3961
4175
|
metricState.endpoint = "not_acceptable";
|
|
@@ -4190,6 +4404,7 @@ function getEndpointCategory(routeName) {
|
|
|
4190
4404
|
case "inbox": return "inbox";
|
|
4191
4405
|
case "sharedInbox": return "shared_inbox";
|
|
4192
4406
|
case "outbox": return "outbox";
|
|
4407
|
+
case "mediaUploader": return "media_upload";
|
|
4193
4408
|
case "following": return "following";
|
|
4194
4409
|
case "followers": return "followers";
|
|
4195
4410
|
case "liked": return "liked";
|
|
@@ -4323,6 +4538,11 @@ var ContextImpl = class ContextImpl {
|
|
|
4323
4538
|
if (path == null) throw new RouterError("No outbox dispatcher registered.");
|
|
4324
4539
|
return new URL(path, this.canonicalOrigin);
|
|
4325
4540
|
}
|
|
4541
|
+
getMediaUploaderUri(identifier) {
|
|
4542
|
+
const path = this.federation.router.build("mediaUploader", { identifier });
|
|
4543
|
+
if (path == null) throw new RouterError("No media uploader registered.");
|
|
4544
|
+
return new URL(path, this.canonicalOrigin);
|
|
4545
|
+
}
|
|
4326
4546
|
getInboxUri(identifier) {
|
|
4327
4547
|
if (identifier == null) {
|
|
4328
4548
|
const path = this.federation.router.build("sharedInbox", {});
|