From 53b26ecf8f0629fea39b0828e744c8064d51d03a Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sat, 18 Nov 2023 00:35:52 +0900 Subject: [PATCH 01/12] =?UTF-8?q?feat:=20Instance=E3=81=AE=E3=82=A8?= =?UTF-8?q?=E3=83=B3=E3=83=86=E3=82=A3=E3=83=86=E3=82=A3=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/domain/model/instance/Instance.kt | 15 +++++++++++++++ .../domain/model/instance/InstanceRepository.kt | 7 +++++++ 2 files changed, 22 insertions(+) create mode 100644 src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Instance.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceRepository.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Instance.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Instance.kt new file mode 100644 index 00000000..1753ec00 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Instance.kt @@ -0,0 +1,15 @@ +package dev.usbharu.hideout.core.domain.model.instance + +data class Instance( + val id: Long, + val name: String, + val description: String, + val url: String, + val iconUrl: String, + val sharedInbox: String, + val software: String, + val version: String, + val isBlocked: Boolean, + val isMuting: Boolean, + val moderationNote: String +) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceRepository.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceRepository.kt new file mode 100644 index 00000000..41c35d3d --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceRepository.kt @@ -0,0 +1,7 @@ +package dev.usbharu.hideout.core.domain.model.instance + +interface InstanceRepository { + suspend fun save(instance: Instance): Instance + suspend fun findById(id: Long): Instance + suspend fun delete(instance: Instance) +} From a29cf62cfba0fb7811a68044c4f0c14b7597c4bb Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sat, 18 Nov 2023 00:51:57 +0900 Subject: [PATCH 02/12] =?UTF-8?q?feat:=20Instance=E3=81=AE=E3=83=AA?= =?UTF-8?q?=E3=83=9D=E3=82=B8=E3=83=88=E3=83=AA=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/domain/model/instance/Instance.kt | 7 +- .../InstanceRepositoryImpl.kt | 88 +++++++++++++++++++ 2 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/InstanceRepositoryImpl.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Instance.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Instance.kt index 1753ec00..f0599b7f 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Instance.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Instance.kt @@ -1,5 +1,7 @@ package dev.usbharu.hideout.core.domain.model.instance +import java.time.Instant + data class Instance( val id: Long, val name: String, @@ -10,6 +12,7 @@ data class Instance( val software: String, val version: String, val isBlocked: Boolean, - val isMuting: Boolean, - val moderationNote: String + val isMuted: Boolean, + val moderationNote: String, + val createdAt: Instant ) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/InstanceRepositoryImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/InstanceRepositoryImpl.kt new file mode 100644 index 00000000..642fd189 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/InstanceRepositoryImpl.kt @@ -0,0 +1,88 @@ +package dev.usbharu.hideout.core.infrastructure.exposedrepository + +import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException +import dev.usbharu.hideout.core.domain.model.instance.InstanceRepository +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 +import dev.usbharu.hideout.core.domain.model.instance.Instance as InstanceEntity + +@Repository +class InstanceRepositoryImpl : InstanceRepository { + override suspend fun save(instance: InstanceEntity): InstanceEntity { + if (Instance.select { Instance.id.eq(instance.id) }.firstOrNull() == null) { + Instance.insert { + it[id] = instance.id + it[name] = instance.name + it[description] = instance.description + it[url] = instance.url + it[iconUrl] = instance.iconUrl + it[sharedInbox] = instance.sharedInbox + it[software] = instance.software + it[version] = instance.version + it[isBlocked] = instance.isBlocked + it[isMuted] = instance.isMuted + it[moderationNote] = instance.moderationNote + it[createdAt] = instance.createdAt + } + } else { + Instance.update({ Instance.id eq instance.id }) { + it[name] = instance.name + it[description] = instance.description + it[url] = instance.url + it[iconUrl] = instance.iconUrl + it[sharedInbox] = instance.sharedInbox + it[software] = instance.software + it[version] = instance.version + it[isBlocked] = instance.isBlocked + it[isMuted] = instance.isMuted + it[moderationNote] = instance.moderationNote + it[createdAt] = instance.createdAt + } + } + return instance + } + + override suspend fun findById(id: Long): InstanceEntity { + return Instance.select { Instance.id eq id } + .singleOr { FailedToGetResourcesException("id: $id doesn't exist.") }.toInstance() + } + + override suspend fun delete(instance: InstanceEntity) { + Instance.deleteWhere { Instance.id eq instance.id } + } +} + +fun ResultRow.toInstance(): InstanceEntity { + return InstanceEntity( + id = this[Instance.id], + name = this[Instance.name], + description = this[Instance.description], + url = this[Instance.url], + iconUrl = this[Instance.iconUrl], + sharedInbox = this[Instance.sharedInbox], + software = this[Instance.software], + version = this[Instance.version], + isBlocked = this[Instance.isBlocked], + isMuted = this[Instance.isMuted], + moderationNote = this[Instance.moderationNote], + createdAt = this[Instance.createdAt] + ) +} + +object Instance : Table("instance") { + val id = long("id") + val name = varchar("name", 1000) + val description = varchar("description", 5000) + val url = varchar("url", 255) + val iconUrl = varchar("icon_url", 255) + val sharedInbox = varchar("shared_inbox", 255) + val software = varchar("software", 255) + val version = varchar("version", 255) + val isBlocked = bool("is_blocked") + val isMuted = bool("is_muted") + val moderationNote = varchar("moderation_note", 10000) + val createdAt = timestamp("created_at") +} From 4aa1970c763432cf903c3344906da5639eaec8c3 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sat, 18 Nov 2023 10:39:37 +0900 Subject: [PATCH 03/12] =?UTF-8?q?feat:=20InstanceService=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/domain/model/instance/Instance.kt | 2 +- .../model/instance/InstanceRepository.kt | 1 + .../InstanceRepositoryImpl.kt | 7 ++-- .../service/instance/InstanceCreateDto.kt | 11 +++++++ .../core/service/instance/InstanceService.kt | 33 +++++++++++++++++++ 5 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceCreateDto.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceService.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Instance.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Instance.kt index f0599b7f..c777dcdc 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Instance.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Instance.kt @@ -8,7 +8,7 @@ data class Instance( val description: String, val url: String, val iconUrl: String, - val sharedInbox: String, + val sharedInbox: String?, val software: String, val version: String, val isBlocked: Boolean, diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceRepository.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceRepository.kt index 41c35d3d..35b6026e 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceRepository.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/InstanceRepository.kt @@ -1,6 +1,7 @@ package dev.usbharu.hideout.core.domain.model.instance interface InstanceRepository { + suspend fun generateId(): Long suspend fun save(instance: Instance): Instance suspend fun findById(id: Long): Instance suspend fun delete(instance: Instance) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/InstanceRepositoryImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/InstanceRepositoryImpl.kt index 642fd189..a62ac486 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/InstanceRepositoryImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/InstanceRepositoryImpl.kt @@ -1,5 +1,6 @@ package dev.usbharu.hideout.core.infrastructure.exposedrepository +import dev.usbharu.hideout.application.service.id.IdGenerateService import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException import dev.usbharu.hideout.core.domain.model.instance.InstanceRepository import dev.usbharu.hideout.util.singleOr @@ -10,7 +11,9 @@ import org.springframework.stereotype.Repository import dev.usbharu.hideout.core.domain.model.instance.Instance as InstanceEntity @Repository -class InstanceRepositoryImpl : InstanceRepository { +class InstanceRepositoryImpl(private val idGenerateService: IdGenerateService) : InstanceRepository { + override suspend fun generateId(): Long = idGenerateService.generateId() + override suspend fun save(instance: InstanceEntity): InstanceEntity { if (Instance.select { Instance.id.eq(instance.id) }.firstOrNull() == null) { Instance.insert { @@ -78,7 +81,7 @@ object Instance : Table("instance") { val description = varchar("description", 5000) val url = varchar("url", 255) val iconUrl = varchar("icon_url", 255) - val sharedInbox = varchar("shared_inbox", 255) + val sharedInbox = varchar("shared_inbox", 255).nullable() val software = varchar("software", 255) val version = varchar("version", 255) val isBlocked = bool("is_blocked") diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceCreateDto.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceCreateDto.kt new file mode 100644 index 00000000..d5345cf0 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceCreateDto.kt @@ -0,0 +1,11 @@ +package dev.usbharu.hideout.core.service.instance + +data class InstanceCreateDto( + val name: String?, + val description: String?, + val url: String, + val iconUrl: String, + val sharedInbox: String?, + val software: String?, + val version: String?, +) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceService.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceService.kt new file mode 100644 index 00000000..afa182af --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceService.kt @@ -0,0 +1,33 @@ +package dev.usbharu.hideout.core.service.instance + +import dev.usbharu.hideout.core.domain.model.instance.Instance +import dev.usbharu.hideout.core.domain.model.instance.InstanceRepository +import org.springframework.stereotype.Service +import java.time.Instant + +interface InstanceService { + suspend fun createNewInstance(instanceCreateDto: InstanceCreateDto): Instance +} + + +@Service +class InstanceServiceImpl(private val instanceRepository: InstanceRepository) : InstanceService { + override suspend fun createNewInstance(instanceCreateDto: InstanceCreateDto): Instance { + val instance = Instance( + instanceRepository.generateId(), + instanceCreateDto.name ?: instanceCreateDto.url, + instanceCreateDto.description ?: "", + instanceCreateDto.url, + instanceCreateDto.iconUrl, + instanceCreateDto.sharedInbox, + instanceCreateDto.software ?: "unknown", + instanceCreateDto.version ?: "unknown", + false, + false, + "", + Instant.now() + ) + instanceRepository.save(instance) + return instance + } +} From b8756b40a6d13f7cbb0391ef71972bc636b3bad9 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sat, 18 Nov 2023 11:05:02 +0900 Subject: [PATCH 04/12] =?UTF-8?q?feat:=20=E6=B1=8E=E7=94=A8ResourceResolve?= =?UTF-8?q?r=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/service/resource/CacheManager.kt | 6 +++ .../service/resource/InMemoryCacheManager.kt | 49 +++++++++++++++++++ .../service/resource/KtorResolveResponse.kt | 14 ++++++ .../resource/KtorResourceResolveService.kt | 24 +++++++++ .../core/service/resource/ResolveResponse.kt | 10 ++++ .../resource/ResourceResolveService.kt | 5 ++ 6 files changed, 108 insertions(+) create mode 100644 src/main/kotlin/dev/usbharu/hideout/core/service/resource/CacheManager.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/core/service/resource/InMemoryCacheManager.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/core/service/resource/KtorResolveResponse.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/core/service/resource/KtorResourceResolveService.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/core/service/resource/ResolveResponse.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/core/service/resource/ResourceResolveService.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/resource/CacheManager.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/resource/CacheManager.kt new file mode 100644 index 00000000..44dfd2a6 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/resource/CacheManager.kt @@ -0,0 +1,6 @@ +package dev.usbharu.hideout.core.service.resource + +interface CacheManager { + suspend fun putCache(key: String, block: suspend () -> ResolveResponse) + suspend fun getOrWait(key: String): ResolveResponse +} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/resource/InMemoryCacheManager.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/resource/InMemoryCacheManager.kt new file mode 100644 index 00000000..b48fadd7 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/resource/InMemoryCacheManager.kt @@ -0,0 +1,49 @@ +package dev.usbharu.hideout.core.service.resource + +import dev.usbharu.hideout.util.LruCache +import kotlinx.coroutines.delay +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import org.springframework.stereotype.Service +import java.time.Instant + +@Service +class InMemoryCacheManager : CacheManager { + private val cacheKey = LruCache(15) + private val valueStore = mutableMapOf() + private val keyMutex = Mutex() + + override suspend fun putCache(key: String, block: suspend () -> ResolveResponse) { + val needRunBlock: Boolean + keyMutex.withLock { + cacheKey.filter { Instant.ofEpochMilli(it.value).plusSeconds(300) <= Instant.now() } + + val cached = cacheKey.get(key) + if (cached == null) { + needRunBlock = true + cacheKey[key] = Instant.now().toEpochMilli() + + valueStore.remove(key) + } else { + needRunBlock = false + } + } + if (needRunBlock) { + val processed = block() + + if (cacheKey.containsKey(key)) { + valueStore[key] = processed + } + } + } + + override suspend fun getOrWait(key: String): ResolveResponse { + while (valueStore.contains(key).not()) { + if (cacheKey.containsKey(key).not()) { + throw IllegalStateException("Invalid cache key.") + } + delay(1) + } + return valueStore.getValue(key) + } +} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/resource/KtorResolveResponse.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/resource/KtorResolveResponse.kt new file mode 100644 index 00000000..8261d8dc --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/resource/KtorResolveResponse.kt @@ -0,0 +1,14 @@ +package dev.usbharu.hideout.core.service.resource + +import io.ktor.client.statement.* +import io.ktor.util.* +import io.ktor.utils.io.jvm.javaio.* +import java.io.InputStream + +class KtorResolveResponse(val ktorHttpResponse: HttpResponse) : ResolveResponse { + + override suspend fun body(): InputStream = ktorHttpResponse.bodyAsChannel().toInputStream() + override suspend fun header(): Map> = ktorHttpResponse.headers.toMap() + override suspend fun status(): Int = ktorHttpResponse.status.value + override suspend fun statusMessage(): String = ktorHttpResponse.status.description +} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/resource/KtorResourceResolveService.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/resource/KtorResourceResolveService.kt new file mode 100644 index 00000000..4cd99bc8 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/resource/KtorResourceResolveService.kt @@ -0,0 +1,24 @@ +package dev.usbharu.hideout.core.service.resource + +import io.ktor.client.* +import io.ktor.client.request.* +import org.springframework.stereotype.Service + +@Service +open class KtorResourceResolveService(private val httpClient: HttpClient, private val cacheManager: CacheManager) : + ResourceResolveService { + override suspend fun resolve(url: String): ResolveResponse { + cacheManager.putCache(getCacheKey(url)) { + runResolve(url) + } + return cacheManager.getOrWait(getCacheKey(url)) + } + + protected suspend fun runResolve(url: String): ResolveResponse { + val httpResponse = httpClient.get(url) + + return KtorResolveResponse(httpResponse) + } + + protected suspend fun getCacheKey(url: String) = url +} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/resource/ResolveResponse.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/resource/ResolveResponse.kt new file mode 100644 index 00000000..bcfb0bcb --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/resource/ResolveResponse.kt @@ -0,0 +1,10 @@ +package dev.usbharu.hideout.core.service.resource + +import java.io.InputStream + +interface ResolveResponse { + suspend fun body(): InputStream + suspend fun header(): Map> + suspend fun status(): Int + suspend fun statusMessage(): String +} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/resource/ResourceResolveService.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/resource/ResourceResolveService.kt new file mode 100644 index 00000000..b2229b30 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/resource/ResourceResolveService.kt @@ -0,0 +1,5 @@ +package dev.usbharu.hideout.core.service.resource + +interface ResourceResolveService { + suspend fun resolve(url: String): ResolveResponse +} From 84356161a8bfece2cf1fda26736675dc35efdad2 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sat, 18 Nov 2023 11:37:51 +0900 Subject: [PATCH 05/12] =?UTF-8?q?feat:=20=E6=96=B0=E8=A6=8F=E3=83=A6?= =?UTF-8?q?=E3=83=BC=E3=82=B6=E3=83=BC=E4=BD=9C=E6=88=90=E6=99=82=E3=81=AB?= =?UTF-8?q?Instance=E3=82=92=E5=8F=96=E5=BE=97=E3=81=99=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/service/instance/InstanceService.kt | 49 ++++++++++++++++++- .../service/resource/KtorResolveResponse.kt | 17 +++++++ .../core/service/resource/ResolveResponse.kt | 2 + .../core/service/user/UserServiceImpl.kt | 6 ++- 4 files changed, 72 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceService.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceService.kt index afa182af..bb4a2a0c 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceService.kt @@ -1,17 +1,64 @@ package dev.usbharu.hideout.core.service.instance +import com.fasterxml.jackson.databind.ObjectMapper +import dev.usbharu.hideout.activitypub.domain.model.nodeinfo.Nodeinfo +import dev.usbharu.hideout.activitypub.domain.model.nodeinfo.Nodeinfo2_0 import dev.usbharu.hideout.core.domain.model.instance.Instance import dev.usbharu.hideout.core.domain.model.instance.InstanceRepository +import dev.usbharu.hideout.core.service.resource.ResourceResolveService import org.springframework.stereotype.Service +import java.net.URL import java.time.Instant interface InstanceService { + suspend fun fetchInstance(url: String): Instance suspend fun createNewInstance(instanceCreateDto: InstanceCreateDto): Instance } @Service -class InstanceServiceImpl(private val instanceRepository: InstanceRepository) : InstanceService { +class InstanceServiceImpl( + private val instanceRepository: InstanceRepository, + private val resourceResolveService: ResourceResolveService, + private val objectMapper: ObjectMapper +) : InstanceService { + override suspend fun fetchInstance(url: String): Instance { + val u = URL(url) + val resolveInstanceUrl = u.protocol + "://" + u.host + val nodeinfoJson = resourceResolveService.resolve("$resolveInstanceUrl/.well-known/nodeinfo").bodyAsText() + val nodeinfo = objectMapper.readValue(nodeinfoJson, Nodeinfo::class.java) + val nodeinfoPathMap = nodeinfo.links.map { it.rel to it.href }.toMap() + + + for ((key, value) in nodeinfoPathMap) { + when (key) { + "http://nodeinfo.diaspora.software/ns/schema/2.0" -> { + val nodeinfo20 = objectMapper.readValue( + resourceResolveService.resolve(value).bodyAsText(), + Nodeinfo2_0::class.java + ) + + val instanceCreateDto = InstanceCreateDto( + nodeinfo20.metadata.nodeName, + nodeinfo20.metadata.nodeDescription, + resolveInstanceUrl, + resolveInstanceUrl + "/favicon.ico", + null, + nodeinfo20.software.name, + nodeinfo20.software.version + ) + return createNewInstance(instanceCreateDto) + } + + else -> { + TODO() + } + } + } + + throw IllegalStateException("Nodeinfo aren't found.") + } + override suspend fun createNewInstance(instanceCreateDto: InstanceCreateDto): Instance { val instance = Instance( instanceRepository.generateId(), diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/resource/KtorResolveResponse.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/resource/KtorResolveResponse.kt index 8261d8dc..3a5e2ad1 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/resource/KtorResolveResponse.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/resource/KtorResolveResponse.kt @@ -7,7 +7,24 @@ import java.io.InputStream class KtorResolveResponse(val ktorHttpResponse: HttpResponse) : ResolveResponse { + private lateinit var _bodyAsText: String + private lateinit var _bodyAsBytes: ByteArray + override suspend fun body(): InputStream = ktorHttpResponse.bodyAsChannel().toInputStream() + override suspend fun bodyAsText(): String { + if (!this::_bodyAsText.isInitialized) { + _bodyAsText = ktorHttpResponse.bodyAsText() + } + return _bodyAsText + } + + override suspend fun bodyAsBytes(): ByteArray { + if (!this::_bodyAsBytes.isInitialized) { + _bodyAsBytes = ktorHttpResponse.readBytes() + } + return _bodyAsBytes + } + override suspend fun header(): Map> = ktorHttpResponse.headers.toMap() override suspend fun status(): Int = ktorHttpResponse.status.value override suspend fun statusMessage(): String = ktorHttpResponse.status.description diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/resource/ResolveResponse.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/resource/ResolveResponse.kt index bcfb0bcb..6b0cc202 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/resource/ResolveResponse.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/resource/ResolveResponse.kt @@ -4,6 +4,8 @@ import java.io.InputStream interface ResolveResponse { suspend fun body(): InputStream + suspend fun bodyAsText(): String + suspend fun bodyAsBytes(): ByteArray suspend fun header(): Map> suspend fun status(): Int suspend fun statusMessage(): String diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt index 40df3646..20a49f4f 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt @@ -8,6 +8,7 @@ import dev.usbharu.hideout.core.domain.model.user.UserRepository import dev.usbharu.hideout.core.query.FollowerQueryService import dev.usbharu.hideout.core.query.UserQueryService import dev.usbharu.hideout.core.service.follow.SendFollowDto +import dev.usbharu.hideout.core.service.instance.InstanceService import org.jetbrains.exposed.exceptions.ExposedSQLException import org.springframework.stereotype.Service import java.time.Instant @@ -20,7 +21,8 @@ class UserServiceImpl( private val userQueryService: UserQueryService, private val followerQueryService: FollowerQueryService, private val userBuilder: User.UserBuilder, - private val applicationConfig: ApplicationConfig + private val applicationConfig: ApplicationConfig, + private val instanceService: InstanceService ) : UserService { @@ -55,6 +57,8 @@ class UserServiceImpl( } override suspend fun createRemoteUser(user: RemoteUserCreateDto): User { + instanceService.fetchInstance(user.url) + val nextId = userRepository.nextId() val userEntity = userBuilder.of( id = nextId, From e0b1cf6bc00ac0314fe02a418982deec90d81d71 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sat, 18 Nov 2023 12:02:40 +0900 Subject: [PATCH 06/12] =?UTF-8?q?feat:=20Nodeinfo=E3=81=AE=E3=83=87?= =?UTF-8?q?=E3=82=B7=E3=83=AA=E3=82=A2=E3=83=A9=E3=82=A4=E3=82=BA=E7=94=A8?= =?UTF-8?q?=E3=82=AF=E3=83=A9=E3=82=B9=E3=82=92=E5=88=A5=E3=81=AB=E6=BA=96?= =?UTF-8?q?=E5=82=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/APResourceResolveServiceImpl.kt | 38 ++++++++++++-- .../service/common/CacheManager.kt | 9 ---- .../service/common/InMemoryCacheManager.kt | 50 ------------------- .../core/domain/model/instance/Nodeinfo.kt | 16 ++++++ .../core/service/instance/InstanceService.kt | 9 ++-- 5 files changed, 55 insertions(+), 67 deletions(-) delete mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/CacheManager.kt delete mode 100644 src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/InMemoryCacheManager.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Nodeinfo.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APResourceResolveServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APResourceResolveServiceImpl.kt index 49906265..81b7aec3 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APResourceResolveServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/APResourceResolveServiceImpl.kt @@ -3,7 +3,10 @@ package dev.usbharu.hideout.activitypub.service.common import dev.usbharu.hideout.activitypub.domain.model.objects.Object import dev.usbharu.hideout.core.domain.model.user.User import dev.usbharu.hideout.core.domain.model.user.UserRepository +import dev.usbharu.hideout.core.service.resource.CacheManager +import dev.usbharu.hideout.core.service.resource.ResolveResponse import org.springframework.stereotype.Service +import java.io.InputStream @Service class APResourceResolveServiceImpl( @@ -25,7 +28,7 @@ class APResourceResolveServiceImpl( cacheManager.putCache(key) { runResolve(url, singerId?.let { userRepository.findById(it) }, clazz) } - return cacheManager.getOrWait(key) as T + return (cacheManager.getOrWait(key) as APResolveResponse).objects } private suspend fun internalResolve(url: String, singer: User?, clazz: Class): T { @@ -33,11 +36,12 @@ class APResourceResolveServiceImpl( cacheManager.putCache(key) { runResolve(url, singer, clazz) } - return cacheManager.getOrWait(key) as T + return (cacheManager.getOrWait(key) as APResolveResponse).objects } - private suspend fun runResolve(url: String, singer: User?, clazz: Class): Object = - apRequestService.apGet(url, singer, clazz) + private suspend fun runResolve(url: String, singer: User?, clazz: Class): ResolveResponse { + return APResolveResponse(apRequestService.apGet(url, singer, clazz)) + } private fun genCacheKey(url: String, singerId: Long?): String { if (singerId != null) { @@ -45,4 +49,30 @@ class APResourceResolveServiceImpl( } return url } + + private class APResolveResponse(val objects: T) : ResolveResponse { + override suspend fun body(): InputStream { + TODO("Not yet implemented") + } + + override suspend fun bodyAsText(): String { + TODO("Not yet implemented") + } + + override suspend fun bodyAsBytes(): ByteArray { + TODO("Not yet implemented") + } + + override suspend fun header(): Map> { + TODO("Not yet implemented") + } + + override suspend fun status(): Int { + TODO("Not yet implemented") + } + + override suspend fun statusMessage(): String { + TODO("Not yet implemented") + } + } } diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/CacheManager.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/CacheManager.kt deleted file mode 100644 index 83ae4f9d..00000000 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/CacheManager.kt +++ /dev/null @@ -1,9 +0,0 @@ -package dev.usbharu.hideout.activitypub.service.common - -import dev.usbharu.hideout.activitypub.domain.model.objects.Object - -interface CacheManager { - - suspend fun putCache(key: String, block: suspend () -> Object) - suspend fun getOrWait(key: String): Object -} diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/InMemoryCacheManager.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/InMemoryCacheManager.kt deleted file mode 100644 index 3c8320db..00000000 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/common/InMemoryCacheManager.kt +++ /dev/null @@ -1,50 +0,0 @@ -package dev.usbharu.hideout.activitypub.service.common - -import dev.usbharu.hideout.activitypub.domain.model.objects.Object -import dev.usbharu.hideout.util.LruCache -import kotlinx.coroutines.delay -import kotlinx.coroutines.sync.Mutex -import kotlinx.coroutines.sync.withLock -import org.springframework.stereotype.Service -import java.time.Instant - -@Service -class InMemoryCacheManager : CacheManager { - private val cacheKey = LruCache(15) - private val valueStore = mutableMapOf() - private val keyMutex = Mutex() - - override suspend fun putCache(key: String, block: suspend () -> Object) { - val needRunBlock: Boolean - keyMutex.withLock { - cacheKey.filter { Instant.ofEpochMilli(it.value).plusSeconds(300) <= Instant.now() } - - val cached = cacheKey.get(key) - if (cached == null) { - needRunBlock = true - cacheKey[key] = Instant.now().toEpochMilli() - - valueStore.remove(key) - } else { - needRunBlock = false - } - } - if (needRunBlock) { - val processed = block() - - if (cacheKey.containsKey(key)) { - valueStore[key] = processed - } - } - } - - override suspend fun getOrWait(key: String): Object { - while (valueStore.contains(key).not()) { - if (cacheKey.containsKey(key).not()) { - throw IllegalStateException("Invalid cache key.") - } - delay(1) - } - return valueStore.getValue(key) - } -} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Nodeinfo.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Nodeinfo.kt new file mode 100644 index 00000000..4aa53126 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Nodeinfo.kt @@ -0,0 +1,16 @@ +package dev.usbharu.hideout.core.domain.model.instance + +class Nodeinfo { + + var links: List = emptyList() + + protected constructor() +} + + +class Links { + var rel: String? = null + var href: String? = null + + protected constructor() +} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceService.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceService.kt index bb4a2a0c..5b54054e 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceService.kt @@ -1,11 +1,12 @@ package dev.usbharu.hideout.core.service.instance import com.fasterxml.jackson.databind.ObjectMapper -import dev.usbharu.hideout.activitypub.domain.model.nodeinfo.Nodeinfo import dev.usbharu.hideout.activitypub.domain.model.nodeinfo.Nodeinfo2_0 import dev.usbharu.hideout.core.domain.model.instance.Instance import dev.usbharu.hideout.core.domain.model.instance.InstanceRepository +import dev.usbharu.hideout.core.domain.model.instance.Nodeinfo import dev.usbharu.hideout.core.service.resource.ResourceResolveService +import org.springframework.beans.factory.annotation.Qualifier import org.springframework.stereotype.Service import java.net.URL import java.time.Instant @@ -20,21 +21,21 @@ interface InstanceService { class InstanceServiceImpl( private val instanceRepository: InstanceRepository, private val resourceResolveService: ResourceResolveService, - private val objectMapper: ObjectMapper + @Qualifier("activitypub") private val objectMapper: ObjectMapper ) : InstanceService { override suspend fun fetchInstance(url: String): Instance { val u = URL(url) val resolveInstanceUrl = u.protocol + "://" + u.host val nodeinfoJson = resourceResolveService.resolve("$resolveInstanceUrl/.well-known/nodeinfo").bodyAsText() val nodeinfo = objectMapper.readValue(nodeinfoJson, Nodeinfo::class.java) - val nodeinfoPathMap = nodeinfo.links.map { it.rel to it.href }.toMap() + val nodeinfoPathMap = nodeinfo.links.associate { it.rel to it.href } for ((key, value) in nodeinfoPathMap) { when (key) { "http://nodeinfo.diaspora.software/ns/schema/2.0" -> { val nodeinfo20 = objectMapper.readValue( - resourceResolveService.resolve(value).bodyAsText(), + resourceResolveService.resolve(value!!).bodyAsText(), Nodeinfo2_0::class.java ) From 1323815abd1cb8f5121c80d943a209358f7c5683 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sat, 18 Nov 2023 12:20:29 +0900 Subject: [PATCH 07/12] =?UTF-8?q?feat:=20Instance=E3=81=AE=E9=87=8D?= =?UTF-8?q?=E8=A4=87=E3=83=81=E3=82=A7=E3=83=83=E3=82=AF=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/objects/user/APUserService.kt | 3 +- .../core/domain/model/instance/Nodeinfo2_0.kt | 22 ++++++++ .../exposedquery/InstanceQueryServiceImpl.kt | 16 ++++++ .../core/query/InstanceQueryService.kt | 7 +++ .../core/service/instance/InstanceService.kt | 53 +++++++++++++++---- .../core/service/user/RemoteUserCreateDto.kt | 3 +- .../core/service/user/UserServiceImpl.kt | 2 +- 7 files changed, 94 insertions(+), 12 deletions(-) create mode 100644 src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Nodeinfo2_0.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/InstanceQueryServiceImpl.kt create mode 100644 src/main/kotlin/dev/usbharu/hideout/core/query/InstanceQueryService.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt index 8aee16a4..9c0777e9 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/objects/user/APUserService.kt @@ -124,7 +124,8 @@ class APUserServiceImpl( ?: throw IllegalActivityPubObjectException("publicKey is null"), keyId = person.publicKey?.id ?: throw IllegalActivityPubObjectException("publicKey keyId is null"), following = person.following, - followers = person.followers + followers = person.followers, + sharedInbox = person.endpoints["sharedInbox"] ) ) } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Nodeinfo2_0.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Nodeinfo2_0.kt new file mode 100644 index 00000000..6ff35b1e --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Nodeinfo2_0.kt @@ -0,0 +1,22 @@ +package dev.usbharu.hideout.core.domain.model.instance + +class Nodeinfo2_0 { + var metadata: Metadata? = null + var software: Software? = null + + protected constructor() +} + +class Metadata { + var nodeName: String? = null + var nodeDescription: String? = null + + protected constructor() +} + +class Software { + var name: String? = null + var version: String? = null + + protected constructor() +} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/InstanceQueryServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/InstanceQueryServiceImpl.kt new file mode 100644 index 00000000..587f57a1 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/InstanceQueryServiceImpl.kt @@ -0,0 +1,16 @@ +package dev.usbharu.hideout.core.infrastructure.exposedquery + +import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException +import dev.usbharu.hideout.core.infrastructure.exposedrepository.Instance +import dev.usbharu.hideout.core.infrastructure.exposedrepository.toInstance +import dev.usbharu.hideout.core.query.InstanceQueryService +import dev.usbharu.hideout.util.singleOr +import org.jetbrains.exposed.sql.select +import org.springframework.stereotype.Repository +import dev.usbharu.hideout.core.domain.model.instance.Instance as InstanceEntity + +@Repository +class InstanceQueryServiceImpl : InstanceQueryService { + override suspend fun findByUrl(url: String): InstanceEntity = Instance.select { Instance.url eq url } + .singleOr { FailedToGetResourcesException("url is doesn't exist") }.toInstance() +} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/query/InstanceQueryService.kt b/src/main/kotlin/dev/usbharu/hideout/core/query/InstanceQueryService.kt new file mode 100644 index 00000000..79e6b213 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/query/InstanceQueryService.kt @@ -0,0 +1,7 @@ +package dev.usbharu.hideout.core.query + +import dev.usbharu.hideout.core.domain.model.instance.Instance + +interface InstanceQueryService { + suspend fun findByUrl(url: String): Instance +} diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceService.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceService.kt index 5b54054e..2f6d8ef9 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceService.kt @@ -1,18 +1,21 @@ package dev.usbharu.hideout.core.service.instance import com.fasterxml.jackson.databind.ObjectMapper -import dev.usbharu.hideout.activitypub.domain.model.nodeinfo.Nodeinfo2_0 +import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException import dev.usbharu.hideout.core.domain.model.instance.Instance import dev.usbharu.hideout.core.domain.model.instance.InstanceRepository import dev.usbharu.hideout.core.domain.model.instance.Nodeinfo +import dev.usbharu.hideout.core.domain.model.instance.Nodeinfo2_0 +import dev.usbharu.hideout.core.query.InstanceQueryService import dev.usbharu.hideout.core.service.resource.ResourceResolveService +import org.slf4j.LoggerFactory import org.springframework.beans.factory.annotation.Qualifier import org.springframework.stereotype.Service import java.net.URL import java.time.Instant interface InstanceService { - suspend fun fetchInstance(url: String): Instance + suspend fun fetchInstance(url: String, sharedInbox: String? = null): Instance suspend fun createNewInstance(instanceCreateDto: InstanceCreateDto): Instance } @@ -21,11 +24,20 @@ interface InstanceService { class InstanceServiceImpl( private val instanceRepository: InstanceRepository, private val resourceResolveService: ResourceResolveService, - @Qualifier("activitypub") private val objectMapper: ObjectMapper + @Qualifier("activitypub") private val objectMapper: ObjectMapper, + private val instanceQueryService: InstanceQueryService ) : InstanceService { - override suspend fun fetchInstance(url: String): Instance { + override suspend fun fetchInstance(url: String, sharedInbox: String?): Instance { val u = URL(url) val resolveInstanceUrl = u.protocol + "://" + u.host + + try { + return instanceQueryService.findByUrl(url) + } catch (e: FailedToGetResourcesException) { + logger.info("Instance not found. try fetch instance info. url: {}", resolveInstanceUrl) + logger.debug("Failed to get resources. url: {}", resolveInstanceUrl, e) + } + val nodeinfoJson = resourceResolveService.resolve("$resolveInstanceUrl/.well-known/nodeinfo").bodyAsText() val nodeinfo = objectMapper.readValue(nodeinfoJson, Nodeinfo::class.java) val nodeinfoPathMap = nodeinfo.links.associate { it.rel to it.href } @@ -40,13 +52,32 @@ class InstanceServiceImpl( ) val instanceCreateDto = InstanceCreateDto( - nodeinfo20.metadata.nodeName, - nodeinfo20.metadata.nodeDescription, + nodeinfo20.metadata?.nodeName, + nodeinfo20.metadata?.nodeDescription, resolveInstanceUrl, resolveInstanceUrl + "/favicon.ico", - null, - nodeinfo20.software.name, - nodeinfo20.software.version + sharedInbox, + nodeinfo20.software?.name, + nodeinfo20.software?.version + ) + return createNewInstance(instanceCreateDto) + } + + // TODO: 多分2.0と2.1で互換性有るのでそのまま使うけどなおす + "http://nodeinfo.diaspora.software/ns/schema/2.1" -> { + val nodeinfo20 = objectMapper.readValue( + resourceResolveService.resolve(value!!).bodyAsText(), + Nodeinfo2_0::class.java + ) + + val instanceCreateDto = InstanceCreateDto( + nodeinfo20.metadata?.nodeName, + nodeinfo20.metadata?.nodeDescription, + resolveInstanceUrl, + resolveInstanceUrl + "/favicon.ico", + sharedInbox, + nodeinfo20.software?.name, + nodeinfo20.software?.version ) return createNewInstance(instanceCreateDto) } @@ -78,4 +109,8 @@ class InstanceServiceImpl( instanceRepository.save(instance) return instance } + + companion object { + private val logger = LoggerFactory.getLogger(InstanceServiceImpl::class.java) + } } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/user/RemoteUserCreateDto.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/user/RemoteUserCreateDto.kt index 992956e3..de85e74d 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/user/RemoteUserCreateDto.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/user/RemoteUserCreateDto.kt @@ -11,5 +11,6 @@ data class RemoteUserCreateDto( val publicKey: String, val keyId: String, val followers: String?, - val following: String? + val following: String?, + val sharedInbox: String? ) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt index 20a49f4f..3d486f45 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt @@ -57,7 +57,7 @@ class UserServiceImpl( } override suspend fun createRemoteUser(user: RemoteUserCreateDto): User { - instanceService.fetchInstance(user.url) + instanceService.fetchInstance(user.url, user.sharedInbox) val nextId = userRepository.nextId() val userEntity = userBuilder.of( From 3bddfa854ed991c438ccabde485e9659cc4da120 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sat, 18 Nov 2023 12:29:20 +0900 Subject: [PATCH 08/12] =?UTF-8?q?feat:=20user=E3=81=ABinstance=E3=81=AEid?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hideout/core/domain/model/user/User.kt | 16 ++++++------- .../exposed/UserResultRowMapper.kt | 3 ++- .../exposedquery/FollowerQueryServiceImpl.kt | 24 ++++++++++++------- .../exposedrepository/UserRepositoryImpl.kt | 3 +++ .../core/service/user/UserServiceImpl.kt | 18 +++++++++++--- 5 files changed, 44 insertions(+), 20 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/user/User.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/user/User.kt index bf021757..9666df3e 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/user/User.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/user/User.kt @@ -21,18 +21,16 @@ data class User private constructor( val createdAt: Instant, val keyId: String, val followers: String? = null, - val following: String? = null + val following: String? = null, + val instance: Long? = null ) { override fun toString(): String = - "User(id=$id, name='$name', domain='$domain', screenName='$screenName', description='$description'," + - " password=$password, inbox='$inbox', outbox='$outbox', url='$url', publicKey='$publicKey'," + - " privateKey=$privateKey, createdAt=$createdAt, keyId='$keyId', followers=$followers," + - " following=$following)" + "User(id=$id, name='$name', domain='$domain', screenName='$screenName', description='$description', password=$password, inbox='$inbox', outbox='$outbox', url='$url', publicKey='$publicKey', privateKey=$privateKey, createdAt=$createdAt, keyId='$keyId', followers=$followers, following=$following, instance=$instance)" @Component class UserBuilder(private val characterLimit: CharacterLimit, private val applicationConfig: ApplicationConfig) { - private val logger = LoggerFactory.getLogger(UserBuilder::class.java) + private val logger = LoggerFactory.getLogger(UserBuilder::class.java) @Suppress("LongParameterList", "FunctionMinLength", "LongMethod") fun of( id: Long, @@ -49,7 +47,8 @@ data class User private constructor( createdAt: Instant, keyId: String, following: String? = null, - followers: String? = null + followers: String? = null, + instance: Long? = null ): User { // idは0未満ではいけない require(id >= 0) { "id must be greater than or equal to 0." } @@ -141,7 +140,8 @@ data class User private constructor( createdAt = createdAt, keyId = keyId, followers = followers, - following = following + following = following, + instance = instance ) } } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/UserResultRowMapper.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/UserResultRowMapper.kt index c55a352a..bad247f3 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/UserResultRowMapper.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposed/UserResultRowMapper.kt @@ -25,7 +25,8 @@ class UserResultRowMapper(private val userBuilder: User.UserBuilder) : ResultRow createdAt = Instant.ofEpochMilli((resultRow[Users.createdAt])), keyId = resultRow[Users.keyId], followers = resultRow[Users.followers], - following = resultRow[Users.following] + following = resultRow[Users.following], + instance = resultRow[Users.instance] ) } } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/FollowerQueryServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/FollowerQueryServiceImpl.kt index 3034c0cb..4c62003e 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/FollowerQueryServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedquery/FollowerQueryServiceImpl.kt @@ -38,7 +38,8 @@ class FollowerQueryServiceImpl(private val userBuilder: User.UserBuilder) : Foll followers[Users.createdAt], followers[Users.keyId], followers[Users.following], - followers[Users.followers] + followers[Users.followers], + followers[Users.instance] ) .select { Users.id eq id } .map { @@ -57,7 +58,8 @@ class FollowerQueryServiceImpl(private val userBuilder: User.UserBuilder) : Foll createdAt = Instant.ofEpochMilli(it[followers[Users.createdAt]]), keyId = it[followers[Users.keyId]], followers = it[followers[Users.followers]], - following = it[followers[Users.following]] + following = it[followers[Users.following]], + instance = it[followers[Users.instance]] ) } } @@ -89,7 +91,8 @@ class FollowerQueryServiceImpl(private val userBuilder: User.UserBuilder) : Foll followers[Users.createdAt], followers[Users.keyId], followers[Users.following], - followers[Users.followers] + followers[Users.followers], + followers[Users.instance] ) .select { Users.name eq name and (Users.domain eq domain) } .map { @@ -108,7 +111,8 @@ class FollowerQueryServiceImpl(private val userBuilder: User.UserBuilder) : Foll createdAt = Instant.ofEpochMilli(it[followers[Users.createdAt]]), keyId = it[followers[Users.keyId]], followers = it[followers[Users.followers]], - following = it[followers[Users.following]] + following = it[followers[Users.following]], + instance = it[followers[Users.instance]] ) } } @@ -140,7 +144,8 @@ class FollowerQueryServiceImpl(private val userBuilder: User.UserBuilder) : Foll followers[Users.createdAt], followers[Users.keyId], followers[Users.following], - followers[Users.followers] + followers[Users.followers], + followers[Users.instance] ) .select { followers[Users.id] eq id } .map { @@ -159,7 +164,8 @@ class FollowerQueryServiceImpl(private val userBuilder: User.UserBuilder) : Foll createdAt = Instant.ofEpochMilli(it[followers[Users.createdAt]]), keyId = it[followers[Users.keyId]], followers = it[followers[Users.followers]], - following = it[followers[Users.following]] + following = it[followers[Users.following]], + instance = it[followers[Users.instance]] ) } } @@ -191,7 +197,8 @@ class FollowerQueryServiceImpl(private val userBuilder: User.UserBuilder) : Foll followers[Users.createdAt], followers[Users.keyId], followers[Users.following], - followers[Users.followers] + followers[Users.followers], + followers[Users.instance] ) .select { followers[Users.name] eq name and (followers[Users.domain] eq domain) } .map { @@ -210,7 +217,8 @@ class FollowerQueryServiceImpl(private val userBuilder: User.UserBuilder) : Foll createdAt = Instant.ofEpochMilli(it[followers[Users.createdAt]]), keyId = it[followers[Users.keyId]], followers = it[followers[Users.followers]], - following = it[followers[Users.following]] + following = it[followers[Users.following]], + instance = it[followers[Users.instance]] ) } } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/UserRepositoryImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/UserRepositoryImpl.kt index 6d45a5e7..5cd94ccf 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/UserRepositoryImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/UserRepositoryImpl.kt @@ -35,6 +35,7 @@ class UserRepositoryImpl( it[keyId] = user.keyId it[following] = user.following it[followers] = user.followers + it[instance] = user.instance } } else { Users.update({ Users.id eq user.id }) { @@ -52,6 +53,7 @@ class UserRepositoryImpl( it[keyId] = user.keyId it[following] = user.following it[followers] = user.followers + it[instance] = user.instance } } return user @@ -98,6 +100,7 @@ object Users : Table("users") { val keyId = varchar("key_id", length = 1000) val following = varchar("following", length = 1000).nullable() val followers = varchar("followers", length = 1000).nullable() + val instance = long("instance").references(Instance.id).nullable() override val primaryKey: PrimaryKey = PrimaryKey(id) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt index 3d486f45..6ee4a53c 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt @@ -10,6 +10,7 @@ import dev.usbharu.hideout.core.query.UserQueryService import dev.usbharu.hideout.core.service.follow.SendFollowDto import dev.usbharu.hideout.core.service.instance.InstanceService import org.jetbrains.exposed.exceptions.ExposedSQLException +import org.slf4j.LoggerFactory import org.springframework.stereotype.Service import java.time.Instant @@ -51,13 +52,19 @@ class UserServiceImpl( createdAt = Instant.now(), following = "$userUrl/following", followers = "$userUrl/followers", - keyId = "$userUrl#pubkey" + keyId = "$userUrl#pubkey", + instance = null ) return userRepository.save(userEntity) } override suspend fun createRemoteUser(user: RemoteUserCreateDto): User { - instanceService.fetchInstance(user.url, user.sharedInbox) + val instance = try { + instanceService.fetchInstance(user.url, user.sharedInbox) + } catch (e: Exception) { + logger.warn("FAILED to fetch instance. url: {}", user.url, e) + null + } val nextId = userRepository.nextId() val userEntity = userBuilder.of( @@ -73,7 +80,8 @@ class UserServiceImpl( createdAt = Instant.now(), followers = user.followers, following = user.following, - keyId = user.keyId + keyId = user.keyId, + instance = instance?.id ) return try { userRepository.save(userEntity) @@ -110,4 +118,8 @@ class UserServiceImpl( followerQueryService.removeFollower(id, followerId) return false } + + companion object { + private val logger = LoggerFactory.getLogger(UserServiceImpl::class.java) + } } From 8bb6b3d017bbceed3bf3c460a9b4d4e00416b87b Mon Sep 17 00:00:00 2001 From: usbharu Date: Sat, 18 Nov 2023 13:41:13 +0900 Subject: [PATCH 09/12] Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../dev/usbharu/hideout/core/domain/model/instance/Nodeinfo.kt | 1 - .../kotlin/dev/usbharu/hideout/core/domain/model/user/User.kt | 1 + .../usbharu/hideout/core/service/instance/InstanceService.kt | 2 -- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Nodeinfo.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Nodeinfo.kt index 4aa53126..013bb969 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Nodeinfo.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Nodeinfo.kt @@ -7,7 +7,6 @@ class Nodeinfo { protected constructor() } - class Links { var rel: String? = null var href: String? = null diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/user/User.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/user/User.kt index 9666df3e..208f5b9d 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/user/User.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/user/User.kt @@ -31,6 +31,7 @@ data class User private constructor( class UserBuilder(private val characterLimit: CharacterLimit, private val applicationConfig: ApplicationConfig) { private val logger = LoggerFactory.getLogger(UserBuilder::class.java) + @Suppress("LongParameterList", "FunctionMinLength", "LongMethod") fun of( id: Long, diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceService.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceService.kt index 2f6d8ef9..380083f2 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceService.kt @@ -19,7 +19,6 @@ interface InstanceService { suspend fun createNewInstance(instanceCreateDto: InstanceCreateDto): Instance } - @Service class InstanceServiceImpl( private val instanceRepository: InstanceRepository, @@ -42,7 +41,6 @@ class InstanceServiceImpl( val nodeinfo = objectMapper.readValue(nodeinfoJson, Nodeinfo::class.java) val nodeinfoPathMap = nodeinfo.links.associate { it.rel to it.href } - for ((key, value) in nodeinfoPathMap) { when (key) { "http://nodeinfo.diaspora.software/ns/schema/2.0" -> { From 9c965957581a29ee31eff296de833189aaa08255 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 19 Nov 2023 11:50:19 +0900 Subject: [PATCH 10/12] style: fix lint --- .../domain/model/objects/ObjectValue.kt | 1 + .../core/domain/model/instance/Nodeinfo.kt | 4 +- .../core/domain/model/instance/Nodeinfo2_0.kt | 7 +-- .../hideout/core/domain/model/user/User.kt | 5 +- .../InstanceRepositoryImpl.kt | 2 +- .../core/service/instance/InstanceService.kt | 52 +++++++++---------- .../core/service/media/MediaServiceImpl.kt | 3 +- 7 files changed, 40 insertions(+), 34 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectValue.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectValue.kt index 1cd01d81..b97b2541 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectValue.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/domain/model/objects/ObjectValue.kt @@ -1,5 +1,6 @@ package dev.usbharu.hideout.activitypub.domain.model.objects +@Suppress("VariableNaming") open class ObjectValue : Object { var `object`: String? = null diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Nodeinfo.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Nodeinfo.kt index 013bb969..427c76a3 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Nodeinfo.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Nodeinfo.kt @@ -4,12 +4,12 @@ class Nodeinfo { var links: List = emptyList() - protected constructor() + private constructor() } class Links { var rel: String? = null var href: String? = null - protected constructor() + private constructor() } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Nodeinfo2_0.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Nodeinfo2_0.kt index 6ff35b1e..fcd99c73 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Nodeinfo2_0.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/instance/Nodeinfo2_0.kt @@ -1,22 +1,23 @@ package dev.usbharu.hideout.core.domain.model.instance +@Suppress("ClassNaming") class Nodeinfo2_0 { var metadata: Metadata? = null var software: Software? = null - protected constructor() + constructor() } class Metadata { var nodeName: String? = null var nodeDescription: String? = null - protected constructor() + constructor() } class Software { var name: String? = null var version: String? = null - protected constructor() + constructor() } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/user/User.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/user/User.kt index 208f5b9d..b8f3be26 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/user/User.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/user/User.kt @@ -25,7 +25,10 @@ data class User private constructor( val instance: Long? = null ) { override fun toString(): String = - "User(id=$id, name='$name', domain='$domain', screenName='$screenName', description='$description', password=$password, inbox='$inbox', outbox='$outbox', url='$url', publicKey='$publicKey', privateKey=$privateKey, createdAt=$createdAt, keyId='$keyId', followers=$followers, following=$following, instance=$instance)" + "User(id=$id, name='$name', domain='$domain', screenName='$screenName', description='$description'," + + " password=$password, inbox='$inbox', outbox='$outbox', url='$url', publicKey='$publicKey', " + + "privateKey=$privateKey, createdAt=$createdAt, keyId='$keyId', followers=$followers," + + " following=$following, instance=$instance)" @Component class UserBuilder(private val characterLimit: CharacterLimit, private val applicationConfig: ApplicationConfig) { diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/InstanceRepositoryImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/InstanceRepositoryImpl.kt index a62ac486..5d4a860d 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/InstanceRepositoryImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/InstanceRepositoryImpl.kt @@ -15,7 +15,7 @@ class InstanceRepositoryImpl(private val idGenerateService: IdGenerateService) : override suspend fun generateId(): Long = idGenerateService.generateId() override suspend fun save(instance: InstanceEntity): InstanceEntity { - if (Instance.select { Instance.id.eq(instance.id) }.firstOrNull() == null) { + if (Instance.select { Instance.id.eq(instance.id) }.empty()) { Instance.insert { it[id] = instance.id it[name] = instance.name diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceService.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceService.kt index 380083f2..4b0e2640 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/instance/InstanceService.kt @@ -50,13 +50,13 @@ class InstanceServiceImpl( ) val instanceCreateDto = InstanceCreateDto( - nodeinfo20.metadata?.nodeName, - nodeinfo20.metadata?.nodeDescription, - resolveInstanceUrl, - resolveInstanceUrl + "/favicon.ico", - sharedInbox, - nodeinfo20.software?.name, - nodeinfo20.software?.version + name = nodeinfo20.metadata?.nodeName, + description = nodeinfo20.metadata?.nodeDescription, + url = resolveInstanceUrl, + iconUrl = resolveInstanceUrl + "/favicon.ico", + sharedInbox = sharedInbox, + software = nodeinfo20.software?.name, + version = nodeinfo20.software?.version ) return createNewInstance(instanceCreateDto) } @@ -69,13 +69,13 @@ class InstanceServiceImpl( ) val instanceCreateDto = InstanceCreateDto( - nodeinfo20.metadata?.nodeName, - nodeinfo20.metadata?.nodeDescription, - resolveInstanceUrl, - resolveInstanceUrl + "/favicon.ico", - sharedInbox, - nodeinfo20.software?.name, - nodeinfo20.software?.version + name = nodeinfo20.metadata?.nodeName, + description = nodeinfo20.metadata?.nodeDescription, + url = resolveInstanceUrl, + iconUrl = resolveInstanceUrl + "/favicon.ico", + sharedInbox = sharedInbox, + software = nodeinfo20.software?.name, + version = nodeinfo20.software?.version ) return createNewInstance(instanceCreateDto) } @@ -91,18 +91,18 @@ class InstanceServiceImpl( override suspend fun createNewInstance(instanceCreateDto: InstanceCreateDto): Instance { val instance = Instance( - instanceRepository.generateId(), - instanceCreateDto.name ?: instanceCreateDto.url, - instanceCreateDto.description ?: "", - instanceCreateDto.url, - instanceCreateDto.iconUrl, - instanceCreateDto.sharedInbox, - instanceCreateDto.software ?: "unknown", - instanceCreateDto.version ?: "unknown", - false, - false, - "", - Instant.now() + id = instanceRepository.generateId(), + name = instanceCreateDto.name ?: instanceCreateDto.url, + description = instanceCreateDto.description.orEmpty(), + url = instanceCreateDto.url, + iconUrl = instanceCreateDto.iconUrl, + sharedInbox = instanceCreateDto.sharedInbox, + software = instanceCreateDto.software ?: "unknown", + version = instanceCreateDto.version ?: "unknown", + isBlocked = false, + isMuted = false, + moderationNote = "", + createdAt = Instant.now() ) instanceRepository.save(instance) return instance diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaServiceImpl.kt index fbf99674..ff8f1d93 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/media/MediaServiceImpl.kt @@ -24,7 +24,7 @@ open class MediaServiceImpl( private val remoteMediaDownloadService: RemoteMediaDownloadService, private val renameService: MediaFileRenameService ) : MediaService { - @Suppress("LongMethod") + @Suppress("LongMethod", "NestedBlockDepth") override suspend fun uploadLocalMedia(mediaRequest: MediaRequest): EntityMedia { val fileName = mediaRequest.file.name logger.info( @@ -95,6 +95,7 @@ open class MediaServiceImpl( } // TODO: 仮の処理として保存したように動かす + @Suppress("LongMethod", "NestedBlockDepth") override suspend fun uploadRemoteMedia(remoteMedia: RemoteMedia): Media { logger.info("MEDIA Remote media. filename:${remoteMedia.name} url:${remoteMedia.url}") From d63503daefea6b3f15ba69703266d58473a82cc9 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Sun, 19 Nov 2023 12:09:44 +0900 Subject: [PATCH 11/12] =?UTF-8?q?test:=20=E3=83=86=E3=82=B9=E3=83=88?= =?UTF-8?q?=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...gnature認証でフォロワーがfollowers投稿を取得できる.sql | 8 ++++---- ...pSignature認証でフォロワーがpublic投稿を取得できる.sql | 8 ++++---- ...ignature認証でフォロワーがunlisted投稿を取得できる.sql | 8 ++++---- ...ア付き投稿はattachmentにDocumentとして画像が存在する.sql | 4 ++-- .../リプライになっている投稿はinReplyToが存在する.sql | 4 ++-- .../note/匿名でfollowers投稿を取得しようとすると404.sql | 4 ++-- .../resources/sql/note/匿名でpublic投稿を取得できる.sql | 4 ++-- .../resources/sql/note/匿名でunlisted投稿を取得できる.sql | 4 ++-- src/intTest/resources/sql/test-user.sql | 4 ++-- .../exposedrepository/InstanceRepositoryImpl.kt | 2 ++ .../service/common/APResourceResolveServiceImplTest.kt | 1 + .../usbharu/hideout/core/service/user/UserServiceTest.kt | 6 ++++-- 12 files changed, 31 insertions(+), 26 deletions(-) diff --git a/src/intTest/resources/sql/note/httpSignature認証でフォロワーがfollowers投稿を取得できる.sql b/src/intTest/resources/sql/note/httpSignature認証でフォロワーがfollowers投稿を取得できる.sql index f1a91192..53d28e59 100644 --- a/src/intTest/resources/sql/note/httpSignature認証でフォロワーがfollowers投稿を取得できる.sql +++ b/src/intTest/resources/sql/note/httpSignature認証でフォロワーがfollowers投稿を取得できる.sql @@ -1,5 +1,5 @@ insert into "USERS" (ID, NAME, DOMAIN, SCREEN_NAME, DESCRIPTION, PASSWORD, INBOX, OUTBOX, URL, PUBLIC_KEY, PRIVATE_KEY, - CREATED_AT, KEY_ID, FOLLOWING, FOLLOWERS) + CREATED_AT, KEY_ID, FOLLOWING, FOLLOWERS, INSTANCE) VALUES (8, 'test-user8', 'example.com', 'Im test-user8.', 'THis account is test-user8.', '5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8', 'https://example.com/users/test-user8/inbox', @@ -7,10 +7,10 @@ VALUES (8, 'test-user8', 'example.com', 'Im test-user8.', 'THis account is test- '-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----', '-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678, 'https://example.com/users/test-user8#pubkey', 'https://example.com/users/test-user8/following', - 'https://example.com/users/test-user8/followers'); + 'https://example.com/users/test-user8/followers', null); insert into "USERS" (ID, NAME, DOMAIN, SCREEN_NAME, DESCRIPTION, PASSWORD, INBOX, OUTBOX, URL, PUBLIC_KEY, PRIVATE_KEY, - CREATED_AT, KEY_ID, FOLLOWING, FOLLOWERS) + CREATED_AT, KEY_ID, FOLLOWING, FOLLOWERS, INSTANCE) VALUES (9, 'test-user9', 'follower.example.com', 'Im test-user9.', 'THis account is test-user9.', null, 'https://follower.example.com/users/test-user9/inbox', @@ -19,7 +19,7 @@ VALUES (9, 'test-user9', 'follower.example.com', 'Im test-user9.', 'THis account null, 12345678, 'https://follower.example.com/users/test-user9#pubkey', 'https://follower.example.com/users/test-user9/following', - 'https://follower.example.com/users/test-user9/followers'); + 'https://follower.example.com/users/test-user9/followers', null); insert into USERS_FOLLOWERS (USER_ID, FOLLOWER_ID) VALUES (8, 9); diff --git a/src/intTest/resources/sql/note/httpSignature認証でフォロワーがpublic投稿を取得できる.sql b/src/intTest/resources/sql/note/httpSignature認証でフォロワーがpublic投稿を取得できる.sql index 20a7e5ba..e0fe1f83 100644 --- a/src/intTest/resources/sql/note/httpSignature認証でフォロワーがpublic投稿を取得できる.sql +++ b/src/intTest/resources/sql/note/httpSignature認証でフォロワーがpublic投稿を取得できる.sql @@ -1,5 +1,5 @@ insert into "USERS" (ID, NAME, DOMAIN, SCREEN_NAME, DESCRIPTION, PASSWORD, INBOX, OUTBOX, URL, PUBLIC_KEY, PRIVATE_KEY, - CREATED_AT, KEY_ID, FOLLOWING, FOLLOWERS) + CREATED_AT, KEY_ID, FOLLOWING, FOLLOWERS, INSTANCE) VALUES (4, 'test-user4', 'example.com', 'Im test user4.', 'THis account is test user4.', '5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8', 'https://example.com/users/test-user4/inbox', @@ -7,10 +7,10 @@ VALUES (4, 'test-user4', 'example.com', 'Im test user4.', 'THis account is test '-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----', '-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678, 'https://example.com/users/test-user4#pubkey', 'https://example.com/users/test-user4/following', - 'https://example.com/users/test-user4/followers'); + 'https://example.com/users/test-user4/followers', null); insert into "USERS" (ID, NAME, DOMAIN, SCREEN_NAME, DESCRIPTION, PASSWORD, INBOX, OUTBOX, URL, PUBLIC_KEY, PRIVATE_KEY, - CREATED_AT, KEY_ID, FOLLOWING, FOLLOWERS) + CREATED_AT, KEY_ID, FOLLOWING, FOLLOWERS, INSTANCE) VALUES (5, 'test-user5', 'follower.example.com', 'Im test user5.', 'THis account is test user5.', null, 'https://follower.example.com/users/test-user5/inbox', @@ -19,7 +19,7 @@ VALUES (5, 'test-user5', 'follower.example.com', 'Im test user5.', 'THis account null, 12345678, 'https://follower.example.com/users/test-user5#pubkey', 'https://follower.example.com/users/test-user5/following', - 'https://follower.example.com/users/test-user5/followers'); + 'https://follower.example.com/users/test-user5/followers', null); insert into USERS_FOLLOWERS (USER_ID, FOLLOWER_ID) VALUES (4, 5); diff --git a/src/intTest/resources/sql/note/httpSignature認証でフォロワーがunlisted投稿を取得できる.sql b/src/intTest/resources/sql/note/httpSignature認証でフォロワーがunlisted投稿を取得できる.sql index cb7707a9..82c7cff9 100644 --- a/src/intTest/resources/sql/note/httpSignature認証でフォロワーがunlisted投稿を取得できる.sql +++ b/src/intTest/resources/sql/note/httpSignature認証でフォロワーがunlisted投稿を取得できる.sql @@ -1,5 +1,5 @@ insert into "USERS" (ID, NAME, DOMAIN, SCREEN_NAME, DESCRIPTION, PASSWORD, INBOX, OUTBOX, URL, PUBLIC_KEY, PRIVATE_KEY, - CREATED_AT, KEY_ID, FOLLOWING, FOLLOWERS) + CREATED_AT, KEY_ID, FOLLOWING, FOLLOWERS, INSTANCE) VALUES (6, 'test-user6', 'example.com', 'Im test-user6.', 'THis account is test-user6.', '5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8', 'https://example.com/users/test-user6/inbox', @@ -7,10 +7,10 @@ VALUES (6, 'test-user6', 'example.com', 'Im test-user6.', 'THis account is test- '-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----', '-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678, 'https://example.com/users/test-user6#pubkey', 'https://example.com/users/test-user6/following', - 'https://example.com/users/test-user6/followers'); + 'https://example.com/users/test-user6/followers', null); insert into "USERS" (ID, NAME, DOMAIN, SCREEN_NAME, DESCRIPTION, PASSWORD, INBOX, OUTBOX, URL, PUBLIC_KEY, PRIVATE_KEY, - CREATED_AT, KEY_ID, FOLLOWING, FOLLOWERS) + CREATED_AT, KEY_ID, FOLLOWING, FOLLOWERS, INSTANCE) VALUES (7, 'test-user7', 'follower.example.com', 'Im test-user7.', 'THis account is test-user7.', null, 'https://follower.example.com/users/test-user7/inbox', @@ -19,7 +19,7 @@ VALUES (7, 'test-user7', 'follower.example.com', 'Im test-user7.', 'THis account null, 12345678, 'https://follower.example.com/users/test-user7#pubkey', 'https://follower.example.com/users/test-user7/following', - 'https://follower.example.com/users/test-user7/followers'); + 'https://follower.example.com/users/test-user7/followers', null); insert into USERS_FOLLOWERS (USER_ID, FOLLOWER_ID) VALUES (6, 7); diff --git a/src/intTest/resources/sql/note/メディア付き投稿はattachmentにDocumentとして画像が存在する.sql b/src/intTest/resources/sql/note/メディア付き投稿はattachmentにDocumentとして画像が存在する.sql index 810c33d4..2f0b65a7 100644 --- a/src/intTest/resources/sql/note/メディア付き投稿はattachmentにDocumentとして画像が存在する.sql +++ b/src/intTest/resources/sql/note/メディア付き投稿はattachmentにDocumentとして画像が存在する.sql @@ -1,5 +1,5 @@ insert into "USERS" (ID, NAME, DOMAIN, SCREEN_NAME, DESCRIPTION, PASSWORD, INBOX, OUTBOX, URL, PUBLIC_KEY, PRIVATE_KEY, - CREATED_AT, KEY_ID, FOLLOWING, FOLLOWERS) + CREATED_AT, KEY_ID, FOLLOWING, FOLLOWERS, INSTANCE) VALUES (11, 'test-user11', 'example.com', 'Im test-user11.', 'THis account is test-user11.', '5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8', 'https://example.com/users/test-user11/inbox', @@ -7,7 +7,7 @@ VALUES (11, 'test-user11', 'example.com', 'Im test-user11.', 'THis account is te '-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----', '-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678, 'https://example.com/users/test-user11#pubkey', 'https://example.com/users/test-user11/following', - 'https://example.com/users/test-user11/followers'); + 'https://example.com/users/test-user11/followers', null); insert into POSTS (ID, "userId", OVERVIEW, TEXT, "createdAt", VISIBILITY, URL, "repostId", "replyId", SENSITIVE, AP_ID) VALUES (1242, 11, null, 'test post', 12345680, 0, 'https://example.com/users/test-user11/posts/1242', null, null, false, diff --git a/src/intTest/resources/sql/note/リプライになっている投稿はinReplyToが存在する.sql b/src/intTest/resources/sql/note/リプライになっている投稿はinReplyToが存在する.sql index cf6f842f..d32ffce0 100644 --- a/src/intTest/resources/sql/note/リプライになっている投稿はinReplyToが存在する.sql +++ b/src/intTest/resources/sql/note/リプライになっている投稿はinReplyToが存在する.sql @@ -1,5 +1,5 @@ insert into "USERS" (ID, NAME, DOMAIN, SCREEN_NAME, DESCRIPTION, PASSWORD, INBOX, OUTBOX, URL, PUBLIC_KEY, PRIVATE_KEY, - CREATED_AT, KEY_ID, FOLLOWING, FOLLOWERS) + CREATED_AT, KEY_ID, FOLLOWING, FOLLOWERS, INSTANCE) VALUES (10, 'test-user10', 'example.com', 'Im test-user10.', 'THis account is test-user10.', '5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8', 'https://example.com/users/test-user10/inbox', @@ -7,7 +7,7 @@ VALUES (10, 'test-user10', 'example.com', 'Im test-user10.', 'THis account is te '-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----', '-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678, 'https://example.com/users/test-user10#pubkey', 'https://example.com/users/test-user10/following', - 'https://example.com/users/test-user10/followers'); + 'https://example.com/users/test-user10/followers', null); insert into POSTS (ID, "userId", OVERVIEW, TEXT, "createdAt", VISIBILITY, URL, "repostId", "replyId", SENSITIVE, AP_ID) VALUES (1240, 10, null, 'test post', 12345680, 0, 'https://example.com/users/test-user10/posts/1240', null, null, false, diff --git a/src/intTest/resources/sql/note/匿名でfollowers投稿を取得しようとすると404.sql b/src/intTest/resources/sql/note/匿名でfollowers投稿を取得しようとすると404.sql index 71ee8f8d..1da76bf1 100644 --- a/src/intTest/resources/sql/note/匿名でfollowers投稿を取得しようとすると404.sql +++ b/src/intTest/resources/sql/note/匿名でfollowers投稿を取得しようとすると404.sql @@ -1,5 +1,5 @@ insert into "USERS" (ID, NAME, DOMAIN, SCREEN_NAME, DESCRIPTION, PASSWORD, INBOX, OUTBOX, URL, PUBLIC_KEY, PRIVATE_KEY, - CREATED_AT, KEY_ID, FOLLOWING, FOLLOWERS) + CREATED_AT, KEY_ID, FOLLOWING, FOLLOWERS, INSTANCE) VALUES (3, 'test-user3', 'example.com', 'Im test user3.', 'THis account is test user3.', '5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8', 'https://example.com/users/test-user3/inbox', @@ -7,7 +7,7 @@ VALUES (3, 'test-user3', 'example.com', 'Im test user3.', 'THis account is test '-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----', '-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678, 'https://example.com/users/test-user3#pubkey', 'https://example.com/users/test-user3/following', - 'https://example.com/users/test-user3/followers'); + 'https://example.com/users/test-user3/followers', null); insert into POSTS (ID, "userId", OVERVIEW, TEXT, "createdAt", VISIBILITY, URL, "repostId", "replyId", SENSITIVE, AP_ID) VALUES (1236, 3, null, 'test post', 12345680, 2, 'https://example.com/users/test-user3/posts/1236', null, null, false, diff --git a/src/intTest/resources/sql/note/匿名でpublic投稿を取得できる.sql b/src/intTest/resources/sql/note/匿名でpublic投稿を取得できる.sql index 23f38afc..e8362d0f 100644 --- a/src/intTest/resources/sql/note/匿名でpublic投稿を取得できる.sql +++ b/src/intTest/resources/sql/note/匿名でpublic投稿を取得できる.sql @@ -1,12 +1,12 @@ insert into "USERS" (ID, NAME, DOMAIN, SCREEN_NAME, DESCRIPTION, PASSWORD, INBOX, OUTBOX, URL, PUBLIC_KEY, PRIVATE_KEY, - CREATED_AT, KEY_ID, FOLLOWING, FOLLOWERS) + CREATED_AT, KEY_ID, FOLLOWING, FOLLOWERS, INSTANCE) VALUES (1, 'test-user', 'example.com', 'Im test user.', 'THis account is test user.', '5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8', 'https://example.com/users/test-user/inbox', 'https://example.com/users/test-user/outbox', 'https://example.com/users/test-user', '-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----', '-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678, 'https://example.com/users/test-user#pubkey', 'https://example.com/users/test-user/following', - 'https://example.com/users/test-users/followers'); + 'https://example.com/users/test-users/followers', null); insert into POSTS (ID, "userId", OVERVIEW, TEXT, "createdAt", VISIBILITY, URL, "repostId", "replyId", SENSITIVE, AP_ID) VALUES (1234, 1, null, 'test post', 12345680, 0, 'https://example.com/users/test-user/posts/1234', null, null, false, diff --git a/src/intTest/resources/sql/note/匿名でunlisted投稿を取得できる.sql b/src/intTest/resources/sql/note/匿名でunlisted投稿を取得できる.sql index 88c8bf9a..fa04a3ce 100644 --- a/src/intTest/resources/sql/note/匿名でunlisted投稿を取得できる.sql +++ b/src/intTest/resources/sql/note/匿名でunlisted投稿を取得できる.sql @@ -1,5 +1,5 @@ insert into "USERS" (ID, NAME, DOMAIN, SCREEN_NAME, DESCRIPTION, PASSWORD, INBOX, OUTBOX, URL, PUBLIC_KEY, PRIVATE_KEY, - CREATED_AT, KEY_ID, FOLLOWING, FOLLOWERS) + CREATED_AT, KEY_ID, FOLLOWING, FOLLOWERS, INSTANCE) VALUES (2, 'test-user2', 'example.com', 'Im test user2.', 'THis account is test user2.', '5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8', 'https://example.com/users/test-user2/inbox', @@ -7,7 +7,7 @@ VALUES (2, 'test-user2', 'example.com', 'Im test user2.', 'THis account is test '-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----', '-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678, 'https://example.com/users/test-user2#pubkey', 'https://example.com/users/test-user2/following', - 'https://example.com/users/test-user2/followers'); + 'https://example.com/users/test-user2/followers', null); insert into POSTS (ID, "userId", OVERVIEW, TEXT, "createdAt", VISIBILITY, URL, "repostId", "replyId", SENSITIVE, AP_ID) VALUES (1235, 2, null, 'test post', 12345680, 1, 'https://example.com/users/test-user2/posts/1235', null, null, false, diff --git a/src/intTest/resources/sql/test-user.sql b/src/intTest/resources/sql/test-user.sql index 8b6df0d4..a21d1795 100644 --- a/src/intTest/resources/sql/test-user.sql +++ b/src/intTest/resources/sql/test-user.sql @@ -1,9 +1,9 @@ insert into "USERS" (ID, NAME, DOMAIN, SCREEN_NAME, DESCRIPTION, PASSWORD, INBOX, OUTBOX, URL, PUBLIC_KEY, PRIVATE_KEY, - CREATED_AT, KEY_ID, FOLLOWING, FOLLOWERS) + CREATED_AT, KEY_ID, FOLLOWING, FOLLOWERS, INSTANCE) VALUES (1, 'test-user', 'example.com', 'Im test user.', 'THis account is test user.', '5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8', 'https://example.com/users/test-user/inbox', 'https://example.com/users/test-user/outbox', 'https://example.com/users/test-user', '-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----', '-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----', 12345678, 'https://example.com/users/test-user#pubkey', 'https://example.com/users/test-user/following', - 'https://example.com/users/test-users/followers'); + 'https://example.com/users/test-users/followers', null); diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/InstanceRepositoryImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/InstanceRepositoryImpl.kt index 5d4a860d..edd79195 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/InstanceRepositoryImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/exposedrepository/InstanceRepositoryImpl.kt @@ -88,4 +88,6 @@ object Instance : Table("instance") { val isMuted = bool("is_muted") val moderationNote = varchar("moderation_note", 10000) val createdAt = timestamp("created_at") + + override val primaryKey: PrimaryKey = PrimaryKey(id) } diff --git a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/common/APResourceResolveServiceImplTest.kt b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/common/APResourceResolveServiceImplTest.kt index 46b50898..5f167d6b 100644 --- a/src/test/kotlin/dev/usbharu/hideout/activitypub/service/common/APResourceResolveServiceImplTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/activitypub/service/common/APResourceResolveServiceImplTest.kt @@ -1,6 +1,7 @@ package dev.usbharu.hideout.activitypub.service.common import dev.usbharu.hideout.core.domain.model.user.UserRepository +import dev.usbharu.hideout.core.service.resource.InMemoryCacheManager import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.test.runTest diff --git a/src/test/kotlin/dev/usbharu/hideout/core/service/user/UserServiceTest.kt b/src/test/kotlin/dev/usbharu/hideout/core/service/user/UserServiceTest.kt index a39dbd70..65a88e8b 100644 --- a/src/test/kotlin/dev/usbharu/hideout/core/service/user/UserServiceTest.kt +++ b/src/test/kotlin/dev/usbharu/hideout/core/service/user/UserServiceTest.kt @@ -41,6 +41,7 @@ class UserServiceTest { mock(), userBuilder, testApplicationConfig, + mock() ) userService.createLocalUser(UserCreateDto("test", "testUser", "XXXXXXXXXXXXX", "test")) verify(userRepository, times(1)).save(any()) @@ -67,7 +68,7 @@ class UserServiceTest { onBlocking { nextId() } doReturn 113345L } val userService = - UserServiceImpl(userRepository, mock(), mock(), mock(), mock(), userBuilder, testApplicationConfig) + UserServiceImpl(userRepository, mock(), mock(), mock(), mock(), userBuilder, testApplicationConfig, mock()) val user = RemoteUserCreateDto( name = "test", domain = "remote.example.com", @@ -79,7 +80,8 @@ class UserServiceTest { publicKey = "-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----", keyId = "a", following = "", - followers = "" + followers = "", + sharedInbox = null ) userService.createRemoteUser(user) verify(userRepository, times(1)).save(any()) From f767da23f3ad78e7a74ade54777294fd59e729f6 Mon Sep 17 00:00:00 2001 From: usbharu Date: Sun, 19 Nov 2023 12:16:24 +0900 Subject: [PATCH 12/12] Update src/main/kotlin/dev/usbharu/hideout/core/domain/model/user/User.kt Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../dev/usbharu/hideout/core/domain/model/user/User.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/user/User.kt b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/user/User.kt index b8f3be26..4c2ded1c 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/domain/model/user/User.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/domain/model/user/User.kt @@ -26,9 +26,9 @@ data class User private constructor( ) { override fun toString(): String = "User(id=$id, name='$name', domain='$domain', screenName='$screenName', description='$description'," + - " password=$password, inbox='$inbox', outbox='$outbox', url='$url', publicKey='$publicKey', " + - "privateKey=$privateKey, createdAt=$createdAt, keyId='$keyId', followers=$followers," + - " following=$following, instance=$instance)" + " password=$password, inbox='$inbox', outbox='$outbox', url='$url', publicKey='$publicKey', " + + "privateKey=$privateKey, createdAt=$createdAt, keyId='$keyId', followers=$followers," + + " following=$following, instance=$instance)" @Component class UserBuilder(private val characterLimit: CharacterLimit, private val applicationConfig: ApplicationConfig) {