feat: update k-ap dependency to v0.1.8 and enhance middleware for ActivityPub requests
This commit is contained in:
6
Cargo.lock
generated
6
Cargo.lock
generated
@@ -2015,8 +2015,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "k-ap"
|
||||
version = "0.1.7"
|
||||
source = "git+https://git.gabrielkaszewski.dev/GKaszewski/k-ap.git?tag=v0.1.7#699258f830922830df956db8e5dea739ee1642aa"
|
||||
version = "0.1.8"
|
||||
source = "git+https://git.gabrielkaszewski.dev/GKaszewski/k-ap.git?tag=v0.1.8#1949fce620a4d8f6ae9aa88412f5dbf4b7b0f089"
|
||||
dependencies = [
|
||||
"activitypub_federation",
|
||||
"anyhow",
|
||||
@@ -4566,7 +4566,7 @@ version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -47,11 +47,18 @@ services:
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.docker.network=traefik"
|
||||
# Original API subdomain — keep for backwards compat and direct API access
|
||||
- "traefik.http.routers.thoughts-api.rule=Host(`api.thoughts.gabrielkaszewski.dev`)"
|
||||
- "traefik.http.routers.thoughts-api.entrypoints=web,websecure"
|
||||
- "traefik.http.routers.thoughts-api.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.routers.thoughts-api.service=thoughts-api"
|
||||
- "traefik.http.services.thoughts-api.loadbalancer.server.port=8000"
|
||||
# Federation routes on the main domain — higher priority than the frontend catch-all
|
||||
- "traefik.http.routers.thoughts-federation.rule=Host(`thoughts.gabrielkaszewski.dev`) && (PathPrefix(`/.well-known`) || PathPrefix(`/nodeinfo`) || Path(`/inbox`) || (Method(`POST`) && PathPrefix(`/users/`)))"
|
||||
- "traefik.http.routers.thoughts-federation.entrypoints=web,websecure"
|
||||
- "traefik.http.routers.thoughts-federation.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.routers.thoughts-federation.service=thoughts-api"
|
||||
- "traefik.http.routers.thoughts-federation.priority=1000"
|
||||
|
||||
worker:
|
||||
container_name: thoughts-worker
|
||||
@@ -77,6 +84,7 @@ services:
|
||||
environment:
|
||||
NEXT_PUBLIC_SERVER_SIDE_API_URL: http://api:8000
|
||||
NEXT_PUBLIC_API_URL: https://api.thoughts.gabrielkaszewski.dev
|
||||
NEXT_PUBLIC_FEDIVERSE_DOMAIN: thoughts.gabrielkaszewski.dev
|
||||
PORT: 3000
|
||||
HOSTNAME: 0.0.0.0
|
||||
depends_on:
|
||||
|
||||
@@ -4,7 +4,7 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
k-ap = { git = "https://git.gabrielkaszewski.dev/GKaszewski/k-ap.git", tag = "v0.1.7" }
|
||||
k-ap = { git = "https://git.gabrielkaszewski.dev/GKaszewski/k-ap.git", tag = "v0.1.8" }
|
||||
domain = { workspace = true }
|
||||
url = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
|
||||
@@ -4,7 +4,7 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
k-ap = { git = "https://git.gabrielkaszewski.dev/GKaszewski/k-ap.git", tag = "v0.1.7" }
|
||||
k-ap = { git = "https://git.gabrielkaszewski.dev/GKaszewski/k-ap.git", tag = "v0.1.8" }
|
||||
sqlx = { workspace = true }
|
||||
uuid = { workspace = true }
|
||||
chrono = { workspace = true }
|
||||
|
||||
@@ -14,7 +14,7 @@ postgres = { workspace = true }
|
||||
postgres-search = { workspace = true }
|
||||
postgres-federation = { workspace = true }
|
||||
activitypub = { workspace = true }
|
||||
k-ap = { git = "https://git.gabrielkaszewski.dev/GKaszewski/k-ap.git", tag = "v0.1.7" }
|
||||
k-ap = { git = "https://git.gabrielkaszewski.dev/GKaszewski/k-ap.git", tag = "v0.1.8" }
|
||||
nats = { workspace = true }
|
||||
event-transport = { workspace = true }
|
||||
auth = { workspace = true }
|
||||
|
||||
@@ -13,7 +13,7 @@ application = { workspace = true }
|
||||
nats = { workspace = true }
|
||||
event-transport = { workspace = true }
|
||||
event-payload = { workspace = true }
|
||||
k-ap = { git = "https://git.gabrielkaszewski.dev/GKaszewski/k-ap.git", tag = "v0.1.7" }
|
||||
k-ap = { git = "https://git.gabrielkaszewski.dev/GKaszewski/k-ap.git", tag = "v0.1.8" }
|
||||
activitypub = { workspace = true }
|
||||
postgres = { workspace = true }
|
||||
postgres-federation = { workspace = true }
|
||||
|
||||
@@ -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}`}>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user