Support for the basics of federation #1
8 changed files with 159 additions and 51 deletions
5
.vscode/extensions.json
vendored
Normal file
5
.vscode/extensions.json
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"recommendations": [
|
||||
"denoland.vscode-deno"
|
||||
]
|
||||
}
|
29
deno.json
29
deno.json
|
@ -1,14 +1,35 @@
|
|||
{
|
||||
"tasks": {
|
||||
"dev": "deno run --watch src/main.ts"
|
||||
},
|
||||
"imports": {
|
||||
"@fedify/fedify": "jsr:@fedify/fedify@^1.0.2",
|
||||
"@fedify/redis": "jsr:@fedify/redis@^0.3.0",
|
||||
"@logtape/logtape": "jsr:@logtape/logtape@^0.6.3",
|
||||
"@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"
|
||||
],
|
||||
"lint": {
|
||||
"rules": {
|
||||
"exclude": [
|
||||
"no-implicit-any",
|
||||
"no-explicit-any",
|
||||
"no-inner-declarations",
|
||||
"no-var"
|
||||
]
|
||||
}
|
||||
},
|
||||
"fmt": {
|
||||
"include": [
|
||||
"src/**",
|
||||
"tests/**"
|
||||
],
|
||||
"indentWidth": 4,
|
||||
"lineWidth": 280,
|
||||
"semiColons": false,
|
||||
"proseWrap": "never",
|
||||
"singleQuote": false,
|
||||
"useTabs": true
|
||||
}
|
||||
}
|
17
deno.lock
17
deno.lock
|
@ -4,15 +4,14 @@
|
|||
"specifiers": {
|
||||
"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:@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/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.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/encoding@^1.0.5": "jsr:@std/encoding@1.0.5",
|
||||
"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",
|
||||
"npm:@phensley/language-tag@^1.9.0": "npm:@phensley/language-tag@1.9.0",
|
||||
"npm:asn1js@^3.0.5": "npm:asn1js@3.0.5",
|
||||
|
@ -52,6 +51,9 @@
|
|||
"jsr:@logtape/logtape@^0.6.3"
|
||||
]
|
||||
},
|
||||
"@hongminhee/x-forwarded-fetch@0.2.0": {
|
||||
"integrity": "8a347e061974e07b480e9461c7d84047e12e92c462fe7679a6f0f59b5c5f48d5"
|
||||
},
|
||||
"@hugoalh/http-header-link@1.0.2": {
|
||||
"integrity": "1f607e34ac0790a0b0759f89ade294ab3a1d211e46a8dea337eaafa26950205f",
|
||||
"dependencies": [
|
||||
|
@ -64,12 +66,6 @@
|
|||
"@logtape/logtape@0.6.3": {
|
||||
"integrity": "64cac3459fddf0455b85d36c8ca3e21764d6b2965c426fb40a0e4a98be436d2b"
|
||||
},
|
||||
"@std/assert@1.0.6": {
|
||||
"integrity": "1904c05806a25d94fe791d6d883b685c9e2dcd60e4f9fc30f4fc5cf010c72207",
|
||||
"dependencies": [
|
||||
"jsr:@std/internal@^1.0.4"
|
||||
]
|
||||
},
|
||||
"@std/bytes@1.0.2": {
|
||||
"integrity": "fbdee322bbd8c599a6af186a1603b3355e59a5fb1baa139f8f4c3c9a1b3e3d57"
|
||||
},
|
||||
|
@ -79,9 +75,6 @@
|
|||
"@std/http@1.0.8": {
|
||||
"integrity": "6ea1b2e8d33929967754a3b6d6c6f399ad6647d7bbb5a466c1eaf9b294a6ebcd"
|
||||
},
|
||||
"@std/internal@1.0.4": {
|
||||
"integrity": "62e8e4911527e5e4f307741a795c0b0a9e6958d0b3790716ae71ce085f755422"
|
||||
},
|
||||
"@std/semver@1.0.3": {
|
||||
"integrity": "7c139c6076a080eeaa4252c78b95ca5302818d7eafab0470d34cafd9930c13c8"
|
||||
}
|
||||
|
@ -348,6 +341,8 @@
|
|||
"dependencies": [
|
||||
"jsr:@fedify/fedify@^1.0.2",
|
||||
"jsr:@fedify/redis@^0.3.0",
|
||||
"jsr:@hongminhee/x-forwarded-fetch@^0.2.0",
|
||||
"jsr:@logtape/logtape@^0.6.3",
|
||||
"jsr:@std/assert@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.
|
40
src/main.ts
40
src/main.ts
|
@ -1,30 +1,24 @@
|
|||
import {createFederation} from "@fedify/fedify"
|
||||
import {RedisKvStore} from "https://jsr.io/@fedify/redis/0.3.0/src/kv.ts"
|
||||
import {Redis} from "ioredis";
|
||||
import { configure, getConsoleSink, getLogger } from "@logtape/logtape"
|
||||
import { proxyHandler } from "./handler.ts"
|
||||
|
||||
await configure({
|
||||
sinks: { console: getConsoleSink() },
|
||||
filters: {},
|
||||
loggers: [
|
||||
{ category: ["logtape", "meta"], sinks: ["console"], level: "warning" },
|
||||
{ category: ["fedify"], sinks: ["console"], level: "info" },
|
||||
{ category: ["dotino-veloce"], sinks: ["console"], level: "debug" },
|
||||
],
|
||||
})
|
||||
|
||||
const federation = createFederation<void>({
|
||||
kv: new RedisKvStore(
|
||||
new Redis({}),
|
||||
{}
|
||||
),
|
||||
});
|
||||
|
||||
const handler: Deno.ServeHandler = function handler(request) {
|
||||
const response = federation.fetch(
|
||||
request,
|
||||
{
|
||||
contextData: undefined,
|
||||
}
|
||||
)
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
const l = getLogger(["dotino-veloce", "main"])
|
||||
|
||||
l.info`Starting server...`
|
||||
Deno.serve(
|
||||
{
|
||||
port: 8080
|
||||
port: 8080,
|
||||
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 { add } from "../src/main.ts";
|
||||
import { assertEquals } from "@std/assert"
|
||||
import { add } from "../src/main.ts"
|
||||
|
||||
Deno.test(function addTest() {
|
||||
assertEquals(add(2, 3), 5);
|
||||
});
|
||||
assertEquals(add(2, 3), 5)
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue