feat: アカウント削除に対応

This commit is contained in:
usbharu 2023-12-13 16:08:05 +09:00
parent 8a7ff91a95
commit 511288f4bb
11 changed files with 188 additions and 1 deletions

View File

@ -0,0 +1,11 @@
package dev.usbharu.hideout.core.domain.model.deletedActor
import java.time.Instant
data class DeletedActor(
val id: Long,
val name: String,
val domain: String,
val publicKey: String,
val deletedAt: Instant
)

View File

@ -0,0 +1,7 @@
package dev.usbharu.hideout.core.domain.model.deletedActor
interface DeletedActorRepository {
suspend fun save(deletedActor: DeletedActor): DeletedActor
suspend fun delete(deletedActor: DeletedActor)
suspend fun findById(id: Long): DeletedActor
}

View File

@ -28,4 +28,6 @@ interface RelationshipRepository {
* @return 取得された[Relationship] 存在しない場合nullが返ります * @return 取得された[Relationship] 存在しない場合nullが返ります
*/ */
suspend fun findByUserIdAndTargetUserId(actorId: Long, targetActorId: Long): Relationship? suspend fun findByUserIdAndTargetUserId(actorId: Long, targetActorId: Long): Relationship?
suspend fun deleteByActorIdOrTargetActorId(actorId: Long, targetActorId: Long)
} }

View File

@ -50,6 +50,7 @@ class RelationshipRepositoryImpl : RelationshipRepository {
} }
} }
override suspend fun findByUserIdAndTargetUserId(actorId: Long, targetActorId: Long): Relationship? { override suspend fun findByUserIdAndTargetUserId(actorId: Long, targetActorId: Long): Relationship? {
return Relationships.select { return Relationships.select {
(Relationships.actorId eq actorId) (Relationships.actorId eq actorId)
@ -57,6 +58,12 @@ class RelationshipRepositoryImpl : RelationshipRepository {
}.singleOrNull() }.singleOrNull()
?.toRelationships() ?.toRelationships()
} }
override suspend fun deleteByActorIdOrTargetActorId(actorId: Long, targetActorId: Long) {
Relationships.deleteWhere {
Relationships.actorId.eq(actorId).or(Relationships.targetActorId.eq(targetActorId))
}
}
} }
fun ResultRow.toRelationships(): Relationship = Relationship( fun ResultRow.toRelationships(): Relationship = Relationship(

View File

@ -0,0 +1,23 @@
package dev.usbharu.hideout.core.infrastructure.exposedquery
import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException
import dev.usbharu.hideout.core.domain.model.deletedActor.DeletedActor
import dev.usbharu.hideout.core.infrastructure.exposedrepository.DeletedActors
import dev.usbharu.hideout.core.infrastructure.exposedrepository.toDeletedActor
import dev.usbharu.hideout.core.query.DeletedActorQueryService
import dev.usbharu.hideout.util.singleOr
import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.select
import org.springframework.stereotype.Repository
@Repository
class DeletedActorQueryServiceImpl : DeletedActorQueryService {
override suspend fun findByNameAndDomain(name: String, domain: String): DeletedActor {
return DeletedActors
.select { DeletedActors.name eq name and (DeletedActors.domain eq domain) }
.singleOr {
FailedToGetResourcesException("name: $name domain: $domain was not exist or duplicate.", it)
}
.toDeletedActor()
}
}

View File

@ -0,0 +1,71 @@
package dev.usbharu.hideout.core.infrastructure.exposedrepository
import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException
import dev.usbharu.hideout.core.domain.model.deletedActor.DeletedActor
import dev.usbharu.hideout.core.domain.model.deletedActor.DeletedActorRepository
import dev.usbharu.hideout.util.singleOr
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.javatime.timestamp
import org.springframework.stereotype.Repository
@Repository
class DeletedActorRepositoryImpl : DeletedActorRepository {
override suspend fun save(deletedActor: DeletedActor): DeletedActor {
val singleOrNull = DeletedActors.select { DeletedActors.id eq deletedActor.id }.singleOrNull()
if (singleOrNull == null) {
DeletedActors.insert {
it[DeletedActors.id] = deletedActor.id
it[DeletedActors.name] = deletedActor.name
it[DeletedActors.domain] = deletedActor.domain
it[DeletedActors.publicKey] = deletedActor.publicKey
it[DeletedActors.deletedAt] = deletedActor.deletedAt
}
} else {
DeletedActors.update({ DeletedActors.id eq deletedActor.id }) {
it[DeletedActors.name] = deletedActor.name
it[DeletedActors.domain] = deletedActor.domain
it[DeletedActors.publicKey] = deletedActor.publicKey
it[DeletedActors.deletedAt] = deletedActor.deletedAt
}
}
return deletedActor
}
override suspend fun delete(deletedActor: DeletedActor) {
DeletedActors.deleteWhere { DeletedActors.id eq deletedActor.id }
}
override suspend fun findById(id: Long): DeletedActor {
val singleOr = DeletedActors.select { DeletedActors.id eq id }
.singleOr { FailedToGetResourcesException("id: $id was not exist or duplicate", it) }
return deletedActor(singleOr)
}
}
fun ResultRow.toDeletedActor(): DeletedActor = deletedActor(this)
private fun deletedActor(singleOr: ResultRow): DeletedActor {
return DeletedActor(
singleOr[DeletedActors.id],
singleOr[DeletedActors.name],
singleOr[DeletedActors.domain],
singleOr[DeletedActors.publicKey],
singleOr[DeletedActors.deletedAt]
)
}
object DeletedActors : Table("deleted_actors") {
val id = long("id")
val name = varchar("name", 300)
val domain = varchar("domain", 255)
val publicKey = varchar("public_key", 10000).uniqueIndex()
val deletedAt = timestamp("deleted_at")
override val primaryKey: PrimaryKey = PrimaryKey(id)
init {
uniqueIndex(name, domain)
}
}

View File

@ -0,0 +1,7 @@
package dev.usbharu.hideout.core.query
import dev.usbharu.hideout.core.domain.model.deletedActor.DeletedActor
interface DeletedActorQueryService {
suspend fun findByNameAndDomain(name: String, domain: String): DeletedActor
}

View File

@ -41,11 +41,17 @@ class PostServiceImpl(
} }
override suspend fun deleteLocal(post: Post) { override suspend fun deleteLocal(post: Post) {
if (post.delted) {
return
}
reactionRepository.deleteByPostId(post.id) reactionRepository.deleteByPostId(post.id)
postRepository.save(post.delete()) postRepository.save(post.delete())
} }
override suspend fun deleteRemote(post: Post) { override suspend fun deleteRemote(post: Post) {
if (post.delted) {
return
}
reactionRepository.deleteByPostId(post.id) reactionRepository.deleteByPostId(post.id)
postRepository.save(post.delete()) postRepository.save(post.delete())
} }

View File

@ -13,4 +13,8 @@ interface UserService {
suspend fun createRemoteUser(user: RemoteUserCreateDto): Actor suspend fun createRemoteUser(user: RemoteUserCreateDto): Actor
suspend fun updateUser(userId: Long, updateUserDto: UpdateUserDto) suspend fun updateUser(userId: Long, updateUserDto: UpdateUserDto)
suspend fun deleteRemoteActor(actorId: Long)
suspend fun deleteLocalUser(userId: Long)
} }

View File

@ -1,11 +1,17 @@
package dev.usbharu.hideout.core.service.user package dev.usbharu.hideout.core.service.user
import dev.usbharu.hideout.application.config.ApplicationConfig import dev.usbharu.hideout.application.config.ApplicationConfig
import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException
import dev.usbharu.hideout.core.domain.model.actor.Actor import dev.usbharu.hideout.core.domain.model.actor.Actor
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
import dev.usbharu.hideout.core.domain.model.deletedActor.DeletedActor
import dev.usbharu.hideout.core.domain.model.deletedActor.DeletedActorRepository
import dev.usbharu.hideout.core.domain.model.reaction.ReactionRepository
import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
import dev.usbharu.hideout.core.query.ActorQueryService import dev.usbharu.hideout.core.query.ActorQueryService
import dev.usbharu.hideout.core.query.DeletedActorQueryService
import dev.usbharu.hideout.core.service.instance.InstanceService import dev.usbharu.hideout.core.service.instance.InstanceService
import org.jetbrains.exposed.exceptions.ExposedSQLException import org.jetbrains.exposed.exceptions.ExposedSQLException
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
@ -21,7 +27,12 @@ class UserServiceImpl(
private val actorBuilder: Actor.UserBuilder, private val actorBuilder: Actor.UserBuilder,
private val applicationConfig: ApplicationConfig, private val applicationConfig: ApplicationConfig,
private val instanceService: InstanceService, private val instanceService: InstanceService,
private val userDetailRepository: UserDetailRepository private val userDetailRepository: UserDetailRepository,
private val deletedActorRepository: DeletedActorRepository,
private val deletedActorQueryService: DeletedActorQueryService,
private val reactionRepository: ReactionRepository,
private val relationshipRepository: RelationshipRepository
) : ) :
UserService { UserService {
@ -60,6 +71,14 @@ class UserServiceImpl(
@Transactional @Transactional
override suspend fun createRemoteUser(user: RemoteUserCreateDto): Actor { override suspend fun createRemoteUser(user: RemoteUserCreateDto): Actor {
logger.info("START Create New remote user. name: {} url: {}", user.name, user.url) logger.info("START Create New remote user. name: {} url: {}", user.name, user.url)
try {
deletedActorQueryService.findByNameAndDomain(user.name, user.domain)
logger.warn("FAILED Deleted actor. user: ${user.name} domain: ${user.domain}")
throw IllegalStateException("Cannot create Deleted actor.")
} catch (_: FailedToGetResourcesException) {
}
@Suppress("TooGenericExceptionCaught") @Suppress("TooGenericExceptionCaught")
val instance = try { val instance = try {
instanceService.fetchInstance(user.url, user.sharedInbox) instanceService.fetchInstance(user.url, user.sharedInbox)
@ -117,6 +136,26 @@ class UserServiceImpl(
) )
} }
override suspend fun deleteRemoteActor(actorId: Long) {
val actor = actorQueryService.findById(actorId)
val deletedActor = DeletedActor(
actor.id,
actor.name,
actor.domain,
actor.publicKey,
Instant.now()
)
relationshipRepository.deleteByActorIdOrTargetActorId(actorId, actorId)
reactionRepository.deleteByActorId(actorId)
deletedActorRepository.save(deletedActor)
}
override suspend fun deleteLocalUser(userId: Long) {
TODO("Not yet implemented")
}
companion object { companion object {
private val logger = LoggerFactory.getLogger(UserServiceImpl::class.java) private val logger = LoggerFactory.getLogger(UserServiceImpl::class.java)
} }

View File

@ -202,3 +202,13 @@ create table if not exists relationships
insert into actors (id, name, domain, screen_name, description, inbox, outbox, url, public_key, private_key, created_at, insert into actors (id, name, domain, screen_name, description, inbox, outbox, url, public_key, private_key, created_at,
key_id, following, followers, instance, locked) key_id, following, followers, instance, locked)
values (0, 'ghost', '', '', '', '', '', '', '', null, 0, '', '', '', null, true); values (0, 'ghost', '', '', '', '', '', '', '', null, 0, '', '', '', null, true);
create table if not exists deleted_actors
(
id bigint primary key,
"name" varchar(300) not null,
domain varchar(255) not null,
public_key varchar(10000) not null,
deleted_at timestamp not null,
unique ("name", domain)
)