Unverified Commit 3b316d61 authored by Hong Minhee's avatar Hong Minhee
Browse files

Let inbox drop redundant activities

parent 55225bfd
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -186,6 +186,8 @@ export async function handleOutbox<TContextData>(
export interface InboxHandlerParameters<TContextData> {
  handle: string;
  context: RequestContext<TContextData>;
  kv: Deno.Kv;
  kvPrefix: Deno.KvKey;
  actorDispatcher?: ActorDispatcher<TContextData>;
  inboxListeners: Map<
    new (...args: unknown[]) => Activity,
@@ -201,6 +203,8 @@ export async function handleInbox<TContextData>(
  {
    handle,
    context,
    kv,
    kvPrefix,
    actorDispatcher,
    inboxListeners,
    inboxErrorHandler,
@@ -250,6 +254,19 @@ export async function handleInbox<TContextData>(
      headers: { "Content-Type": "text/plain; charset=utf-8" },
    });
  }
  const cacheKey = activity.id == null ? null : [...kvPrefix, activity.id.href];
  if (cacheKey != null) {
    const cached = await kv.get(cacheKey);
    if (cached != null && cached.value === true) {
      return new Response(
        `Activity <${activity.id}> has already been processed.`,
        {
          status: 202,
          headers: { "Content-Type": "text/plain; charset=utf-8" },
        },
      );
    }
  }
  if (activity.actorId == null) {
    const response = new Response("Missing actor.", {
      status: 400,
@@ -281,6 +298,9 @@ export async function handleInbox<TContextData>(
  const listener = inboxListeners.get(cls)!;
  const promise = listener(context, activity);
  if (promise instanceof Promise) await promise;
  if (cacheKey != null) {
    await kv.set(cacheKey, true, { expireIn: 1000 * 60 * 60 * 24 });
  }
  return new Response("", {
    status: 202,
    headers: { "Content-Type": "text/plain; charset=utf-8" },
+31 −1
Original line number Diff line number Diff line
@@ -26,10 +26,27 @@ import { extractInboxes, sendActivity } from "./send.ts";
 */
export interface FederationParameters {
  kv: Deno.Kv;
  kvPrefixes?: Partial<FederationKvPrefixes>;
  documentLoader?: DocumentLoader;
  treatHttps?: boolean;
}

/**
 * Prefixes for namespacing keys in the Deno KV store.
 */
export interface FederationKvPrefixes {
  /**
   * The key prefix used for storing whether activities have already been
   * processed or not.
   */
  activityIdempotence: Deno.KvKey;

  /**
   * The key prefix used for storing remote JSON-LD documents.
   */
  remoteDocument: Deno.KvKey;
}

/**
 * An object that registers federation-related business logic and dispatches
 * requests to the appropriate handlers.
@@ -39,6 +56,7 @@ export interface FederationParameters {
 */
export class Federation<TContextData> {
  #kv: Deno.Kv;
  #kvPrefixes: FederationKvPrefixes;
  #router: Router;
  #actorCallbacks?: ActorCallbacks<TContextData>;
  #outboxCallbacks?: OutboxCallbacks<TContextData>;
@@ -54,14 +72,24 @@ export class Federation<TContextData> {
   * Create a new {@link Federation} instance.
   * @param parameters Parameters for initializing the instance.
   */
  constructor({ kv, documentLoader, treatHttps }: FederationParameters) {
  constructor(
    { kv, kvPrefixes, documentLoader, treatHttps }: FederationParameters,
  ) {
    this.#kv = kv;
    this.#kvPrefixes = {
      ...({
        activityIdempotence: ["_fedify", "activityIdempotence"],
        remoteDocument: ["_fedify", "remoteDocument"],
      } satisfies FederationKvPrefixes),
      ...(kvPrefixes ?? {}),
    };
    this.#router = new Router();
    this.#router.add("/.well-known/webfinger", "webfinger");
    this.#inboxListeners = new Map();
    this.#documentLoader = documentLoader ?? kvCache({
      loader: fetchDocumentLoader,
      kv: kv,
      prefix: this.#kvPrefixes.remoteDocument,
    });
    this.#treatHttps = treatHttps ?? false;

@@ -400,6 +428,8 @@ export class Federation<TContextData> {
        return await handleInbox(request, {
          handle: route.values.handle,
          context,
          kv: this.#kv,
          kvPrefix: this.#kvPrefixes.activityIdempotence,
          documentLoader: this.#documentLoader,
          actorDispatcher: this.#actorCallbacks?.dispatcher,
          inboxListeners: this.#inboxListeners,