feat: Enable fetching and caching of remote ActivityPub posts in the post details API.
This commit is contained in:
+102
-59
@@ -1,6 +1,7 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { db, posts, users, media } from '@/db';
|
||||
import { db, posts, users, media, remotePosts } from '@/db';
|
||||
import { eq, desc, and } from 'drizzle-orm';
|
||||
import { fetchRemotePost } from '@/lib/activitypub/fetchRemotePost';
|
||||
|
||||
export async function GET(
|
||||
request: Request,
|
||||
@@ -9,7 +10,11 @@ export async function GET(
|
||||
try {
|
||||
const { id } = await params;
|
||||
|
||||
// Fetch the main post
|
||||
const nodeDomain = process.env.NEXT_PUBLIC_NODE_DOMAIN || 'localhost:3000';
|
||||
|
||||
let mainPost: any = null;
|
||||
let replyPosts: any[] = [];
|
||||
|
||||
const post = await db.query.posts.findFirst({
|
||||
where: eq(posts.id, id),
|
||||
with: {
|
||||
@@ -21,65 +26,103 @@ export async function GET(
|
||||
},
|
||||
});
|
||||
|
||||
if (!post) {
|
||||
return NextResponse.json({ error: 'Post not found' }, { status: 404 });
|
||||
if (post) {
|
||||
mainPost = post;
|
||||
|
||||
const replies = await db.query.posts.findMany({
|
||||
where: and(
|
||||
eq(posts.replyToId, id),
|
||||
eq(posts.isRemoved, false)
|
||||
),
|
||||
with: {
|
||||
author: true,
|
||||
media: true,
|
||||
},
|
||||
orderBy: [desc(posts.createdAt)],
|
||||
});
|
||||
|
||||
let allPostIds = [post.id, ...replies.map(r => r.id)];
|
||||
|
||||
try {
|
||||
const { requireAuth } = await import('@/lib/auth');
|
||||
const { likes } = await import('@/db');
|
||||
const { inArray } = await import('drizzle-orm');
|
||||
|
||||
const viewer = await requireAuth();
|
||||
allPostIds = [post.id, ...replies.map(r => r.id)];
|
||||
|
||||
if (allPostIds.length > 0) {
|
||||
const viewerLikes = await db.query.likes.findMany({
|
||||
where: and(
|
||||
eq(likes.userId, viewer.id),
|
||||
inArray(likes.postId, allPostIds)
|
||||
),
|
||||
});
|
||||
const likedPostIds = new Set(viewerLikes.map(l => l.postId));
|
||||
|
||||
const viewerReposts = await db.query.posts.findMany({
|
||||
where: and(
|
||||
eq(posts.userId, viewer.id),
|
||||
inArray(posts.repostOfId, allPostIds)
|
||||
),
|
||||
});
|
||||
const repostedPostIds = new Set(viewerReposts.map(r => r.repostOfId));
|
||||
|
||||
mainPost = {
|
||||
...post,
|
||||
isLiked: likedPostIds.has(post.id),
|
||||
isReposted: repostedPostIds.has(post.id),
|
||||
} as any;
|
||||
|
||||
replyPosts = replies.map(r => ({
|
||||
...r,
|
||||
isLiked: likedPostIds.has(r.id),
|
||||
isReposted: repostedPostIds.has(r.id),
|
||||
})) as any[];
|
||||
}
|
||||
} catch {
|
||||
}
|
||||
} else {
|
||||
const cached = await db.query.remotePosts.findFirst({
|
||||
where: eq(remotePosts.apId, id),
|
||||
});
|
||||
|
||||
if (cached) {
|
||||
mainPost = {
|
||||
id: cached.id,
|
||||
content: cached.content,
|
||||
createdAt: cached.publishedAt.toISOString(),
|
||||
likesCount: 0,
|
||||
repostsCount: 0,
|
||||
repliesCount: 0,
|
||||
author: {
|
||||
id: cached.authorHandle,
|
||||
handle: cached.authorHandle,
|
||||
displayName: cached.authorDisplayName || cached.authorHandle,
|
||||
avatarUrl: cached.authorAvatarUrl,
|
||||
bio: null,
|
||||
isRemote: true,
|
||||
},
|
||||
media: cached.mediaJson ? JSON.parse(cached.mediaJson) : null,
|
||||
linkPreviewUrl: cached.linkPreviewUrl,
|
||||
linkPreviewTitle: cached.linkPreviewTitle,
|
||||
linkPreviewDescription: cached.linkPreviewDescription,
|
||||
linkPreviewImage: cached.linkPreviewImage,
|
||||
isLiked: false,
|
||||
isReposted: false,
|
||||
};
|
||||
} else {
|
||||
const postUrl = `https://${nodeDomain}/posts/${id}`;
|
||||
const result = await fetchRemotePost(postUrl, nodeDomain);
|
||||
|
||||
if (result.post) {
|
||||
mainPost = result.post;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch replies to this post
|
||||
const replies = await db.query.posts.findMany({
|
||||
where: and(
|
||||
eq(posts.replyToId, id),
|
||||
eq(posts.isRemoved, false)
|
||||
),
|
||||
with: {
|
||||
author: true,
|
||||
media: true,
|
||||
},
|
||||
orderBy: [desc(posts.createdAt)],
|
||||
});
|
||||
|
||||
let mainPost = post;
|
||||
let replyPosts = replies;
|
||||
|
||||
try {
|
||||
const { requireAuth } = await import('@/lib/auth');
|
||||
const { likes } = await import('@/db');
|
||||
const { inArray } = await import('drizzle-orm');
|
||||
|
||||
const viewer = await requireAuth();
|
||||
const allPostIds = [post.id, ...replies.map(r => r.id)];
|
||||
|
||||
if (allPostIds.length > 0) {
|
||||
const viewerLikes = await db.query.likes.findMany({
|
||||
where: and(
|
||||
eq(likes.userId, viewer.id),
|
||||
inArray(likes.postId, allPostIds)
|
||||
),
|
||||
});
|
||||
const likedPostIds = new Set(viewerLikes.map(l => l.postId));
|
||||
|
||||
const viewerReposts = await db.query.posts.findMany({
|
||||
where: and(
|
||||
eq(posts.userId, viewer.id),
|
||||
inArray(posts.repostOfId, allPostIds)
|
||||
),
|
||||
});
|
||||
const repostedPostIds = new Set(viewerReposts.map(r => r.repostOfId));
|
||||
|
||||
mainPost = {
|
||||
...post,
|
||||
isLiked: likedPostIds.has(post.id),
|
||||
isReposted: repostedPostIds.has(post.id),
|
||||
} as any;
|
||||
|
||||
replyPosts = replies.map(r => ({
|
||||
...r,
|
||||
isLiked: likedPostIds.has(r.id),
|
||||
isReposted: repostedPostIds.has(r.id),
|
||||
})) as any;
|
||||
}
|
||||
} catch {
|
||||
// Not authenticated or other error, skip flags
|
||||
if (!mainPost) {
|
||||
return NextResponse.json({ error: 'Post not found' }, { status: 404 });
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
|
||||
Reference in New Issue
Block a user