A post named by its page is found as by its id
Pixelfed names one of our posts by the address of its page (/@name/<post id>, the post's url) in its Like, Announce and their Undo, so its likes were dropped as unknown objects. Before an activity is handled, such a reference to a post of ours, as its object or the object of the activity it undoes, is replaced by the post's id. Found by the pasture's new Pixelfed peer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
This commit is contained in:
1 parent
34c0f696af
commit
26dac40720
4 files changed
+72
-2
No files matched your search
@@ -26,10 +26,12 @@ namespace PrivaPub.Federation.Inbox
|
||||
readonly IReadOnlyDictionary<string, IActivityHandler> _handlers;
|
||||
readonly ILogger<InboxProcessor> _logger;
|
||||
readonly IInteractionLedger _ledger;
|
||||
readonly ILocalActorService _localActors;
|
||||
|
||||
public InboxProcessor(IRemoteActorService remoteActors, IEnumerable<IActivityHandler> handlers, ILogger<InboxProcessor> logger,
|
||||
IInteractionLedger ledger = default)
|
||||
IInteractionLedger ledger = default, ILocalActorService localActors = default)
|
||||
{
|
||||
_localActors = localActors;
|
||||
_remoteActors = remoteActors;
|
||||
_handlers = handlers.ToDictionary(h => h.Type, StringComparer.Ordinal);
|
||||
_logger = logger;
|
||||
@@ -65,6 +67,7 @@ namespace PrivaPub.Federation.Inbox
|
||||
Arrival.Current = arrival;
|
||||
try
|
||||
{
|
||||
await LocalReferences.Canonicalise(activity, _localActors?.BaseAddress, token);
|
||||
await handler.Handle(activity, actor, token);
|
||||
}
|
||||
catch (Exception ex) when (ex is not OperationCanceledException)
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
using MongoDB.Entities;
|
||||
|
||||
using PostEntity = PrivaPub.Models.Post.Post;
|
||||
|
||||
namespace PrivaPub.Federation.Inbox
|
||||
{
|
||||
// Some servers name one of our posts by the address of its page instead of its id (Pixelfed likes /@name/<post id>, the
|
||||
// post's `url`). Before an activity is handled, such a reference to a post of ours, as the activity's object or as the
|
||||
// object of the activity it undoes, is replaced by the post's id, so that every handler finds the post the same way.
|
||||
public static partial class LocalReferences
|
||||
{
|
||||
[GeneratedRegex("^/@[^/?#]+/([0-9a-f]{24})$")]
|
||||
private static partial Regex PostPage();
|
||||
|
||||
public static async Task Canonicalise(JsonNode activity, string baseAddress, CancellationToken token)
|
||||
{
|
||||
if (activity is not JsonObject outer || string.IsNullOrEmpty(baseAddress))
|
||||
return;
|
||||
await Replace(outer, baseAddress, token);
|
||||
if (outer["object"] is JsonObject inner)
|
||||
await Replace(inner, baseAddress, token);
|
||||
}
|
||||
|
||||
static async Task Replace(JsonObject node, string baseAddress, CancellationToken token)
|
||||
{
|
||||
if (node["object"] is not JsonValue value || !value.TryGetValue<string>(out var uri) || PostId(uri, baseAddress) is not { } id)
|
||||
return;
|
||||
var post = await DB.Default.Find<PostEntity>().Match(p => p.ID == id && !p.IsFederatedCopy).ExecuteFirstAsync(token);
|
||||
if (post?.ObjectURI is { } canonical)
|
||||
node["object"] = canonical;
|
||||
}
|
||||
|
||||
// the id of the post whose page the address is, on our own site
|
||||
public static string PostId(string uri, string baseAddress) =>
|
||||
uri.StartsWith(baseAddress + "/@", StringComparison.OrdinalIgnoreCase) && PostPage().Match(uri[baseAddress.Length..]) is { Success: true } page
|
||||
? page.Groups[1].Value
|
||||
: default;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user