feat(federation): Implement Share federation support
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 0s
Build Docker Images / lint (push) Failing after 6s
Build Docker Images / check (push) Failing after 7s
Build Docker Images / tests (push) Failing after 6s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / build (push) Failing after 0s
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Mirror to Codeberg / Mirror (push) Failing after 0s
Nix Build / check (push) Failing after 0s

This commit is contained in:
Jesse Wierzbinski 2025-05-02 12:48:47 +02:00
parent ec69fc2ac0
commit cd12ccd6c1
No known key found for this signature in database
12 changed files with 533 additions and 85 deletions

View file

@ -4,7 +4,7 @@ import { Likes, Notes } from "@versia/kit/tables";
import type { SocketAddress } from "bun";
import { Glob } from "bun";
import chalk from "chalk";
import { eq } from "drizzle-orm";
import { and, eq } from "drizzle-orm";
import { matches } from "ip-matching";
import { isValidationError } from "zod-validation-error";
import { sentry } from "@/sentry";
@ -202,6 +202,9 @@ export class InboxProcessor {
.on(VersiaEntities.User, async (u) => {
await User.fromVersia(u);
})
.on(VersiaEntities.Share, async (s) =>
InboxProcessor.processShare(s),
)
.sort(() => {
throw new ApiError(400, "Unknown entity type");
});
@ -332,6 +335,29 @@ export class InboxProcessor {
});
}
/**
* Handles Share entity processing.
*
* @param {VersiaShare} share - The Share entity to process.
* @returns {Promise<void>}
*/
private static async processShare(
share: VersiaEntities.Share,
): Promise<void> {
const author = await User.resolve(new URL(share.data.author));
const sharedNote = await Note.resolve(new URL(share.data.shared));
if (!author) {
throw new ApiError(404, "Author not found");
}
if (!sharedNote) {
throw new ApiError(404, "Shared Note not found");
}
await author.reblog(sharedNote, "public", new URL(share.data.uri));
}
/**
* Handles Delete entity processing.
*
@ -394,6 +420,37 @@ export class InboxProcessor {
await like.delete();
return;
}
case "pub.versia:shares/Share": {
if (!author) {
throw new ApiError(404, "Author not found");
}
const reblog = await Note.fromSql(
and(eq(Notes.uri, toDelete), eq(Notes.authorId, author.id)),
);
if (!reblog) {
throw new ApiError(
404,
"Share not found or not owned by sender",
);
}
const reblogged = await Note.fromId(
reblog.data.reblogId,
author.id,
);
if (!reblogged) {
throw new ApiError(
404,
"Share not found or not owned by sender",
);
}
await author.unreblog(reblogged);
return;
}
default: {
throw new ApiError(
400,