16 | Re: {comment.author.name} 17 |
18 |19 | {comment.author.handle} ·{" "} 20 | 21 | 24 | 25 |
26 | 27 |19 | {comment.author.handle} ·{" "} 20 | 21 | 24 | 25 |
26 | 27 |16 | 19 |
20 | 21 |37 | 38 | @{blog.handle}@{domain} 39 | {" "} 40 | ·{" "} 41 | 42 | {followers === 1n ? "1 follower" : `${followers} followers`} 43 | {" "} 44 | · {blog.description} 45 |
46 | 47 |58 | {nextCursor 59 | ? More posts… 60 | : "No more posts."} (Total {total.toString()} posts.) 61 |
62 | > 63 | ); 64 | } 65 | -------------------------------------------------------------------------------- /examples/blog/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@fedify/blog", 3 | "exports": "./main.ts", 4 | "tasks": { 5 | "codegen": "deno task -f @fedify/fedify codegen", 6 | "check": { 7 | "command": "deno fmt --check && deno lint && deno check **/*.ts && deno check **/*.tsx", 8 | "dependencies": [ 9 | "codegen" 10 | ] 11 | }, 12 | "cli": { 13 | "command": "echo \"import '\\$fresh/src/dev/cli.ts'\" | deno run --unstable -A -", 14 | "dependencies": [ 15 | "codegen" 16 | ] 17 | }, 18 | "manifest": { 19 | "command": "deno task cli manifest $(pwd)", 20 | "dependencies": [ 21 | "codegen" 22 | ] 23 | }, 24 | "start": { 25 | "command": "deno run -A --watch=static/,routes/ --unstable-kv dev.ts", 26 | "dependencies": [ 27 | "codegen" 28 | ] 29 | }, 30 | "build": { 31 | "command": "deno run -A dev.ts build", 32 | "dependencies": [ 33 | "codegen" 34 | ] 35 | }, 36 | "preview": { 37 | "command": "deno run -A main.ts", 38 | "dependencies": [ 39 | "codegen" 40 | ] 41 | }, 42 | "update": "deno run -A -r https://fresh.deno.dev/update ." 43 | }, 44 | "lint": { 45 | "rules": { 46 | "tags": [ 47 | "fresh", 48 | "recommended" 49 | ], 50 | "exclude": [ 51 | "react-no-danger" 52 | ] 53 | } 54 | }, 55 | "exclude": [ 56 | "**/_fresh/*" 57 | ], 58 | "imports": { 59 | "$fresh/": "https://deno.land/x/fresh@1.7.3/", 60 | "@hongminhee/x-forwarded-fetch": "jsr:@hongminhee/x-forwarded-fetch@^0.2.0", 61 | "@preact/signals": "npm:@preact/signals@1.2.2", 62 | "@preact/signals-core": "npm:@preact/signals-core@1.5.1", 63 | "@std/dotenv/load": "jsr:@std/dotenv@^0.224.0/load", 64 | "@std/encoding/hex": "jsr:@std/encoding@^0.224.3/hex", 65 | "markdown-it": "npm:markdown-it@^14.1.0", 66 | "preact": "npm:preact@10.19.6", 67 | "sanitize-html": "npm:sanitize-html@^2.13.0", 68 | "scrypt": "jsr:@denorg/scrypt@4.4.4", 69 | "uuidv7": "npm:uuidv7@^1.0.0" 70 | }, 71 | "compilerOptions": { 72 | "jsx": "react-jsx", 73 | "jsxImportSource": "preact" 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /examples/blog/dev.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S deno run -A --watch=static/,routes/ 2 | import dev from "$fresh/dev.ts"; 3 | import "@std/dotenv/load"; 4 | 5 | await dev(import.meta.url, "./main.ts"); 6 | -------------------------------------------------------------------------------- /examples/blog/fresh.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "$fresh/server.ts"; 2 | import "./loggers.ts"; 3 | 4 | export default defineConfig({}); 5 | -------------------------------------------------------------------------------- /examples/blog/fresh.gen.ts: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This file is generated by Fresh. 2 | // This file SHOULD be checked into source version control. 3 | // This file is automatically updated during development when running `dev.ts`. 4 | 5 | import * as $_app from "./routes/_app.tsx"; 6 | import * as $_middleware from "./routes/_middleware.ts"; 7 | import * as $followers from "./routes/followers.tsx"; 8 | import * as $index from "./routes/index.tsx"; 9 | import * as $posts_uuid_ from "./routes/posts/[uuid].tsx"; 10 | import * as $posts_index from "./routes/posts/index.tsx"; 11 | import * as $users_handle_ from "./routes/users/[handle].ts"; 12 | 13 | import type { Manifest } from "$fresh/server.ts"; 14 | 15 | const manifest = { 16 | routes: { 17 | "./routes/_app.tsx": $_app, 18 | "./routes/_middleware.ts": $_middleware, 19 | "./routes/followers.tsx": $followers, 20 | "./routes/index.tsx": $index, 21 | "./routes/posts/[uuid].tsx": $posts_uuid_, 22 | "./routes/posts/index.tsx": $posts_index, 23 | "./routes/users/[handle].ts": $users_handle_, 24 | }, 25 | islands: {}, 26 | baseUrl: import.meta.url, 27 | } satisfies Manifest; 28 | 29 | export default manifest; 30 | -------------------------------------------------------------------------------- /examples/blog/images/handle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/examples/blog/images/handle.png -------------------------------------------------------------------------------- /examples/blog/images/setup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/examples/blog/images/setup.png -------------------------------------------------------------------------------- /examples/blog/loggers.ts: -------------------------------------------------------------------------------- 1 | import { getFileSink } from "@logtape/file"; 2 | import { configure, getConsoleSink, type LogLevel } from "@logtape/logtape"; 3 | import { AsyncLocalStorage } from "node:async_hooks"; 4 | 5 | await configure({ 6 | contextLocalStorage: new AsyncLocalStorage(), 7 | sinks: { 8 | console: getConsoleSink(), 9 | file: Deno.env.get("DENO_DEPLOYMENT_ID") == null 10 | ? getFileSink("log.jsonl", { 11 | formatter(log) { 12 | return JSON.stringify(log) + "\n"; 13 | }, 14 | }) 15 | : (_) => {}, 16 | }, 17 | filters: {}, 18 | loggers: [ 19 | { 20 | category: "fedify", 21 | lowestLevel: (Deno.env.get("FEDIFY_LOG") as LogLevel | undefined) ?? 22 | "debug", 23 | sinks: ["console", "file"], 24 | }, 25 | { 26 | category: "blog", 27 | lowestLevel: (Deno.env.get("BLOG_LOG") as LogLevel | undefined) ?? 28 | "debug", 29 | sinks: ["console", "file"], 30 | }, 31 | { 32 | category: ["logtape", "meta"], 33 | lowestLevel: "warning", 34 | sinks: ["console", "file"], 35 | }, 36 | ], 37 | }); 38 | -------------------------------------------------------------------------------- /examples/blog/main.ts: -------------------------------------------------------------------------------- 1 | ///The blog is not set up yet.
; 13 | return ( 14 | <> 15 | 16 |24 | 25 | @{blog.handle}@{domain} 26 | {" "} 27 | ·{" "} 28 | {totalFollowers === 1n 29 | ? "1 follower" 30 | : `${totalFollowers} followers`} · {blog.description} 31 |
32 | 33 |40 | {follower.handle} 41 |
42 |45 | {nextCursor == null 46 | ? "No more followers." 47 | : More followers} 48 |
49 | > 50 | ); 51 | } 52 | -------------------------------------------------------------------------------- /examples/blog/routes/users/[handle].ts: -------------------------------------------------------------------------------- 1 | import { Handler } from "$fresh/server.ts"; 2 | import { getBlog } from "../../models/blog.ts"; 3 | 4 | export const handler: Handler = async (_req, ctx) => { 5 | const blog = await getBlog(); 6 | if (blog == null) return await ctx.renderNotFound(); 7 | if (ctx.params.handle !== blog.handle) return await ctx.renderNotFound(); 8 | return Response.redirect(new URL("/", ctx.url), 301); 9 | }; 10 | -------------------------------------------------------------------------------- /examples/blog/static/styles.css: -------------------------------------------------------------------------------- 1 | @import url(https://cdn.jsdelivr.net/npm/@picocss/pico@1/css/pico.min.css); 2 | -------------------------------------------------------------------------------- /examples/cloudflare-workers/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | Fedify on Cloudflare Workers 4 | ============================ 5 | 6 | This project contains an example of Fedify on [Cloudflare Workers]. Although 7 | it can just accept `Follow` and `Undo(Follow)` activities, it sufficiently 8 | shows how to use Fedify on Cloudflare Workers. 9 | 10 | The application code is placed in the *src/index.ts* file, and the most 11 | important part for integration with Cloudflare Workers is the end of the file, 12 | where the `export default` statement is used to export the `fetch()` and 13 | `queue()` methods. 14 | 15 | Here are some important notes about the code: 16 | 17 | - Since `KvNamespace` and `Queue` are not bound to global variables, 18 | but rather passed as an argument to the `fetch()` and `queue()` methods, 19 | you need to instantiate your `Federation` object inside these methods, 20 | rather than at the top level. 21 | 22 | - Since defining a `queue()` method is the only way to consume messages from 23 | the queue in Cloudflare Workers, we need to define it so that the messages 24 | can be manually processed by `Federation.processQueuedTake()` method. 25 | 26 | You can run this example in your local machine using the `pnpm dev` command: 27 | 28 | ~~~~ bash 29 | pnpm install 30 | pnpm dev 31 | ~~~~ 32 | 33 | [Cloudflare Workers]: https://workers.cloudflare.com/ 34 | -------------------------------------------------------------------------------- /examples/cloudflare-workers/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@fedify/cloudflare-workers-example", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "deploy": "wrangler deploy", 7 | "dev": "wrangler dev", 8 | "start": "wrangler dev", 9 | "cf-typegen": "wrangler types" 10 | }, 11 | "devDependencies": { 12 | "typescript": "^5.5.2", 13 | "wrangler": "^4.18.0" 14 | }, 15 | "dependencies": { 16 | "@fedify/fedify": "1.6.1-dev.871" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /examples/cloudflare-workers/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2021", 4 | "lib": ["es2021"], 5 | "jsx": "react-jsx", 6 | "module": "NodeNext", 7 | "moduleResolution": "nodenext", 8 | "resolveJsonModule": true, 9 | "allowJs": true, 10 | "checkJs": false, 11 | "noEmit": true, 12 | "isolatedModules": true, 13 | "allowSyntheticDefaultImports": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "strict": true, 16 | "skipLibCheck": true, 17 | "types": ["./worker-configuration.d.ts"] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /examples/cloudflare-workers/wrangler.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "node_modules/wrangler/config-schema.json", 3 | "name": "fedify-example", 4 | "main": "src/index.ts", 5 | "compatibility_date": "2025-05-31", 6 | "compatibility_flags": ["nodejs_compat"], 7 | "observability": { 8 | "enabled": true 9 | }, 10 | "kv_namespaces": [ 11 | { 12 | "binding": "FEDIFY_KV", 13 | "id": "4623e47dd1eb4d5bb7df86d7f07a4d72" 14 | } 15 | ], 16 | "queues": { 17 | "consumers": [ 18 | { 19 | "queue": "fedify-queue" 20 | } 21 | ], 22 | "producers": [ 23 | { 24 | "binding": "FEDIFY_QUEUE", 25 | "queue": "fedify-queue" 26 | } 27 | ] 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /examples/express/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /examples/express/.nvmrc: -------------------------------------------------------------------------------- 1 | v20.17.0 2 | -------------------------------------------------------------------------------- /examples/express/README.md: -------------------------------------------------------------------------------- 1 | Fedify–Express integration example 2 | ================================== 3 | 4 | This is a simple example of how to integrate Fedify into an [Express] 5 | application. 6 | 7 | [Express]: https://expressjs.com/ 8 | 9 | 10 | Running the example 11 | ------------------- 12 | 13 | 1. Clone the repository: 14 | 15 | ~~~~ sh 16 | git clone https://github.com/fedify-dev/fedify.git 17 | cd fedify/examples/express 18 | ~~~~ 19 | 20 | 2. Install dependencies: 21 | 22 | ~~~~ sh 23 | # optional 24 | nvm use 25 | npm i 26 | ~~~~ 27 | 28 | 3. Start the server: 29 | 30 | ~~~~ sh 31 | npm start & npx @fedify/cli tunnel 8000 32 | ~~~~ 33 | 34 | 4. Open your browser tunneled URL and start interacting with the app. 35 | You can see your handle such as 36 | `@demo@6c10b40c63d9e1ce7da55667ef0ef8b4.serveo.net`. 37 | 38 | 5. Access9 | This small federated server app is a demo of Fedify. The only one thing 10 | it does is to accept follow requests. 11 |
12 |
13 | You can follow this demo app via the below handle:{" "}
14 |
15 | @demo@{headers().get("host")}
16 |
17 |
20 | No followers yet. Try to add a follower using{" "} 21 | 26 | ActivityPub.Academy 27 | . 28 |
29 | ) : ( 30 | <> 31 |This account has the below {relationStore.size} followers:
32 |9 | This small federated server app is a demo of Fedify. The only one thing 10 | it does is to accept follow requests. 11 |
12 |
13 | You can follow this demo app via the below handle:{" "}
14 |
15 | @demo@{(await headers()).get("host")}
16 |
17 |
20 | No followers yet. Try to add a follower using{" "} 21 | 26 | ActivityPub.Academy 27 | 28 | . 29 |
30 | ) : ( 31 | <> 32 |This account has the below {relationStore.size} followers:
33 |