feat: update k-ap dependency to v0.1.8 and enhance middleware for ActivityPub requests

This commit is contained in:
2026-05-28 01:08:45 +02:00
parent f6893b19dc
commit 5ca5ad9561
8 changed files with 80 additions and 18 deletions

View File

@@ -126,11 +126,13 @@ export default async function ProfilePage({ params }: ProfilePageProps) {
const followingCount = localFollowingCount + remoteFollowingCount;
const isFollowing = user.isFollowedByViewer;
const apiDomain = process.env.NEXT_PUBLIC_API_URL
? new URL(process.env.NEXT_PUBLIC_API_URL).hostname
: null;
const fediverseDomain =
process.env.NEXT_PUBLIC_FEDIVERSE_DOMAIN ??
(process.env.NEXT_PUBLIC_API_URL
? new URL(process.env.NEXT_PUBLIC_API_URL).hostname
: null);
const fediverseHandle =
user.local && apiDomain ? `@${user.username}@${apiDomain}` : null;
user.local && fediverseDomain ? `@${user.username}@${fediverseDomain}` : null;
return (
<div id={`profile-page-${user.username}`}>

View File

@@ -1,16 +1,68 @@
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
export function middleware(request: NextRequest) {
const parts = request.nextUrl.pathname.split("/");
const UUID_RE =
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
// /users/@user@instance or /users/%40user%40instance
if (parts.length === 3 && parts[1] === "users") {
const decoded = decodeURIComponent(parts[2]);
if (decoded.startsWith("@") && decoded.indexOf("@", 1) !== -1) {
function isApRequest(accept: string): boolean {
return (
accept.includes("application/activity+json") ||
accept.includes("application/ld+json")
);
}
export async function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
const parts = pathname.split("/");
if (parts.length >= 3 && parts[1] === "users") {
const segment = decodeURIComponent(parts[2]);
const accept = request.headers.get("accept") ?? "";
if (UUID_RE.test(segment)) {
const apiBase =
process.env.NEXT_PUBLIC_SERVER_SIDE_API_URL ?? "http://api:8000";
if (isApRequest(accept)) {
// AP GET request → proxy to backend (actor JSON, outbox, followers, following)
// Inbox POSTs are routed directly via Traefik to preserve the host header for signature verification
const forwardHeaders: Record<string, string> = {};
for (const [key, value] of request.headers.entries()) {
if (key.toLowerCase() !== "host") {
forwardHeaders[key] = value;
}
}
const res = await fetch(`${apiBase}${pathname}`, {
headers: forwardHeaders,
});
// Buffer the body — streaming ReadableStream via NextResponse is unreliable in Edge runtime
const body = await res.text();
return new NextResponse(body, {
status: res.status,
headers: {
"content-type":
res.headers.get("content-type") ?? "application/activity+json",
},
});
}
// Browser request → redirect to the human-readable username URL
const res = await fetch(`${apiBase}/users/${segment}`);
if (res.ok) {
const user = await res.json();
const url = request.nextUrl.clone();
url.pathname = `/users/${user.username}`;
return NextResponse.redirect(url, 301);
}
}
// Remote handle redirect: /users/@user@instance
if (segment.startsWith("@") && segment.indexOf("@", 1) !== -1) {
const url = request.nextUrl.clone();
url.pathname = "/remote-actor";
url.searchParams.set("handle", decoded);
url.searchParams.set("handle", segment);
return NextResponse.rewrite(url);
}
}