[backend/federation] Fix user profile mention resolution recursion limiter (ISH-33)
This commit is contained in:
parent
c4a0cf528d
commit
3a2e1bd3b2
5 changed files with 33 additions and 14 deletions
|
@ -25,7 +25,6 @@ public static class ServiceExtensions
|
|||
public static void AddServices(this IServiceCollection services)
|
||||
{
|
||||
// Transient = instantiated per request and class
|
||||
services.AddTransient<FollowupTaskService>();
|
||||
|
||||
// Scoped = instantiated per request
|
||||
services
|
||||
|
@ -56,7 +55,8 @@ public static class ServiceExtensions
|
|||
.AddScoped<NoteRenderer>()
|
||||
.AddScoped<UserRenderer>()
|
||||
.AddScoped<NotificationRenderer>()
|
||||
.AddScoped<ActivityPubController>();
|
||||
.AddScoped<ActivityPubController>()
|
||||
.AddScoped<FollowupTaskService>();
|
||||
|
||||
// Singleton = instantiated once across application lifetime
|
||||
services
|
||||
|
|
|
@ -133,15 +133,17 @@ public class UserResolver(
|
|||
}
|
||||
}
|
||||
|
||||
public async Task<User?> ResolveAsyncLimited(string query, Func<bool> limitReached)
|
||||
public async Task<User?> ResolveAsyncLimited(string username, string? host, Func<bool> limitReached)
|
||||
{
|
||||
query = NormalizeQuery(query);
|
||||
var query = $"acct:{username}@{host}";
|
||||
|
||||
// First, let's see if we already know the user
|
||||
var user = await userSvc.GetUserFromQueryAsync(query);
|
||||
if (user != null)
|
||||
return await GetUpdatedUser(user);
|
||||
|
||||
if (host == null) return null;
|
||||
|
||||
// We don't, so we need to run WebFinger
|
||||
var (acct, uri) = await WebFingerAsync(query);
|
||||
|
||||
|
|
|
@ -2,6 +2,10 @@ namespace Iceshrimp.Backend.Core.Services;
|
|||
|
||||
public class FollowupTaskService(IServiceScopeFactory serviceScopeFactory)
|
||||
{
|
||||
public bool IsBackgroundWorker { get; private set; }
|
||||
|
||||
public IServiceProvider? ServiceProvider { get; set; }
|
||||
|
||||
public Task ExecuteTask(string taskName, Func<IServiceProvider, Task> work)
|
||||
{
|
||||
return Task.Run(async () =>
|
||||
|
@ -10,6 +14,9 @@ public class FollowupTaskService(IServiceScopeFactory serviceScopeFactory)
|
|||
try
|
||||
{
|
||||
var provider = scope.ServiceProvider;
|
||||
var instance = provider.GetRequiredService<FollowupTaskService>();
|
||||
instance.IsBackgroundWorker = true;
|
||||
instance.ServiceProvider = ServiceProvider ?? provider;
|
||||
await work(provider);
|
||||
}
|
||||
catch (Exception e)
|
||||
|
|
|
@ -8,11 +8,11 @@ using Microsoft.Extensions.Options;
|
|||
|
||||
namespace Iceshrimp.Backend.Core.Services;
|
||||
|
||||
public class UserProfileMentionsResolver(ActivityPub.UserResolver userResolver, IOptions<Config.InstanceSection> config)
|
||||
public class UserProfileMentionsResolver(ActivityPub.UserResolver userResolver, IOptions<Config.InstanceSection> config, ILogger<UserProfileMentionsResolver> logger)
|
||||
{
|
||||
private int _recursionLimit = 10;
|
||||
|
||||
public async Task<List<Note.MentionedUser>> ResolveMentions(UserProfile.Field[]? fields, string? bio)
|
||||
public async Task<List<Note.MentionedUser>> ResolveMentions(UserProfile.Field[]? fields, string? bio, string? host)
|
||||
{
|
||||
if (fields is not { Length: > 0 } && bio == null) return [];
|
||||
var input = (fields ?? [])
|
||||
|
@ -29,8 +29,12 @@ public class UserProfileMentionsResolver(ActivityPub.UserResolver userResolver,
|
|||
{
|
||||
try
|
||||
{
|
||||
return await userResolver.ResolveAsyncLimited(p.Acct,
|
||||
() => --_recursionLimit >= 0);
|
||||
return await userResolver.ResolveAsyncLimited(p.Username, p.Host ?? host,
|
||||
() =>
|
||||
{
|
||||
logger.LogDebug("Recursion limiter is at: {limit}", _recursionLimit);
|
||||
return _recursionLimit-- <= 0;
|
||||
});
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
@ -41,6 +45,7 @@ public class UserProfileMentionsResolver(ActivityPub.UserResolver userResolver,
|
|||
|
||||
return users.Where(p => p != null)
|
||||
.Cast<User>()
|
||||
.DistinctBy(p => p.Id)
|
||||
.Select(p => new Note.MentionedUser
|
||||
{
|
||||
Host = p.Host,
|
||||
|
|
|
@ -165,7 +165,7 @@ public class UserService(
|
|||
var processPendingDeletes = await ResolveAvatarAndBanner(user, actor);
|
||||
await db.SaveChangesAsync();
|
||||
await processPendingDeletes();
|
||||
UpdateProfileMentionsInBackground(user);
|
||||
await UpdateProfileMentionsInBackground(user);
|
||||
return user;
|
||||
}
|
||||
catch (UniqueConstraintException)
|
||||
|
@ -270,7 +270,7 @@ public class UserService(
|
|||
db.Update(user);
|
||||
await db.SaveChangesAsync();
|
||||
await processPendingDeletes();
|
||||
UpdateProfileMentionsInBackground(user);
|
||||
await UpdateProfileMentionsInBackground(user);
|
||||
return user;
|
||||
}
|
||||
|
||||
|
@ -605,18 +605,23 @@ public class UserService(
|
|||
[SuppressMessage("ReSharper", "EntityFramework.NPlusOne.IncompleteDataQuery", Justification = "Projectables")]
|
||||
[SuppressMessage("ReSharper", "EntityFramework.NPlusOne.IncompleteDataUsage", Justification = "Same as above")]
|
||||
[SuppressMessage("ReSharper", "SuggestBaseTypeForParameter", Justification = "Method only makes sense for users")]
|
||||
private void UpdateProfileMentionsInBackground(User user)
|
||||
private async Task UpdateProfileMentionsInBackground(User user)
|
||||
{
|
||||
_ = followupTaskSvc.ExecuteTask("UpdateProfileMentionsInBackground", async provider =>
|
||||
var task = followupTaskSvc.ExecuteTask("UpdateProfileMentionsInBackground", async provider =>
|
||||
{
|
||||
var bgDbContext = provider.GetRequiredService<DatabaseContext>();
|
||||
var bgMentionsResolver = provider.GetRequiredService<UserProfileMentionsResolver>();
|
||||
var bgMentionsResolver = (followupTaskSvc.ServiceProvider ?? provider)
|
||||
.GetRequiredService<UserProfileMentionsResolver>();
|
||||
var bgUser = await bgDbContext.Users.IncludeCommonProperties().FirstOrDefaultAsync(p => p.Id == user.Id);
|
||||
if (bgUser?.UserProfile == null) return;
|
||||
bgUser.UserProfile.Mentions =
|
||||
await bgMentionsResolver.ResolveMentions(bgUser.UserProfile.Fields, bgUser.UserProfile.Description);
|
||||
await bgMentionsResolver.ResolveMentions(bgUser.UserProfile.Fields, bgUser.UserProfile.Description,
|
||||
bgUser.Host);
|
||||
bgDbContext.Update(bgUser.UserProfile);
|
||||
await bgDbContext.SaveChangesAsync();
|
||||
});
|
||||
|
||||
if (followupTaskSvc.IsBackgroundWorker)
|
||||
await task;
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue