diff --git a/Sources/VernissageServer/Errors/StatusError.swift b/Sources/VernissageServer/Errors/StatusError.swift index 811e54e6..b02ae6d0 100644 --- a/Sources/VernissageServer/Errors/StatusError.swift +++ b/Sources/VernissageServer/Errors/StatusError.swift @@ -15,6 +15,7 @@ enum StatusError: String, Error { case cannotReblogMentionedStatus case cannotReblogComments case cannotAddCommentWithoutCommentedStatus + case cannotDeleteStatus } extension StatusError: LocalizedTerminateError { @@ -22,6 +23,8 @@ extension StatusError: LocalizedTerminateError { switch self { case .cannotReblogMentionedStatus, .cannotReblogComments: return .forbidden + case .cannotDeleteStatus: + return .internalServerError default: return .badRequest } @@ -35,6 +38,7 @@ extension StatusError: LocalizedTerminateError { case .cannotReblogMentionedStatus: return "Cannot reblog status with mentioned visibility." case .cannotReblogComments: return "Cannot reblog comments." case .cannotAddCommentWithoutCommentedStatus: return "Cannot add comment without commented status." + case .cannotDeleteStatus: return "Error occurred while deleting status." } } diff --git a/Sources/VernissageServer/Services/StatusesService.swift b/Sources/VernissageServer/Services/StatusesService.swift index 603427f2..0a37740f 100644 --- a/Sources/VernissageServer/Services/StatusesService.swift +++ b/Sources/VernissageServer/Services/StatusesService.swift @@ -948,8 +948,18 @@ final class StatusesService: StatusesServiceType { .field(\.$id) .all() + var errorOccurred = false for status in statuses { - try await self.delete(id: status.requireID(), on: context.application.db) + do { + try await self.delete(id: status.requireID(), on: context.application.db) + } catch { + errorOccurred = true + context.logger.error("Failed to delete status: '\(status.stringId() ?? "")': \(error).") + } + } + + if errorOccurred { + throw StatusError.cannotDeleteStatus } } diff --git a/Sources/VernissageServer/Services/UsersService.swift b/Sources/VernissageServer/Services/UsersService.swift index f031f3a3..e69d259d 100644 --- a/Sources/VernissageServer/Services/UsersService.swift +++ b/Sources/VernissageServer/Services/UsersService.swift @@ -470,7 +470,7 @@ final class UsersService: UsersServiceType { func delete(localUser userId: Int64, on context: QueueContext) async throws { let statusesService = context.application.services.statusesService - // We have to delete all user's statuses from local database. + // We have to try to delete all user's statuses from local database. try await statusesService.delete(owner: userId, on: context) // We have to delete all user's follows. @@ -481,7 +481,7 @@ final class UsersService: UsersServiceType { .filter(\.$source.$id == userId) } .all() - let sourceIds = follows.map({ $0.$source.id }) + let sourceIds = follows.map { $0.$source.id } // We have to delete all statuses featured by the user. let featuredStatuses = try await FeaturedStatus.query(on: context.application.db) @@ -493,7 +493,7 @@ final class UsersService: UsersServiceType { .filter(\.$user.$id == userId) .all() - // We have to delete all user's notifications. + // We have to delete all user's notifications and notifications to other users. let notifications = try await Notification.query(on: context.application.db) .group(.or) { group in group @@ -502,6 +502,13 @@ final class UsersService: UsersServiceType { } .all() + // We have to delete notification markers which points to notification to delete. + // Maybe in the future we can figure out something more clever. + let notificationIds = try notifications.map { try $0.requireID() } + let notificationMarkers = try await NotificationMarker.query(on: context.application.db) + .filter(\.$notification.$id ~~ notificationIds) + .all() + // We have to delete all user's reports. let reports = try await Report.query(on: context.application.db) .group(.or) { group in @@ -554,6 +561,7 @@ final class UsersService: UsersServiceType { try await userAliases.delete(on: transaction) try await follows.delete(on: transaction) try await notificationMarker.delete(on: transaction) + try await notificationMarkers.delete(on: transaction) try await notifications.delete(on: transaction) try await reports.delete(on: transaction) try await trendingUser.delete(on: transaction)