[backend/core] Move import/export to ImportExportService and resolve imported users
This commit is contained in:
parent
bfc36cbc48
commit
137dc0d0e6
4 changed files with 68 additions and 53 deletions
|
@ -19,7 +19,7 @@ namespace Iceshrimp.Backend.Controllers.Web;
|
||||||
[EnableRateLimiting("sliding")]
|
[EnableRateLimiting("sliding")]
|
||||||
[Route("/api/iceshrimp/settings")]
|
[Route("/api/iceshrimp/settings")]
|
||||||
[Produces(MediaTypeNames.Application.Json)]
|
[Produces(MediaTypeNames.Application.Json)]
|
||||||
public class SettingsController(DatabaseContext db, UserService userSvc) : ControllerBase
|
public class SettingsController(DatabaseContext db, ImportExportService importExportSvc) : ControllerBase
|
||||||
{
|
{
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[ProducesResults(HttpStatusCode.OK)]
|
[ProducesResults(HttpStatusCode.OK)]
|
||||||
|
@ -79,7 +79,7 @@ public class SettingsController(DatabaseContext db, UserService userSvc) : Contr
|
||||||
if (followCount < 1)
|
if (followCount < 1)
|
||||||
throw GracefulException.BadRequest("You do not follow any users");
|
throw GracefulException.BadRequest("You do not follow any users");
|
||||||
|
|
||||||
await userSvc.ExportFollowingAsync(user);
|
await importExportSvc.ExportFollowingAsync(user);
|
||||||
|
|
||||||
return Accepted();
|
return Accepted();
|
||||||
}
|
}
|
||||||
|
@ -100,7 +100,7 @@ public class SettingsController(DatabaseContext db, UserService userSvc) : Contr
|
||||||
.Where(fqn => fqn.Contains('@'))
|
.Where(fqn => fqn.Contains('@'))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
await userSvc.ImportFollowingAsync(user, fqns);
|
await importExportSvc.ImportFollowingAsync(user, fqns);
|
||||||
|
|
||||||
return Accepted();
|
return Accepted();
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,6 +65,7 @@ public static class ServiceExtensions
|
||||||
.AddScoped<NotificationService>()
|
.AddScoped<NotificationService>()
|
||||||
.AddScoped<DatabaseMaintenanceService>()
|
.AddScoped<DatabaseMaintenanceService>()
|
||||||
.AddScoped<BiteService>()
|
.AddScoped<BiteService>()
|
||||||
|
.AddScoped<ImportExportService>()
|
||||||
.AddScoped<UserProfileMentionsResolver>()
|
.AddScoped<UserProfileMentionsResolver>()
|
||||||
.AddScoped<AuthorizedFetchMiddleware>()
|
.AddScoped<AuthorizedFetchMiddleware>()
|
||||||
.AddScoped<InboxValidationMiddleware>()
|
.AddScoped<InboxValidationMiddleware>()
|
||||||
|
|
64
Iceshrimp.Backend/Core/Services/ImportExportService.cs
Normal file
64
Iceshrimp.Backend/Core/Services/ImportExportService.cs
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
using System.Text;
|
||||||
|
using Iceshrimp.Backend.Core.Configuration;
|
||||||
|
using Iceshrimp.Backend.Core.Database;
|
||||||
|
using Iceshrimp.Backend.Core.Database.Tables;
|
||||||
|
using Iceshrimp.Backend.Core.Extensions;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using static Iceshrimp.Backend.Core.Federation.ActivityPub.UserResolver;
|
||||||
|
|
||||||
|
namespace Iceshrimp.Backend.Core.Services;
|
||||||
|
|
||||||
|
public class ImportExportService(
|
||||||
|
DatabaseContext db,
|
||||||
|
ILogger<UserService> logger,
|
||||||
|
IOptions<Config.InstanceSection> instance,
|
||||||
|
CacheService cacheSvc,
|
||||||
|
DriveService driveSvc,
|
||||||
|
UserService userSvc,
|
||||||
|
ActivityPub.UserResolver userResolver
|
||||||
|
)
|
||||||
|
{
|
||||||
|
public async Task ExportFollowingAsync(User user)
|
||||||
|
{
|
||||||
|
var followees = await db.Followings
|
||||||
|
.Include(p => p.Followee)
|
||||||
|
.Where(p => p.FollowerId == user.Id)
|
||||||
|
.Select(p => p.Followee)
|
||||||
|
.Where(p => !p.IsDeleted && !p.IsSystemUser && p.MovedToUri == null)
|
||||||
|
.OrderBy(p => p.Host)
|
||||||
|
.ThenBy(p => p.UsernameLower)
|
||||||
|
.Select(p => p.GetFqn(instance.Value.AccountDomain))
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
var stream = new MemoryStream(Encoding.UTF8.GetBytes(string.Join("\n", followees)));
|
||||||
|
|
||||||
|
await driveSvc.StoreFile(stream, user,
|
||||||
|
new DriveFileCreationRequest
|
||||||
|
{
|
||||||
|
Filename = $"following-{DateTime.UtcNow:yyyy-MM-dd-HH-mm-ss}.csv",
|
||||||
|
IsSensitive = false,
|
||||||
|
MimeType = "text/csv"
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ImportFollowingAsync(User user, List<string> fqns)
|
||||||
|
{
|
||||||
|
foreach (var fqn in fqns)
|
||||||
|
{
|
||||||
|
var followee = await userResolver.ResolveAsync($"acct:{fqn}", ResolveFlags.Acct);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await userSvc.FollowUserAsync(user, followee);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
logger.LogWarning("Failed to import follow {followee} for user {follower}: {error}",
|
||||||
|
followee.Id, user.Id, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await QueryableTimelineExtensions.ResetHeuristic(user, cacheSvc);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,6 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using AsyncKeyedLock;
|
using AsyncKeyedLock;
|
||||||
using EntityFramework.Exceptions.Common;
|
using EntityFramework.Exceptions.Common;
|
||||||
|
@ -40,7 +39,6 @@ public class UserService(
|
||||||
QueueService queueSvc,
|
QueueService queueSvc,
|
||||||
EventService eventSvc,
|
EventService eventSvc,
|
||||||
WebFingerService webFingerSvc,
|
WebFingerService webFingerSvc,
|
||||||
CacheService cacheSvc,
|
|
||||||
ActivityPub.FederationControlService fedCtrlSvc
|
ActivityPub.FederationControlService fedCtrlSvc
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
@ -947,54 +945,6 @@ public class UserService(
|
||||||
await db.UserListMembers.Where(p => p.UserList.User == user && p.User == followee).ExecuteDeleteAsync();
|
await db.UserListMembers.Where(p => p.UserList.User == user && p.User == followee).ExecuteDeleteAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ExportFollowingAsync(User user)
|
|
||||||
{
|
|
||||||
var followees = await db.Followings
|
|
||||||
.Include(p => p.Followee)
|
|
||||||
.Where(p => p.FollowerId == user.Id)
|
|
||||||
.Select(p => p.Followee)
|
|
||||||
.Where(p => !p.IsDeleted && !p.IsSystemUser && p.MovedToUri == null)
|
|
||||||
.OrderBy(p => p.Host)
|
|
||||||
.ThenBy(p => p.UsernameLower)
|
|
||||||
.Select(p => p.GetFqn(instance.Value.AccountDomain))
|
|
||||||
.ToListAsync();
|
|
||||||
|
|
||||||
var stream = new MemoryStream(Encoding.UTF8.GetBytes(string.Join("\n", followees)));
|
|
||||||
|
|
||||||
await driveSvc.StoreFile(stream, user,
|
|
||||||
new DriveFileCreationRequest
|
|
||||||
{
|
|
||||||
Filename = $"following-{DateTime.UtcNow:yyyy-MM-dd-HH-mm-ss}.csv",
|
|
||||||
IsSensitive = false,
|
|
||||||
MimeType = "text/csv"
|
|
||||||
}, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task ImportFollowingAsync(User user, List<string> fqns)
|
|
||||||
{
|
|
||||||
foreach (var fqn in fqns.Select(fqn => fqn.Split("@")))
|
|
||||||
{
|
|
||||||
var followee = await db.Users
|
|
||||||
.IncludeCommonProperties()
|
|
||||||
.FirstOrDefaultAsync(p => fqn[0].ToLower() == p.UsernameLower &&
|
|
||||||
fqn[1] == (p.Host ?? instance.Value.AccountDomain));
|
|
||||||
|
|
||||||
if (followee == null) continue;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await FollowUserAsync(user, followee);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
logger.LogWarning("Failed to import follow {followee} for user {follower}: {error}",
|
|
||||||
followee.Id, user.Id, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await QueryableTimelineExtensions.ResetHeuristic(user, cacheSvc);
|
|
||||||
}
|
|
||||||
|
|
||||||
[SuppressMessage("ReSharper", "SuggestBaseTypeForParameter", Justification = "Method only makes sense for users")]
|
[SuppressMessage("ReSharper", "SuggestBaseTypeForParameter", Justification = "Method only makes sense for users")]
|
||||||
private void UpdateUserPinnedNotesInBackground(ASActor actor, User user, bool force = false)
|
private void UpdateUserPinnedNotesInBackground(ASActor actor, User user, bool force = false)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Reference in a new issue