Unverified Commit cf19cf40 authored by Hong Minhee's avatar Hong Minhee
Browse files

Following collection

parent ae5975be
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -42,6 +42,7 @@ federation.setActorDispatcher("/users/{handle}", async (ctx, handle, key) => {
    url: new URL("/", ctx.request.url),
    outbox: ctx.getOutboxUri(handle),
    inbox: ctx.getInboxUri(handle),
    following: ctx.getFollowingUri(handle),
    followers: ctx.getFollowersUri(handle),
    publicKey: key,
  });
@@ -155,6 +156,18 @@ federation.setInboxListeners("/users/{handle}/inbox")
  })
  .onError((e) => console.error(e));

// Since the blog does not follow anyone, the following dispatcher is
// implemented to return just an empty list:
federation.setFollowingDispatcher(
  "/users/{handle}/following",
  async (_ctx, handle, _cursor) => {
    const blog = await getBlog();
    if (blog == null) return null;
    else if (blog.handle !== handle) return null;
    return { items: [] };
  },
);

federation
  .setFollowersDispatcher(
    "/users/{handle}/followers",
+7 −0
Original line number Diff line number Diff line
@@ -37,6 +37,13 @@ export interface Context<TContextData> {
   */
  getInboxUri(handle: string): URL;

  /**
   * Builds the URI of an actor's following collection with the given handle.
   * @param handle The actor's handle.
   * @returns The actor's following collection URI.
   */
  getFollowingUri(handle: string): URL;

  /**
   * Builds the URI of an actor's followers collection with the given handle.
   * @param handle The actor's handle.
+65 −2
Original line number Diff line number Diff line
@@ -61,6 +61,7 @@ export class Federation<TContextData> {
  #router: Router;
  #actorCallbacks?: ActorCallbacks<TContextData>;
  #outboxCallbacks?: CollectionCallbacks<Activity, TContextData>;
  #followingCallbacks?: CollectionCallbacks<Actor | URL, TContextData>;
  #followersCallbacks?: CollectionCallbacks<Actor | URL, TContextData>;
  #inboxListeners: Map<
    new (...args: unknown[]) => Activity,
@@ -202,6 +203,13 @@ export class Federation<TContextData> {
        }
        return new URL(path, url);
      },
      getFollowingUri: (handle: string): URL => {
        const path = this.#router.build("following", { handle });
        if (path == null) {
          throw new RouterError("No following collection path registered.");
        }
        return new URL(path, url);
      },
      getFollowersUri: (handle: string): URL => {
        const path = this.#router.build("followers", { handle });
        if (path == null) {
@@ -344,12 +352,55 @@ export class Federation<TContextData> {
  }

  /**
   * Registers an followers collection dispatcher.
   * Registers a following collection dispatcher.
   * @param path The URI path pattern for the following collection.  The syntax
   *             is based on URI Template
   *             ([RFC 6570](https://tools.ietf.org/html/rfc6570)).  The path
   *             must have one variable: `{handle}`.
   * @param dispatcher A following collection callback to register.
   * @throws {@link RouterError} Thrown if the path pattern is invalid.
   */
  setFollowingDispatcher(
    path: string,
    dispatcher: CollectionDispatcher<Actor | URL, TContextData>,
  ): CollectionCallbackSetters<TContextData> {
    if (this.#router.has("following")) {
      throw new RouterError("Following collection dispatcher already set.");
    }
    const variables = this.#router.add(path, "following");
    if (variables.size !== 1 || !variables.has("handle")) {
      throw new RouterError(
        "Path for following collection dispatcher must have one variable: {handle}",
      );
    }
    const callbacks: CollectionCallbacks<Actor | URL, TContextData> = {
      dispatcher,
    };
    this.#followingCallbacks = callbacks;
    const setters: CollectionCallbackSetters<TContextData> = {
      setCounter(counter: CollectionCounter<TContextData>) {
        callbacks.counter = counter;
        return setters;
      },
      setFirstCursor(cursor: CollectionCursor<TContextData>) {
        callbacks.firstCursor = cursor;
        return setters;
      },
      setLastCursor(cursor: CollectionCursor<TContextData>) {
        callbacks.lastCursor = cursor;
        return setters;
      },
    };
    return setters;
  }

  /**
   * Registers a followers collection dispatcher.
   * @param path The URI path pattern for the followers collection.  The syntax
   *             is based on URI Template
   *             ([RFC 6570](https://tools.ietf.org/html/rfc6570)).  The path
   *             must have one variable: `{handle}`.
   * @param dispatcher An outbox collection callback to register.
   * @param dispatcher A followers collection callback to register.
   * @throws {@link RouterError} Thrown if the path pattern is invalid.
   */
  setFollowersDispatcher(
@@ -516,6 +567,18 @@ export class Federation<TContextData> {
          inboxErrorHandler: this.#inboxErrorHandler,
          onNotFound,
        });
      case "following":
        return await handleCollection(request, {
          handle: route.values.handle,
          context,
          documentLoader: this.#documentLoader,
          collectionDispatcher: this.#followingCallbacks?.dispatcher,
          collectionCounter: this.#followingCallbacks?.counter,
          collectionFirstCursor: this.#followingCallbacks?.firstCursor,
          collectionLastCursor: this.#followingCallbacks?.lastCursor,
          onNotFound,
          onNotAcceptable,
        });
      case "followers":
        return await handleCollection(request, {
          handle: route.values.handle,