diff --git a/Iceshrimp.Backend/Controllers/Mastodon/AccountController.cs b/Iceshrimp.Backend/Controllers/Mastodon/AccountController.cs index 1a3ab7c0..a011d5a4 100644 --- a/Iceshrimp.Backend/Controllers/Mastodon/AccountController.cs +++ b/Iceshrimp.Backend/Controllers/Mastodon/AccountController.cs @@ -604,7 +604,8 @@ public class AccountController( [ProducesErrors(HttpStatusCode.NotFound)] public async Task LookupUser([FromQuery] string acct) { - var user = await userResolver.LookupAsync(acct, false) ?? throw GracefulException.RecordNotFound(); + var user = await userResolver.LookupAsync(acct) ?? throw GracefulException.RecordNotFound(); + user = await userResolver.GetUpdatedUser(user); return await userRenderer.RenderAsync(user); } diff --git a/Iceshrimp.Backend/Controllers/Mastodon/SearchController.cs b/Iceshrimp.Backend/Controllers/Mastodon/SearchController.cs index 1f94af91..eecf3b33 100644 --- a/Iceshrimp.Backend/Controllers/Mastodon/SearchController.cs +++ b/Iceshrimp.Backend/Controllers/Mastodon/SearchController.cs @@ -86,7 +86,9 @@ public class SearchController( if (pagination.Offset is not null and not 0) return []; try { - var result = await userResolver.ResolveAsync(search.Query, false); + var result = await userResolver.ResolveAsync(search.Query) + .ContinueWithResult(userResolver.GetUpdatedUser); + return [await userRenderer.RenderAsync(result)]; } catch @@ -118,7 +120,9 @@ public class SearchController( try { - var result = await userResolver.ResolveAsync($"@{username}@{host}", false); + var result = await userResolver.ResolveAsync($"@{username}@{host}") + .ContinueWithResult(userResolver.GetUpdatedUser); + return [await userRenderer.RenderAsync(result)]; } catch diff --git a/Iceshrimp.Backend/Controllers/Web/SearchController.cs b/Iceshrimp.Backend/Controllers/Web/SearchController.cs index 5277e74b..4167a798 100644 --- a/Iceshrimp.Backend/Controllers/Web/SearchController.cs +++ b/Iceshrimp.Backend/Controllers/Web/SearchController.cs @@ -87,7 +87,7 @@ public class SearchController( if (target.StartsWith('@') || target.StartsWith(userPrefixAlt)) { - var hit = await userResolver.ResolveAsyncOrNull(target, false); + var hit = await userResolver.ResolveAsyncOrNull(target); if (hit != null) return new RedirectResponse { TargetUrl = $"/users/{hit.Id}" }; throw GracefulException.NotFound("No result found"); } @@ -125,7 +125,7 @@ public class SearchController( noteHit = await noteSvc.ResolveNoteAsync(target); if (noteHit != null) return new RedirectResponse { TargetUrl = $"/notes/{noteHit.Id}" }; - userHit = await userResolver.ResolveAsyncOrNull(target, false); + userHit = await userResolver.ResolveAsyncOrNull(target); if (userHit != null) return new RedirectResponse { TargetUrl = $"/users/{userHit.Id}" }; throw GracefulException.NotFound("No result found"); diff --git a/Iceshrimp.Backend/Core/Federation/ActivityPub/ActivityHandlerService.cs b/Iceshrimp.Backend/Core/Federation/ActivityPub/ActivityHandlerService.cs index a7550ec9..fc06a46f 100644 --- a/Iceshrimp.Backend/Core/Federation/ActivityPub/ActivityHandlerService.cs +++ b/Iceshrimp.Backend/Core/Federation/ActivityPub/ActivityHandlerService.cs @@ -39,7 +39,7 @@ public class ActivityHandlerService( if (activity.Object == null && activity is not ASBite) throw GracefulException.UnprocessableEntity("Activity object is null"); - var resolvedActor = await userResolver.ResolveAsync(activity.Actor.Id, true); + var resolvedActor = await userResolver.ResolveAsync(activity.Actor.Id); if (authenticatedUserId == null) throw GracefulException.UnprocessableEntity("Refusing to process activity without authenticatedUserId"); @@ -157,7 +157,7 @@ public class ActivityHandlerService( if (activity.Object is not ASActor obj) throw GracefulException.UnprocessableEntity("Follow activity object is invalid"); - var followee = await userResolver.ResolveAsync(obj.Id, true); + var followee = await userResolver.ResolveAsync(obj.Id); if (followee.IsRemoteUser) throw GracefulException.UnprocessableEntity("Cannot process follow for remote followee"); @@ -223,7 +223,7 @@ public class ActivityHandlerService( if (follow is not { Actor: not null }) throw GracefulException.UnprocessableEntity("Refusing to reject object with invalid follow object"); - var resolvedFollower = await userResolver.ResolveAsync(follow.Actor.Id, true); + var resolvedFollower = await userResolver.ResolveAsync(follow.Actor.Id); if (resolvedFollower is not { IsLocalUser: true }) throw GracefulException.UnprocessableEntity("Refusing to reject remote follow"); if (resolvedActor.Uri == null) @@ -355,7 +355,7 @@ public class ActivityHandlerService( Uri = activity.Id, User = resolvedActor, UserHost = resolvedActor.Host, - TargetUser = await userResolver.ResolveAsync(targetActor.Id, true) + TargetUser = await userResolver.ResolveAsync(targetActor.Id) }, ASNote targetNote => new Bite { @@ -385,7 +385,7 @@ public class ActivityHandlerService( Uri = activity.Id, User = resolvedActor, UserHost = resolvedActor.Host, - TargetUser = await userResolver.ResolveAsync(activity.To.Id, true) + TargetUser = await userResolver.ResolveAsync(activity.To.Id) }, _ => throw GracefulException.UnprocessableEntity($"Invalid bite target {target.Id} with type {target.Type}") @@ -479,7 +479,7 @@ public class ActivityHandlerService( private async Task UnfollowAsync(ASActor followeeActor, User follower) { //TODO: send reject? or do we not want to copy that part of the old ap core - var followee = await userResolver.ResolveAsync(followeeActor.Id, true); + var followee = await userResolver.ResolveAsync(followeeActor.Id); await db.FollowRequests.Where(p => p.Follower == follower && p.Followee == followee).ExecuteDeleteAsync(); diff --git a/Iceshrimp.Backend/Core/Federation/ActivityPub/UserResolver.cs b/Iceshrimp.Backend/Core/Federation/ActivityPub/UserResolver.cs index 3ac3d202..06c12132 100644 --- a/Iceshrimp.Backend/Core/Federation/ActivityPub/UserResolver.cs +++ b/Iceshrimp.Backend/Core/Federation/ActivityPub/UserResolver.cs @@ -192,23 +192,18 @@ public class UserResolver( return query; } - public async Task ResolveAsync(string username, string? host, bool skipUpdate) + public async Task ResolveAsync(string username, string? host) { - return host != null - ? await ResolveAsync($"acct:{username}@{host}", skipUpdate) - : await ResolveAsync($"acct:{username}", skipUpdate); + return host != null ? await ResolveAsync($"acct:{username}@{host}") : await ResolveAsync($"acct:{username}"); } - public async Task LookupAsync(string query, bool skipUpdate) + public async Task LookupAsync(string query) { query = NormalizeQuery(query); - var user = await userSvc.GetUserFromQueryAsync(query); - if (user != null) - return skipUpdate ? user : await GetUpdatedUser(user); - return user; + return await userSvc.GetUserFromQueryAsync(query); } - public async Task ResolveAsync(string query, bool skipUpdate) + public async Task ResolveAsync(string query) { query = NormalizeQuery(query); @@ -218,17 +213,15 @@ public class UserResolver( // First, let's see if we already know the user var user = await userSvc.GetUserFromQueryAsync(query); - if (user != null) - return skipUpdate ? user : await GetUpdatedUser(user); + if (user != null) return user; // We don't, so we need to run WebFinger var (acct, uri) = await WebFingerAsync(query); // Check the database again with the new data - if (uri != query) user = await userSvc.GetUserFromQueryAsync(uri); - if (user == null && acct != query) user = await userSvc.GetUserFromQueryAsync(acct); - if (user != null) - return skipUpdate ? user : await GetUpdatedUser(user); + if (uri != query) user = await userSvc.GetUserFromQueryAsync(uri); + if (user == null && acct != query) await userSvc.GetUserFromQueryAsync(acct); + if (user != null) return user; using (await KeyedLocker.LockAsync(uri)) { @@ -237,7 +230,7 @@ public class UserResolver( } } - public async Task ResolveAsync(string query, bool onlyExisting, bool skipUpdate) + public async Task ResolveAsync(string query, bool onlyExisting) { query = NormalizeQuery(query); @@ -247,8 +240,7 @@ public class UserResolver( // First, let's see if we already know the user var user = await userSvc.GetUserFromQueryAsync(query); - if (user != null) - return skipUpdate ? user : await GetUpdatedUser(user); + if (user != null) return user; if (onlyExisting) return null; @@ -257,10 +249,9 @@ public class UserResolver( var (acct, uri) = await WebFingerAsync(query); // Check the database again with the new data - if (uri != query) user = await userSvc.GetUserFromQueryAsync(uri); - if (user == null && acct != query) user = await userSvc.GetUserFromQueryAsync(acct); - if (user != null) - return skipUpdate ? user : await GetUpdatedUser(user); + if (uri != query) user = await userSvc.GetUserFromQueryAsync(uri); + if (user == null && acct != query) await userSvc.GetUserFromQueryAsync(acct); + if (user != null) return user; using (await KeyedLocker.LockAsync(uri)) { @@ -269,7 +260,7 @@ public class UserResolver( } } - public async Task ResolveAsyncOrNull(string username, string? host, bool skipUpdate) + public async Task ResolveAsyncOrNull(string username, string? host) { try { @@ -277,8 +268,7 @@ public class UserResolver( // First, let's see if we already know the user var user = await userSvc.GetUserFromQueryAsync(query); - if (user != null) - return skipUpdate ? user : await GetUpdatedUser(user); + if (user != null) return user; if (host == null) return null; @@ -286,10 +276,9 @@ public class UserResolver( var (acct, uri) = await WebFingerAsync(query); // Check the database again with the new data - if (uri != query) user = await userSvc.GetUserFromQueryAsync(uri); - if (user == null && acct != query) user = await userSvc.GetUserFromQueryAsync(acct); - if (user != null) - return skipUpdate ? user : await GetUpdatedUser(user); + if (uri != query) user = await userSvc.GetUserFromQueryAsync(uri); + if (user == null && acct != query) await userSvc.GetUserFromQueryAsync(acct); + if (user != null) return user; using (await KeyedLocker.LockAsync(uri)) { @@ -303,7 +292,7 @@ public class UserResolver( } } - public async Task ResolveAsyncOrNull(string query, bool skipUpdate) + public async Task ResolveAsyncOrNull(string query) { try { @@ -311,8 +300,7 @@ public class UserResolver( // First, let's see if we already know the user var user = await userSvc.GetUserFromQueryAsync(query); - if (user != null) - return skipUpdate ? user : await GetUpdatedUser(user); + if (user != null) return user; if (query.StartsWith($"https://{config.Value.WebDomain}/")) return null; @@ -322,8 +310,7 @@ public class UserResolver( // Check the database again with the new data if (resolvedUri != query) user = await userSvc.GetUserFromQueryAsync(resolvedUri); if (user == null && acct != query) await userSvc.GetUserFromQueryAsync(acct); - if (user != null) - return skipUpdate ? user : await GetUpdatedUser(user); + if (user != null) return user; using (await KeyedLocker.LockAsync(resolvedUri)) { diff --git a/Iceshrimp.Backend/Core/Middleware/AuthorizedFetchMiddleware.cs b/Iceshrimp.Backend/Core/Middleware/AuthorizedFetchMiddleware.cs index 0fd4afc6..c9ceacb8 100644 --- a/Iceshrimp.Backend/Core/Middleware/AuthorizedFetchMiddleware.cs +++ b/Iceshrimp.Backend/Core/Middleware/AuthorizedFetchMiddleware.cs @@ -66,7 +66,7 @@ public class AuthorizedFetchMiddleware( { try { - var user = await userResolver.ResolveAsync(sig.KeyId, skipUpdate: true).WaitAsync(ct); + var user = await userResolver.ResolveAsync(sig.KeyId).WaitAsync(ct); key = await db.UserPublickeys.Include(p => p.User) .FirstOrDefaultAsync(p => p.User == user, ct); diff --git a/Iceshrimp.Backend/Core/Middleware/InboxValidationMiddleware.cs b/Iceshrimp.Backend/Core/Middleware/InboxValidationMiddleware.cs index 7b8ef788..a94c6f7f 100644 --- a/Iceshrimp.Backend/Core/Middleware/InboxValidationMiddleware.cs +++ b/Iceshrimp.Backend/Core/Middleware/InboxValidationMiddleware.cs @@ -108,7 +108,7 @@ public class InboxValidationMiddleware( { try { - var user = await userResolver.ResolveAsync(sig.KeyId, activity is ASDelete, true).WaitAsync(ct); + var user = await userResolver.ResolveAsync(sig.KeyId, activity is ASDelete).WaitAsync(ct); if (user == null) throw AuthFetchException.NotFound("Delete activity actor is unknown"); key = await db.UserPublickeys.Include(p => p.User) .FirstOrDefaultAsync(p => p.User == user, ct); @@ -185,7 +185,7 @@ public class InboxValidationMiddleware( if (key == null) { - var user = await userResolver.ResolveAsync(activity.Actor.Id, activity is ASDelete, true) + var user = await userResolver.ResolveAsync(activity.Actor.Id, activity is ASDelete) .WaitAsync(ct); if (user == null) throw AuthFetchException.NotFound("Delete activity actor is unknown"); key = await db.UserPublickeys diff --git a/Iceshrimp.Backend/Core/Services/NoteService.cs b/Iceshrimp.Backend/Core/Services/NoteService.cs index 606d0d0f..36f1c5f4 100644 --- a/Iceshrimp.Backend/Core/Services/NoteService.cs +++ b/Iceshrimp.Backend/Core/Services/NoteService.cs @@ -177,7 +177,7 @@ public class NoteService( if (asNote != null) { visibleUserIds = (await asNote.GetRecipients(user) - .Select(p => userResolver.ResolveAsync(p, true)) + .Select(userResolver.ResolveAsync) .AwaitAllNoConcurrencyAsync()) .Select(p => p.Id) .Concat(mentionedUserIds) @@ -480,7 +480,7 @@ public class NoteService( if (asNote != null) { visibleUserIds = (await asNote.GetRecipients(note.User) - .Select(p => userResolver.ResolveAsync(p, true)) + .Select(userResolver.ResolveAsync) .AwaitAllNoConcurrencyAsync()) .Select(p => p.Id) .Concat(visibleUserIds) @@ -907,7 +907,7 @@ public class NoteService( { try { - return await userResolver.ResolveAsync(p.Href!.Id!, true); + return await userResolver.ResolveAsync(p.Href!.Id!); } catch { @@ -930,7 +930,7 @@ public class NoteService( { try { - return await userResolver.ResolveAsync(p.Acct, true); + return await userResolver.ResolveAsync(p.Acct); } catch { @@ -1096,7 +1096,7 @@ public class NoteService( if (res != null && !forceRefresh) return res; } - var actor = await userResolver.ResolveAsync(attrTo.Id, true); + var actor = await userResolver.ResolveAsync(attrTo.Id); using (await KeyedLocker.LockAsync(uri)) { diff --git a/Iceshrimp.Backend/Core/Services/UserProfileMentionsResolver.cs b/Iceshrimp.Backend/Core/Services/UserProfileMentionsResolver.cs index 63b7fcd0..9b0b92f6 100644 --- a/Iceshrimp.Backend/Core/Services/UserProfileMentionsResolver.cs +++ b/Iceshrimp.Backend/Core/Services/UserProfileMentionsResolver.cs @@ -39,12 +39,12 @@ public class UserProfileMentionsResolver(ActivityPub.UserResolver userResolver, var users = await mentionNodes .DistinctBy(p => p.Acct) - .Select(p => userResolver.ResolveAsyncOrNull(p.Username, p.Host?.Value ?? host, true)) + .Select(async p => await userResolver.ResolveAsyncOrNull(p.Username, p.Host?.Value ?? host)) .AwaitAllNoConcurrencyAsync(); users.AddRange(await userUris .Distinct() - .Select(p => userResolver.ResolveAsyncOrNull(p, true)) + .Select(async p => await userResolver.ResolveAsyncOrNull(p)) .AwaitAllNoConcurrencyAsync()); var mentions = users.Where(p => p != null) @@ -78,11 +78,11 @@ public class UserProfileMentionsResolver(ActivityPub.UserResolver userResolver, .Cast() .ToList(); - var nodes = input.SelectMany(MfmParser.Parse); + var nodes = input.SelectMany(p => MfmParser.Parse(p)); var mentionNodes = EnumerateMentions(nodes); var users = await mentionNodes .DistinctBy(p => p.Acct) - .Select(p => userResolver.ResolveAsyncOrNull(p.Username, p.Host?.Value ?? host, true)) + .Select(async p => await userResolver.ResolveAsyncOrNull(p.Username, p.Host?.Value ?? host)) .AwaitAllNoConcurrencyAsync(); return users.Where(p => p != null)