using System.Net; using System.Net.Mime; using Iceshrimp.Backend.Controllers.Shared.Attributes; using Iceshrimp.Backend.Core.Configuration; using Iceshrimp.Backend.Core.Database; using Iceshrimp.Backend.Core.Database.Tables; using Iceshrimp.Backend.Core.Extensions; using Iceshrimp.Backend.Core.Middleware; using Iceshrimp.Backend.Core.Services; using Iceshrimp.Shared.Schemas.Web; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.RateLimiting; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Options; using static Iceshrimp.Backend.Core.Federation.ActivityPub.UserResolver; namespace Iceshrimp.Backend.Controllers.Web; [ApiController] [Authenticate] [Authorize] [EnableRateLimiting("sliding")] [Route("/api/iceshrimp/migration")] [Produces(MediaTypeNames.Application.Json)] public class MigrationController( DatabaseContext db, UserService userSvc, ActivityPub.UserResolver userResolver, IOptions config ) : ControllerBase { [HttpGet] [ProducesResults(HttpStatusCode.OK)] public MigrationSchemas.MigrationStatusResponse GetMigrationStatus() { var user = HttpContext.GetUserOrFail(); return new MigrationSchemas.MigrationStatusResponse { Aliases = user.AlsoKnownAs ?? [], MovedTo = user.MovedToUri }; } [HttpPost("aliases")] [ProducesResults(HttpStatusCode.OK)] [ProducesErrors(HttpStatusCode.BadRequest, HttpStatusCode.NotFound)] public async Task AddAlias(MigrationSchemas.MigrationRequest rq) { var user = HttpContext.GetUserOrFail(); User? aliasUser = null; if (rq.UserId is not null) aliasUser = await db.Users.IncludeCommonProperties().Where(p => p.Id == rq.UserId).FirstOrDefaultAsync(); if (rq.UserUri is not null) aliasUser ??= await userResolver.ResolveOrNullAsync(rq.UserUri, EnforceUriFlags); if (aliasUser is null) throw GracefulException.NotFound("Alias user not found or not specified"); await userSvc.AddAliasAsync(user, aliasUser); } [HttpDelete("aliases")] [ProducesResults(HttpStatusCode.OK)] [ProducesErrors(HttpStatusCode.NotFound)] public async Task RemoveAlias(MigrationSchemas.MigrationRequest rq) { var user = HttpContext.GetUserOrFail(); var aliasUri = rq.UserUri; if (rq.UserId is not null) { aliasUri ??= await db.Users.IncludeCommonProperties() .Where(p => p.Id == rq.UserId) .FirstOrDefaultAsync() .ContinueWithResult(p => p is null ? null : p.Uri ?? p.GetPublicUri(config.Value)); } if (aliasUri is null) throw GracefulException.NotFound("Alias user not found"); await userSvc.RemoveAliasAsync(user, aliasUri); } [HttpPost("move")] [ProducesResults(HttpStatusCode.OK)] [ProducesErrors(HttpStatusCode.BadRequest, HttpStatusCode.NotFound)] public async Task MoveTo(MigrationSchemas.MigrationRequest rq) { var user = HttpContext.GetUserOrFail(); User? targetUser = null; if (rq.UserId is not null) targetUser = await db.Users.IncludeCommonProperties().Where(p => p.Id == rq.UserId).FirstOrDefaultAsync(); if (rq.UserUri is not null) targetUser ??= await userResolver.ResolveOrNullAsync(rq.UserUri, EnforceUriFlags); if (targetUser is null) throw GracefulException.NotFound("Target user not found"); await userSvc.MoveToUserAsync(user, targetUser); } [HttpDelete("move")] [ProducesResults(HttpStatusCode.OK)] public async Task UndoMove() { var user = HttpContext.GetUserOrFail(); await userSvc.UndoMoveAsync(user); } }