Support for the basics of federation #1
7 changed files with 145 additions and 87 deletions
53
deno.json
53
deno.json
|
@ -1,22 +1,35 @@
|
||||||
{
|
{
|
||||||
"imports": {
|
"imports": {
|
||||||
"@fedify/fedify": "jsr:@fedify/fedify@^1.0.2",
|
"@fedify/fedify": "jsr:@fedify/fedify@^1.0.2",
|
||||||
"@fedify/redis": "jsr:@fedify/redis@^0.3.0",
|
"@fedify/redis": "jsr:@fedify/redis@^0.3.0",
|
||||||
"@logtape/logtape": "jsr:@logtape/logtape@^0.6.3",
|
"@logtape/logtape": "jsr:@logtape/logtape@^0.6.3",
|
||||||
"@std/assert": "jsr:@std/assert@1",
|
"@std/assert": "jsr:@std/assert@1",
|
||||||
"ioredis": "npm:ioredis@^5.4.1"
|
"@hongminhee/x-forwarded-fetch": "jsr:@hongminhee/x-forwarded-fetch@^0.2.0",
|
||||||
},
|
"@@npm/ioredis": "npm:ioredis@^5.4.1"
|
||||||
"unstable": [
|
},
|
||||||
"temporal"
|
"unstable": [
|
||||||
],
|
"temporal"
|
||||||
"lint": {
|
],
|
||||||
"rules": {
|
"lint": {
|
||||||
"exclude": [
|
"rules": {
|
||||||
"no-implicit-any",
|
"exclude": [
|
||||||
"no-explicit-any",
|
"no-implicit-any",
|
||||||
"no-inner-declarations",
|
"no-explicit-any",
|
||||||
"no-var"
|
"no-inner-declarations",
|
||||||
]
|
"no-var"
|
||||||
}
|
]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"fmt": {
|
||||||
|
"include": [
|
||||||
|
"src/**",
|
||||||
|
"tests/**"
|
||||||
|
],
|
||||||
|
"indentWidth": 4,
|
||||||
|
"lineWidth": 280,
|
||||||
|
"semiColons": false,
|
||||||
|
"proseWrap": "never",
|
||||||
|
"singleQuote": false,
|
||||||
|
"useTabs": true
|
||||||
|
}
|
||||||
}
|
}
|
16
deno.lock
16
deno.lock
|
@ -4,15 +4,14 @@
|
||||||
"specifiers": {
|
"specifiers": {
|
||||||
"jsr:@fedify/fedify@^1.0.2": "jsr:@fedify/fedify@1.0.2",
|
"jsr:@fedify/fedify@^1.0.2": "jsr:@fedify/fedify@1.0.2",
|
||||||
"jsr:@fedify/redis@^0.3.0": "jsr:@fedify/redis@0.3.0",
|
"jsr:@fedify/redis@^0.3.0": "jsr:@fedify/redis@0.3.0",
|
||||||
|
"jsr:@hongminhee/x-forwarded-fetch@^0.2.0": "jsr:@hongminhee/x-forwarded-fetch@0.2.0",
|
||||||
"jsr:@hugoalh/http-header-link@^1.0.2": "jsr:@hugoalh/http-header-link@1.0.2",
|
"jsr:@hugoalh/http-header-link@^1.0.2": "jsr:@hugoalh/http-header-link@1.0.2",
|
||||||
"jsr:@hugoalh/is-string-singleline@1.0.2": "jsr:@hugoalh/is-string-singleline@1.0.2",
|
"jsr:@hugoalh/is-string-singleline@1.0.2": "jsr:@hugoalh/is-string-singleline@1.0.2",
|
||||||
"jsr:@logtape/logtape@^0.6.2": "jsr:@logtape/logtape@0.6.3",
|
"jsr:@logtape/logtape@^0.6.2": "jsr:@logtape/logtape@0.6.3",
|
||||||
"jsr:@logtape/logtape@^0.6.3": "jsr:@logtape/logtape@0.6.3",
|
"jsr:@logtape/logtape@^0.6.3": "jsr:@logtape/logtape@0.6.3",
|
||||||
"jsr:@std/assert@1": "jsr:@std/assert@1.0.6",
|
|
||||||
"jsr:@std/bytes@^1.0.2": "jsr:@std/bytes@1.0.2",
|
"jsr:@std/bytes@^1.0.2": "jsr:@std/bytes@1.0.2",
|
||||||
"jsr:@std/encoding@^1.0.5": "jsr:@std/encoding@1.0.5",
|
"jsr:@std/encoding@^1.0.5": "jsr:@std/encoding@1.0.5",
|
||||||
"jsr:@std/http@^1.0.6": "jsr:@std/http@1.0.8",
|
"jsr:@std/http@^1.0.6": "jsr:@std/http@1.0.8",
|
||||||
"jsr:@std/internal@^1.0.4": "jsr:@std/internal@1.0.4",
|
|
||||||
"jsr:@std/semver@^1.0.3": "jsr:@std/semver@1.0.3",
|
"jsr:@std/semver@^1.0.3": "jsr:@std/semver@1.0.3",
|
||||||
"npm:@phensley/language-tag@^1.9.0": "npm:@phensley/language-tag@1.9.0",
|
"npm:@phensley/language-tag@^1.9.0": "npm:@phensley/language-tag@1.9.0",
|
||||||
"npm:asn1js@^3.0.5": "npm:asn1js@3.0.5",
|
"npm:asn1js@^3.0.5": "npm:asn1js@3.0.5",
|
||||||
|
@ -52,6 +51,9 @@
|
||||||
"jsr:@logtape/logtape@^0.6.3"
|
"jsr:@logtape/logtape@^0.6.3"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"@hongminhee/x-forwarded-fetch@0.2.0": {
|
||||||
|
"integrity": "8a347e061974e07b480e9461c7d84047e12e92c462fe7679a6f0f59b5c5f48d5"
|
||||||
|
},
|
||||||
"@hugoalh/http-header-link@1.0.2": {
|
"@hugoalh/http-header-link@1.0.2": {
|
||||||
"integrity": "1f607e34ac0790a0b0759f89ade294ab3a1d211e46a8dea337eaafa26950205f",
|
"integrity": "1f607e34ac0790a0b0759f89ade294ab3a1d211e46a8dea337eaafa26950205f",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
|
@ -64,12 +66,6 @@
|
||||||
"@logtape/logtape@0.6.3": {
|
"@logtape/logtape@0.6.3": {
|
||||||
"integrity": "64cac3459fddf0455b85d36c8ca3e21764d6b2965c426fb40a0e4a98be436d2b"
|
"integrity": "64cac3459fddf0455b85d36c8ca3e21764d6b2965c426fb40a0e4a98be436d2b"
|
||||||
},
|
},
|
||||||
"@std/assert@1.0.6": {
|
|
||||||
"integrity": "1904c05806a25d94fe791d6d883b685c9e2dcd60e4f9fc30f4fc5cf010c72207",
|
|
||||||
"dependencies": [
|
|
||||||
"jsr:@std/internal@^1.0.4"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"@std/bytes@1.0.2": {
|
"@std/bytes@1.0.2": {
|
||||||
"integrity": "fbdee322bbd8c599a6af186a1603b3355e59a5fb1baa139f8f4c3c9a1b3e3d57"
|
"integrity": "fbdee322bbd8c599a6af186a1603b3355e59a5fb1baa139f8f4c3c9a1b3e3d57"
|
||||||
},
|
},
|
||||||
|
@ -79,9 +75,6 @@
|
||||||
"@std/http@1.0.8": {
|
"@std/http@1.0.8": {
|
||||||
"integrity": "6ea1b2e8d33929967754a3b6d6c6f399ad6647d7bbb5a466c1eaf9b294a6ebcd"
|
"integrity": "6ea1b2e8d33929967754a3b6d6c6f399ad6647d7bbb5a466c1eaf9b294a6ebcd"
|
||||||
},
|
},
|
||||||
"@std/internal@1.0.4": {
|
|
||||||
"integrity": "62e8e4911527e5e4f307741a795c0b0a9e6958d0b3790716ae71ce085f755422"
|
|
||||||
},
|
|
||||||
"@std/semver@1.0.3": {
|
"@std/semver@1.0.3": {
|
||||||
"integrity": "7c139c6076a080eeaa4252c78b95ca5302818d7eafab0470d34cafd9930c13c8"
|
"integrity": "7c139c6076a080eeaa4252c78b95ca5302818d7eafab0470d34cafd9930c13c8"
|
||||||
}
|
}
|
||||||
|
@ -348,6 +341,7 @@
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
"jsr:@fedify/fedify@^1.0.2",
|
"jsr:@fedify/fedify@^1.0.2",
|
||||||
"jsr:@fedify/redis@^0.3.0",
|
"jsr:@fedify/redis@^0.3.0",
|
||||||
|
"jsr:@hongminhee/x-forwarded-fetch@^0.2.0",
|
||||||
"jsr:@logtape/logtape@^0.6.3",
|
"jsr:@logtape/logtape@^0.6.3",
|
||||||
"jsr:@std/assert@1",
|
"jsr:@std/assert@1",
|
||||||
"npm:ioredis@^5.4.1"
|
"npm:ioredis@^5.4.1"
|
||||||
|
|
42
src/federation.ts
Normal file
42
src/federation.ts
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
import { createFederation, Follow, Service } from "@fedify/fedify"
|
||||||
|
import { kv } from "./redis.ts"
|
||||||
|
import { getLogger } from "https://jsr.io/@logtape/logtape/0.6.3/logtape/logger.ts"
|
||||||
|
|
||||||
|
const l = getLogger(["dotino-veloce", "federation"])
|
||||||
|
|
||||||
|
l.debug`Creating federation object...`
|
||||||
|
export const federation = createFederation<void>({ kv })
|
||||||
|
|
||||||
|
l.debug`Creating actor dispatcher...`
|
||||||
|
// deno-lint-ignore require-await
|
||||||
|
async function actorDispatcher(ctx: any, handle: string) {
|
||||||
|
l.debug`Received request for actor ${handle}, handling...`
|
||||||
|
|
||||||
|
if (handle !== "service") {
|
||||||
|
l.debug`No match found for ${handle}, returning null.`
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
l.debug`Determining id of actor ${handle}...`
|
||||||
|
const id = ctx.getActorUri(handle)
|
||||||
|
|
||||||
|
l.info`Returning actor: ${id.href}`
|
||||||
|
return new Service({
|
||||||
|
id,
|
||||||
|
name: "[TEST] Dotino Veloce",
|
||||||
|
summary: "Core account of a Dotino Veloce instance.",
|
||||||
|
preferredUsername: id.href,
|
||||||
|
// Akkoma expects URL to be equal to ID
|
||||||
|
// https://akkoma.dev/AkkomaGang/akkoma/src/commit/f1018867097e6f293d8b2b5b6935f0a7ebf99bd0/lib/pleroma/object/fetcher.ex#L287
|
||||||
|
url: id,
|
||||||
|
inbox: ctx.getInboxUri(handle),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
l.debug`Connecting actor dispatcher to federation object...`
|
||||||
|
federation.setActorDispatcher("/users/{identifier}", actorDispatcher)
|
||||||
|
|
||||||
|
// Akkoma requires inboxes to be setup to display profiles
|
||||||
|
// https://akkoma.dev/AkkomaGang/akkoma/src/commit/f1018867097e6f293d8b2b5b6935f0a7ebf99bd0/lib/pleroma/web/activity_pub/object_validators/user_validator.ex#L72
|
||||||
|
l.debug`Initializing inbox listener...`
|
||||||
|
federation.setInboxListeners("/inbox/{identifier}") // I don't really care about the shared inbox for this project
|
40
src/handler.ts
Normal file
40
src/handler.ts
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import { getLogger } from "@logtape/logtape"
|
||||||
|
import { behindProxy, Fetch } from "@hongminhee/x-forwarded-fetch"
|
||||||
|
import { federation } from "./federation.ts"
|
||||||
|
|
||||||
|
const l = getLogger(["dotino-veloce", "handler"])
|
||||||
|
|
||||||
|
l.debug`Creating Deno handler...`
|
||||||
|
function handler(request: Request, _info: Deno.ServeHandlerInfo) {
|
||||||
|
l.debug`Received a request, processing...`
|
||||||
|
|
||||||
|
const agent = request.headers.get("User-Agent")
|
||||||
|
const requestUrl = new URL(request.url)
|
||||||
|
l.debug`Received request from ${agent} to ${requestUrl.href}`
|
||||||
|
|
||||||
|
// Akkoma expects host-meta to be correctly setup
|
||||||
|
// https://akkoma.dev/AkkomaGang/akkoma/src/commit/f1018867097e6f293d8b2b5b6935f0a7ebf99bd0/lib/pleroma/web/web_finger.ex#L177
|
||||||
|
l.debug`Routing request to: ${requestUrl.pathname}`
|
||||||
|
if (requestUrl.pathname === "/.well-known/host-meta") {
|
||||||
|
l.debug`Intercepting request to inject host-meta for ${requestUrl.origin}`
|
||||||
|
return new Response(
|
||||||
|
`<?xml version="1.0" encoding="UTF-8"?><XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0"><Link type="application/xrd+xml" template="${requestUrl.origin}/.well-known/webfinger?resource={uri}" rel="lrdd" /></XRD>`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/xml",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
l.debug`Delegating request to Federation...`
|
||||||
|
return federation.fetch(
|
||||||
|
request,
|
||||||
|
{
|
||||||
|
contextData: undefined,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
l.debug`Creating proxyied Deno handler...`
|
||||||
|
export const proxyHandler: Deno.ServeHandler = behindProxy(handler as Fetch) // Should be good.
|
60
src/main.ts
60
src/main.ts
|
@ -1,60 +1,18 @@
|
||||||
import {createFederation, Service} from "@fedify/fedify"
|
import { configure, getConsoleSink, getLogger } from "@logtape/logtape"
|
||||||
import {configure, getConsoleSink, getLogger} from "@logtape/logtape"
|
import { proxyHandler } from "./handler.ts"
|
||||||
import {RedisKvStore} from "https://jsr.io/@fedify/redis/0.3.0/src/kv.ts"
|
|
||||||
import {Redis} from "ioredis"
|
|
||||||
|
|
||||||
|
|
||||||
await configure({
|
await configure({
|
||||||
sinks: {console: getConsoleSink()},
|
sinks: { console: getConsoleSink() },
|
||||||
filters: {},
|
filters: {},
|
||||||
loggers: [
|
loggers: [
|
||||||
{category: ["logtape", "meta"], sinks: ["console"], level: "warning"},
|
{ category: ["logtape", "meta"], sinks: ["console"], level: "warning" },
|
||||||
{category: ["fedify"], sinks: ["console"], level: "info"},
|
{ category: ["fedify"], sinks: ["console"], level: "info" },
|
||||||
{category: ["dotino-veloce"], sinks: ["console"], level: "debug"},
|
{ category: ["dotino-veloce"], sinks: ["console"], level: "debug" },
|
||||||
],
|
],
|
||||||
});
|
})
|
||||||
|
|
||||||
const l = getLogger(["dotino-veloce", "main"])
|
const l = getLogger(["dotino-veloce", "main"])
|
||||||
|
|
||||||
l.debug`Creating redis object...`
|
|
||||||
const redis = new Redis({})
|
|
||||||
|
|
||||||
l.debug`Creating federation object...`
|
|
||||||
const kv = new RedisKvStore(redis, {})
|
|
||||||
|
|
||||||
l.debug`Creating federation object...`
|
|
||||||
const federation = createFederation<void>({kv})
|
|
||||||
|
|
||||||
l.debug`Creating actor dispatcher...`
|
|
||||||
const actorDispatcher = async function actorDispatcher(ctx: any, id: string) {
|
|
||||||
if(id !== "service") {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Service({
|
|
||||||
id: ctx.getActorUri(id),
|
|
||||||
name: "[TEST] Dotino Service",
|
|
||||||
summary: "Core account of a Dotino Veloce instance.",
|
|
||||||
preferredUsername: id,
|
|
||||||
url: new URL("/", ctx.url),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
l.debug`Connecting actor dispatcher to federation object...`
|
|
||||||
federation.setActorDispatcher("/users/{identifier}", actorDispatcher)
|
|
||||||
|
|
||||||
l.debug`Creating Deno handler...`
|
|
||||||
const handler: Deno.ServeHandler = function handler(request) {
|
|
||||||
const response = federation.fetch(
|
|
||||||
request,
|
|
||||||
{
|
|
||||||
contextData: undefined,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
return response
|
|
||||||
};
|
|
||||||
|
|
||||||
l.info`Starting server...`
|
l.info`Starting server...`
|
||||||
Deno.serve(
|
Deno.serve(
|
||||||
{
|
{
|
||||||
|
@ -62,5 +20,5 @@ Deno.serve(
|
||||||
onListen: (_localAddr) => {
|
onListen: (_localAddr) => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
handler,
|
proxyHandler,
|
||||||
);
|
)
|
||||||
|
|
11
src/redis.ts
Normal file
11
src/redis.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import { RedisKvStore } from "@fedify/redis"
|
||||||
|
import { getLogger } from "@logtape/logtape"
|
||||||
|
import { Redis } from "@@npm/ioredis"
|
||||||
|
|
||||||
|
const l = getLogger(["dotino-veloce", "redis"])
|
||||||
|
|
||||||
|
l.debug`Creating redis object...`
|
||||||
|
export const redis = new Redis({})
|
||||||
|
|
||||||
|
l.debug`Creating federation object...`
|
||||||
|
export const kv = new RedisKvStore(redis, {})
|
|
@ -1,6 +1,6 @@
|
||||||
import { assertEquals } from "@std/assert";
|
import { assertEquals } from "@std/assert"
|
||||||
import { add } from "../src/main.ts";
|
import { add } from "../src/main.ts"
|
||||||
|
|
||||||
Deno.test(function addTest() {
|
Deno.test(function addTest() {
|
||||||
assertEquals(add(2, 3), 5);
|
assertEquals(add(2, 3), 5)
|
||||||
});
|
})
|
||||||
|
|
Loading…
Reference in a new issue