fix: ActivityPubのidが重複した際の処理を修正

This commit is contained in:
usbharu 2023-06-04 00:47:09 +09:00
parent d3d91fc2bc
commit 1a63ae104c
3 changed files with 73 additions and 58 deletions

View File

@ -20,4 +20,5 @@ interface IPostRepository {
userId: Long?): List<Post> userId: Long?): List<Post>
suspend fun findByUserId(idOrNull: Long, since: Instant?, until: Instant?, minId: Long?, maxId: Long?, limit: Int?, userId: Long?): List<Post> suspend fun findByUserId(idOrNull: Long, since: Instant?, until: Instant?, minId: Long?, maxId: Long?, limit: Int?, userId: Long?): List<Post>
suspend fun findByApId(id: String): Post?
} }

View File

@ -89,6 +89,12 @@ class PostRepositoryImpl(database: Database, private val idGenerateService: IdGe
override suspend fun findByUserId(idOrNull: Long, since: Instant?, until: Instant?, minId: Long?, maxId: Long?, limit: Int?, userId: Long?): List<Post> { override suspend fun findByUserId(idOrNull: Long, since: Instant?, until: Instant?, minId: Long?, maxId: Long?, limit: Int?, userId: Long?): List<Post> {
TODO("Not yet implemented") TODO("Not yet implemented")
} }
override suspend fun findByApId(id: String): Post? {
return query {
Posts.select { Posts.apId eq id }.singleOrNull()?.toPost()
}
}
} }
object Posts : Table() { object Posts : Table() {

View File

@ -22,11 +22,11 @@ import java.time.Instant
@Single @Single
class ActivityPubNoteServiceImpl( class ActivityPubNoteServiceImpl(
private val httpClient: HttpClient, private val httpClient: HttpClient,
private val jobQueueParentService: JobQueueParentService, private val jobQueueParentService: JobQueueParentService,
private val userService: IUserService, private val userService: IUserService,
private val postRepository: IPostRepository, private val postRepository: IPostRepository,
private val activityPubUserService: ActivityPubUserService private val activityPubUserService: ActivityPubUserService
) : ActivityPubNoteService { ) : ActivityPubNoteService {
private val logger = LoggerFactory.getLogger(this::class.java) private val logger = LoggerFactory.getLogger(this::class.java)
@ -48,33 +48,44 @@ class ActivityPubNoteServiceImpl(
val actor = props[DeliverPostJob.actor] val actor = props[DeliverPostJob.actor]
val postEntity = Config.configData.objectMapper.readValue<Post>(props[DeliverPostJob.post]) val postEntity = Config.configData.objectMapper.readValue<Post>(props[DeliverPostJob.post])
val note = Note( val note = Note(
name = "Note", name = "Note",
id = postEntity.url, id = postEntity.url,
attributedTo = actor, attributedTo = actor,
content = postEntity.text, content = postEntity.text,
published = Instant.ofEpochMilli(postEntity.createdAt).toString(), published = Instant.ofEpochMilli(postEntity.createdAt).toString(),
to = listOf(public, actor + "/follower") to = listOf(public, actor + "/follower")
) )
val inbox = props[DeliverPostJob.inbox] val inbox = props[DeliverPostJob.inbox]
logger.debug("createNoteJob: actor={}, note={}, inbox={}", actor, postEntity, inbox) logger.debug("createNoteJob: actor={}, note={}, inbox={}", actor, postEntity, inbox)
httpClient.postAp( httpClient.postAp(
urlString = inbox, urlString = inbox,
username = "$actor#pubkey", username = "$actor#pubkey",
jsonLd = Create( jsonLd = Create(
name = "Create Note", name = "Create Note",
`object` = note, `object` = note,
actor = note.attributedTo, actor = note.attributedTo,
id = "${Config.configData.url}/create/note/${postEntity.id}" id = "${Config.configData.url}/create/note/${postEntity.id}"
) )
) )
} }
override suspend fun fetchNote(url: String, targetActor: String?): Note { override suspend fun fetchNote(url: String, targetActor: String?): Note {
val post = postRepository.findByUrl(url) val post = postRepository.findByUrl(url)
if (post != null) { if (post != null) {
val user = userService.findById(post.userId) return postToNote(post)
val reply = post.replyId?.let { postRepository.findOneById(it) } }
return Note( val response = httpClient.getAp(
url,
"$targetActor#pubkey"
)
val note = response.body<Note>()
return note(note, targetActor, url)
}
private suspend fun postToNote(post: Post): Note {
val user = userService.findById(post.userId)
val reply = post.replyId?.let { postRepository.findOneById(it) }
return Note(
name = "Post", name = "Post",
id = post.apId, id = post.apId,
attributedTo = user.url, attributedTo = user.url,
@ -84,38 +95,35 @@ class ActivityPubNoteServiceImpl(
sensitive = post.sensitive, sensitive = post.sensitive,
cc = listOf(public, user.url + "/follower"), cc = listOf(public, user.url + "/follower"),
inReplyTo = reply?.url inReplyTo = reply?.url
)
}
val response = httpClient.getAp(
url,
"$targetActor#pubkey"
) )
val note = response.body<Note>()
return note(note, targetActor, url)
} }
private suspend fun ActivityPubNoteServiceImpl.note( private suspend fun ActivityPubNoteServiceImpl.note(
note: Note, note: Note,
targetActor: String?, targetActor: String?,
url: String url: String
): Note { ): Note {
val findByApId = postRepository.findByApId(url)
if (findByApId != null) {
return postToNote(findByApId)
}
val person = activityPubUserService.fetchPerson( val person = activityPubUserService.fetchPerson(
note.attributedTo ?: throw IllegalActivityPubObjectException("note.attributedTo is null"), note.attributedTo ?: throw IllegalActivityPubObjectException("note.attributedTo is null"),
targetActor targetActor
) )
val user = val user =
userService.findByUrl(person.url ?: throw IllegalActivityPubObjectException("person.url is null")) userService.findByUrl(person.url ?: throw IllegalActivityPubObjectException("person.url is null"))
val visibility = val visibility =
if (note.to.contains(public) && note.cc.contains(public)) { if (note.to.contains(public) && note.cc.contains(public)) {
Visibility.PUBLIC Visibility.PUBLIC
} else if (note.to.find { it.endsWith("/followers") } != null && note.cc.contains(public)) { } else if (note.to.find { it.endsWith("/followers") } != null && note.cc.contains(public)) {
Visibility.UNLISTED Visibility.UNLISTED
} else if (note.to.find { it.endsWith("/followers") } != null) { } else if (note.to.find { it.endsWith("/followers") } != null) {
Visibility.FOLLOWERS Visibility.FOLLOWERS
} else { } else {
Visibility.DIRECT Visibility.DIRECT
} }
val reply = note.inReplyTo?.let { val reply = note.inReplyTo?.let {
fetchNote(it, targetActor) fetchNote(it, targetActor)
@ -123,25 +131,25 @@ class ActivityPubNoteServiceImpl(
} }
postRepository.save( postRepository.save(
Post( Post(
id = postRepository.generateId(), id = postRepository.generateId(),
userId = user.id, userId = user.id,
overview = null, overview = null,
text = note.content.orEmpty(), text = note.content.orEmpty(),
createdAt = Instant.parse(note.published).toEpochMilli(), createdAt = Instant.parse(note.published).toEpochMilli(),
visibility = visibility, visibility = visibility,
url = note.id ?: url, url = note.id ?: url,
repostId = null, repostId = null,
replyId = reply?.id, replyId = reply?.id,
sensitive = note.sensitive, sensitive = note.sensitive,
apId = note.id ?: url, apId = note.id ?: url,
) )
) )
return note return note
} }
override suspend fun fetchNote(note: Note, targetActor: String?): Note = override suspend fun fetchNote(note: Note, targetActor: String?): Note =
note(note, targetActor, note.id ?: throw IllegalArgumentException("note.id is null")) note(note, targetActor, note.id ?: throw IllegalArgumentException("note.id is null"))
companion object { companion object {
val public: String = "https://www.w3.org/ns/activitystreams#Public" val public: String = "https://www.w3.org/ns/activitystreams#Public"