diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..cbbd017 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "denoland.vscode-deno" + ] +} \ No newline at end of file diff --git a/deno.json b/deno.json index 62ec572..6f1df61 100644 --- a/deno.json +++ b/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", - "@std/assert": "jsr:@std/assert@1", - "ioredis": "npm:ioredis@^5.4.1" - }, - "unstable": [ - "temporal" - ] -} + "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", + "@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 + } +} \ No newline at end of file diff --git a/deno.lock b/deno.lock index 86beb42..428a561 100644 --- a/deno.lock +++ b/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" ] diff --git a/src/federation.ts b/src/federation.ts new file mode 100644 index 0000000..6b4c6d1 --- /dev/null +++ b/src/federation.ts @@ -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({ 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 diff --git a/src/handler.ts b/src/handler.ts new file mode 100644 index 0000000..da7fb02 --- /dev/null +++ b/src/handler.ts @@ -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( + ``, + { + 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. diff --git a/src/main.ts b/src/main.ts index b6c6041..82c2f3a 100644 --- a/src/main.ts +++ b/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({ - 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, ) diff --git a/src/redis.ts b/src/redis.ts new file mode 100644 index 0000000..deae4f9 --- /dev/null +++ b/src/redis.ts @@ -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, {}) diff --git a/tests/main_test.ts b/tests/main_test.ts index b2e5c11..93299e9 100644 --- a/tests/main_test.ts +++ b/tests/main_test.ts @@ -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) +})