Commit 36fb8e96 authored by BaHwan's avatar BaHwan
Browse files

scaffold nextjs example

parent fde51c88
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
{
  "extends": ["next/core-web-vitals", "next/typescript"]
}
+40 −0
Original line number Diff line number Diff line
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# env files (can opt-in for commiting if needed)
.env*

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts
+1 −0
Original line number Diff line number Diff line
v20.17.0
+34 −0
Original line number Diff line number Diff line
# Fedify Next.JS Integration

## Running the Example

1. Clone the repository:

   ```sh
   git clone https://github.com/dahlia/fedify.git
   cd fedify/examples/next-app-router
   ```

2. Install dependencies:

   ```sh
   # optional
   nvm use
   npm i
   ```

3. Start the server:

   ```sh
   npm run dev & npx @fedify/cli tunnel 8000
   ```

4. Open your browser tunneled URL and start interacting with the app. You can see your handle such as `@demo@6c10b40c63d9e1ce7da55667ef0ef8b4.serveo.net`

5. Access https://activitypub.academy/ and search your handle and follow.

6. You can see following list like:
   ```
   This account has the below 1 followers:
   https://activitypub.academy/users/beboes_bedoshs
   ```
+106 −0
Original line number Diff line number Diff line
import {
  Accept,
  Endpoints,
  Follow,
  Person,
  Undo,
  createFederation,
  generateCryptoKeyPair,
  MemoryKvStore,
} from "@fedify/fedify";
import { keyPairsStore, relationStore } from "~/data/store";
import { integrateFederation } from "~/shared/integrate-fedify";

const federation = createFederation<void>({
  kv: new MemoryKvStore(),
});

const requestHanlder = integrateFederation(federation, () => {});

export {
  requestHanlder as GET,
  requestHanlder as POST,
  requestHanlder as PUT,
  requestHanlder as PATCH,
  requestHanlder as DELETE,
};

federation
  .setActorDispatcher("/users/{handle}", async (context, handle) => {
    if (handle != "demo") {
      return null;
    }
    const keyPairs = await context.getActorKeyPairs(handle);
    return new Person({
      id: context.getActorUri(handle),
      name: "Fedify Demo",
      summary: "This is a Fedify Demo account.",
      preferredUsername: handle,
      url: new URL("/", context.url),
      inbox: context.getInboxUri(handle),
      endpoints: new Endpoints({
        sharedInbox: context.getInboxUri(),
      }),
      publicKey: keyPairs[0].cryptographicKey,
      assertionMethods: keyPairs.map((keyPair) => keyPair.multikey),
    });
  })
  .setKeyPairsDispatcher(async (_, handle) => {
    if (handle != "demo") {
      return [];
    }
    const keyPairs = keyPairsStore.get(handle);
    if (keyPairs) {
      return keyPairs;
    }
    const { privateKey, publicKey } = await generateCryptoKeyPair();
    keyPairsStore.set(handle, [{ privateKey, publicKey }]);
    return [{ privateKey, publicKey }];
  });

federation
  .setInboxListeners("/users/{handle}/inbox", "/inbox")
  .on(Follow, async (context, follow) => {
    if (
      follow.id == null ||
      follow.actorId == null ||
      follow.objectId == null
    ) {
      return;
    }
    const result = context.parseUri(follow.objectId);
    if (result?.type !== "actor" || result.handle !== "demo") {
      return;
    }
    const follower = await follow.getActor(context);
    if (follower?.id == null) {
      throw new Error("follower is null");
    }
    await context.sendActivity(
      { handle: result.handle },
      follower,
      new Accept({
        id: new URL(
          `#accepts/${follower.id.href}`,
          context.getActorUri("demo")
        ),
        actor: follow.objectId,
        object: follow,
      })
    );
    relationStore.set(follower.id.href, follow.actorId.href);
  })
  .on(Undo, async (context, undo) => {
    const activity = await undo.getObject(context);
    if (activity instanceof Follow) {
      if (activity.id == null) {
        return;
      }
      if (undo.actorId == null) {
        return;
      }
      relationStore.delete(undo.actorId.href);
    } else {
      console.debug(undo);
    }
  });
Loading